diff options
| author | 2009-07-21 11:16:54 -0700 | |
|---|---|---|
| committer | 2009-07-21 11:16:54 -0700 | |
| commit | cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f (patch) | |
| tree | 6510f35ad004f1a4640b48264c290926e8596d7a | |
| parent | 4cf03d381b2dff908857fceff0bec445f8d44f36 (diff) | |
donut snapshot
1032 files changed, 81329 insertions, 15135 deletions
diff --git a/Android.mk b/Android.mk index ca76fb4b74de..f32129e40fa5 100644 --- a/Android.mk +++ b/Android.mk @@ -64,15 +64,17 @@ endif ## ## READ ME: ######################################################## LOCAL_SRC_FILES += \ + core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \ + core/java/android/accessibilityservice/IEventListener.aidl \ core/java/android/accounts/IAccountsService.aidl \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IActivityWatcher.aidl \ core/java/android/app/IAlarmManager.aidl \ + core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ - core/java/android/app/IIntentReceiver.aidl \ - core/java/android/app/IIntentSender.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ + core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStatusBar.aidl \ core/java/android/app/IThumbnailReceiver.aidl \ @@ -80,15 +82,18 @@ 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/IBackupService.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 \ core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ - core/java/android/content/IContentService.aidl \ + core/java/android/content/IContentService.aidl \ + core/java/android/content/IIntentReceiver.aidl \ + core/java/android/content/IIntentSender.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ - core/java/android/content/ISyncStatusObserver.aidl \ + core/java/android/content/ISyncStatusObserver.aidl \ core/java/android/content/pm/IPackageDataObserver.aidl \ core/java/android/content/pm/IPackageDeleteObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver.aidl \ @@ -106,6 +111,8 @@ LOCAL_SRC_FILES += \ core/java/android/os/IPermissionController.aidl \ core/java/android/os/IPowerManager.aidl \ core/java/android/text/IClipboard.aidl \ + core/java/android/view/accessibility/IAccessibilityManager.aidl \ + core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IOnKeyguardExitResult.aidl \ core/java/android/view/IRotationWatcher.aidl \ @@ -114,6 +121,8 @@ LOCAL_SRC_FILES += \ core/java/android/view/IWindowSession.aidl \ core/java/android/speech/IRecognitionListener.aidl \ core/java/android/speech/IRecognitionService.aidl \ + core/java/android/speech/tts/ITts.aidl \ + core/java/android/speech/tts/ITtsCallback.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ core/java/com/android/internal/app/IUsageStats.aidl \ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \ @@ -131,7 +140,6 @@ LOCAL_SRC_FILES += \ location/java/android/location/IGeocodeProvider.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ - location/java/android/location/ILocationCollector.aidl \ location/java/android/location/ILocationListener.aidl \ location/java/android/location/ILocationManager.aidl \ location/java/android/location/ILocationProvider.aidl \ @@ -145,7 +153,8 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl \ telephony/java/com/android/internal/telephony/ISms.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ - telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl + telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \ + vpn/java/android/net/vpn/IVpnService.aidl \ # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) @@ -190,6 +199,7 @@ aidl_files := \ frameworks/base/core/java/android/app/PendingIntent.aidl \ frameworks/base/core/java/android/content/ComponentName.aidl \ frameworks/base/core/java/android/content/Intent.aidl \ + frameworks/base/core/java/android/content/IntentSender.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/res/Configuration.aidl \ frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \ @@ -215,7 +225,8 @@ aidl_files := \ frameworks/base/location/java/android/location/Location.aidl \ frameworks/base/telephony/java/android/telephony/ServiceState.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ - frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl + frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \ + frameworks/base/vpn/java/android/net/vpn/IVpnService.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) @@ -337,7 +348,7 @@ web_docs_sample_code_flags := \ # most current Android platform version included in the SDK package. framework_docs_SDK_VERSION := 1.5 # release version for SDK (ie "Release x") -framework_docs_SDK_REL_ID := 1 +framework_docs_SDK_REL_ID := 2 framework_docs_SDK_CURRENT_DIR := $(framework_docs_SDK_VERSION)_r$(framework_docs_SDK_REL_ID) framework_docs_LOCAL_DROIDDOC_OPTIONS += \ @@ -220,3 +220,54 @@ the Apache2 License. END OF TERMS AND CONDITIONS + + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +and http://www.unicode.org/cldr/data/ . Unicode Software includes any +source code published in the Unicode Standard or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA +FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY +ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF +THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, +DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation (the +"Data Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, +and to permit persons to whom the Data Files or Software are furnished to +do so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File +or in the Software as well as in the documentation associated with the +Data File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT +OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in these Data Files or Software without prior written +authorization of the copyright holder. diff --git a/api/4.xml b/api/4.xml index 893301e10009..c8a2e83fd136 100644 --- a/api/4.xml +++ b/api/4.xml @@ -122,17 +122,6 @@ visibility="public" > </field> -<field name="ADD_SYSTEM_SERVICE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.ADD_SYSTEM_SERVICE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="BATTERY_STATS" type="java.lang.String" transient="false" @@ -463,17 +452,6 @@ visibility="public" > </field> -<field name="FOTA_UPDATE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.FOTA_UPDATE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="GET_ACCOUNTS" type="java.lang.String" transient="false" @@ -925,17 +903,6 @@ visibility="public" > </field> -<field name="SET_PROCESS_FOREGROUND" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.SET_PROCESS_FOREGROUND"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="SET_PROCESS_LIMIT" type="java.lang.String" transient="false" @@ -88854,7 +88821,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""title"" + value=""title_key"" static="true" final="true" deprecated="not deprecated" @@ -88951,7 +88918,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""title"" + value=""title_key"" static="true" final="true" deprecated="not deprecated" diff --git a/api/current.xml b/api/current.xml index eaf15122da2b..78562f09ac8f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -122,17 +122,6 @@ visibility="public" > </field> -<field name="ADD_SYSTEM_SERVICE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.ADD_SYSTEM_SERVICE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="BATTERY_STATS" type="java.lang.String" transient="false" @@ -309,6 +298,17 @@ visibility="public" > </field> +<field name="CHANGE_WIFI_MULTICAST_STATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.CHANGE_WIFI_MULTICAST_STATE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CHANGE_WIFI_STATE" type="java.lang.String" transient="false" @@ -463,17 +463,6 @@ visibility="public" > </field> -<field name="FOTA_UPDATE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.FOTA_UPDATE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="GET_ACCOUNTS" type="java.lang.String" transient="false" @@ -529,17 +518,6 @@ visibility="public" > </field> -<field name="INSTALL_LOCATION_COLLECTOR" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.INSTALL_LOCATION_COLLECTOR"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="INSTALL_LOCATION_PROVIDER" type="java.lang.String" transient="false" @@ -705,6 +683,17 @@ visibility="public" > </field> +<field name="READ_HISTORY_BOOKMARKS" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.READ_HISTORY_BOOKMARKS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="READ_INPUT_STATE" type="java.lang.String" transient="false" @@ -870,17 +859,6 @@ visibility="public" > </field> -<field name="SDCARD_WRITE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.SDCARD_WRITE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="SEND_SMS" type="java.lang.String" transient="false" @@ -958,17 +936,6 @@ visibility="public" > </field> -<field name="SET_PROCESS_FOREGROUND" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.permission.SET_PROCESS_FOREGROUND"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="SET_PROCESS_LIMIT" type="java.lang.String" transient="false" @@ -1046,6 +1013,17 @@ visibility="public" > </field> +<field name="STOP_APP_SWITCHES" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.STOP_APP_SWITCHES"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SUBSCRIBED_FEEDS_READ" type="java.lang.String" transient="false" @@ -1145,6 +1123,17 @@ visibility="public" > </field> +<field name="WRITE_EXTERNAL_STORAGE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.WRITE_EXTERNAL_STORAGE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="WRITE_GSERVICES" type="java.lang.String" transient="false" @@ -1156,6 +1145,17 @@ visibility="public" > </field> +<field name="WRITE_HISTORY_BOOKMARKS" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.WRITE_HISTORY_BOOKMARKS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="WRITE_OWNER_DATA" type="java.lang.String" transient="false" @@ -1449,6 +1449,237 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17432608" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17432599" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17432598" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17432597" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17432596" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17432595" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="17432594" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="17432593" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="17432592" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="17432591" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="17432590" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17432607" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="17432589" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad21" + type="int" + transient="false" + volatile="false" + value="17432588" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17432606" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17432605" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17432604" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17432603" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17432602" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17432601" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17432600" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="fade_in" type="int" transient="false" @@ -1471,6 +1702,17 @@ visibility="public" > </field> +<field name="linear_interpolator" + type="int" + transient="false" + volatile="false" + value="17432587" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="overshoot_interpolator" type="int" transient="false" @@ -1521,6 +1763,138 @@ visibility="public" > </constructor> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17235984" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17235975" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17235974" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17235973" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17235983" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17235982" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17235981" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17235980" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17235979" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17235978" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17235977" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17235976" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="emailAddressTypes" type="int" transient="false" @@ -1692,6 +2066,17 @@ visibility="public" > </field> +<field name="allowBackup" + type="int" + transient="false" + volatile="false" + value="16843392" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="allowClearUserData" type="int" transient="false" @@ -1923,6 +2308,17 @@ visibility="public" > </field> +<field name="backupAgent" + type="int" + transient="false" + volatile="false" + value="16843391" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="baselineAlignBottom" type="int" transient="false" @@ -2550,6 +2946,17 @@ visibility="public" > </field> +<field name="contentDescription" + type="int" + transient="false" + volatile="false" + value="16843379" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="cropToPadding" type="int" transient="false" @@ -2847,6 +3254,226 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="16843423" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="16843414" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="16843413" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="16843412" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="16843411" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="16843410" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="16843409" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="16843408" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="16843407" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="16843406" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="16843405" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="16843422" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="16843404" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="16843421" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="16843420" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="16843419" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="16843418" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="16843417" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="16843416" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="16843415" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="drawSelectorOnTop" type="int" transient="false" @@ -2946,6 +3573,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" @@ -3144,6 +3782,17 @@ visibility="public" > </field> +<field name="eventsInterceptionEnabled" + type="int" + transient="false" + volatile="false" + value="16843389" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="excludeFromRecents" type="int" transient="false" @@ -3265,6 +3914,39 @@ visibility="public" > </field> +<field name="fadeDuration" + type="int" + transient="false" + volatile="false" + value="16843384" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="fadeEnabled" + type="int" + transient="false" + volatile="false" + value="16843390" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="fadeOffset" + type="int" + transient="false" + volatile="false" + value="16843383" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="fadingEdge" type="int" transient="false" @@ -3584,6 +4266,83 @@ visibility="public" > </field> +<field name="gestureColor" + type="int" + transient="false" + volatile="false" + value="16843381" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="gestureStrokeAngleThreshold" + type="int" + transient="false" + volatile="false" + value="16843388" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="gestureStrokeLengthThreshold" + type="int" + transient="false" + volatile="false" + value="16843386" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="gestureStrokeSquarenessThreshold" + type="int" + transient="false" + volatile="false" + value="16843387" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="gestureStrokeType" + type="int" + transient="false" + volatile="false" + value="16843385" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="gestureStrokeWidth" + type="int" + transient="false" + volatile="false" + value="16843380" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="glEsVersion" + type="int" + transient="false" + volatile="false" + value="16843393" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="gradientRadius" type="int" transient="false" @@ -4464,6 +5223,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" @@ -5344,6 +6114,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" @@ -5894,6 +6675,17 @@ visibility="public" > </field> +<field name="progressBarStyleInverse" + type="int" + transient="false" + volatile="false" + value="16843399" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="progressBarStyleLarge" type="int" transient="false" @@ -5905,6 +6697,17 @@ visibility="public" > </field> +<field name="progressBarStyleLargeInverse" + type="int" + transient="false" + volatile="false" + value="16843401" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="progressBarStyleSmall" type="int" transient="false" @@ -5916,6 +6719,17 @@ visibility="public" > </field> +<field name="progressBarStyleSmallInverse" + type="int" + transient="false" + volatile="false" + value="16843400" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="progressBarStyleSmallTitle" type="int" transient="false" @@ -5971,6 +6785,17 @@ visibility="public" > </field> +<field name="queryAfterZeroResults" + type="int" + transient="false" + volatile="false" + value="16843394" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="radioButtonStyle" type="int" transient="false" @@ -6455,6 +7280,17 @@ visibility="public" > </field> +<field name="searchSettingsDescription" + type="int" + transient="false" + volatile="false" + value="16843402" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="searchSuggestAuthority" type="int" transient="false" @@ -6719,6 +7555,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" @@ -7555,6 +8402,17 @@ visibility="public" > </field> +<field name="textColorPrimaryInverseDisableOnly" + type="int" + transient="false" + volatile="false" + value="16843403" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="textColorPrimaryInverseNoDisable" type="int" transient="false" @@ -7984,6 +8842,17 @@ visibility="public" > </field> +<field name="uncertainGestureColor" + type="int" + transient="false" + volatile="false" + value="16843382" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="unselectedAlpha" type="int" transient="false" @@ -8557,6 +9426,347 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17170480" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17170471" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17170470" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17170469" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17170468" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17170467" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="17170466" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="17170465" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="17170464" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="17170463" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="17170462" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17170479" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="17170461" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad21" + type="int" + transient="false" + volatile="false" + value="17170460" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad22" + type="int" + transient="false" + volatile="false" + value="17170459" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad23" + type="int" + transient="false" + volatile="false" + value="17170458" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad24" + type="int" + transient="false" + volatile="false" + value="17170457" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad25" + type="int" + transient="false" + volatile="false" + value="17170456" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad26" + type="int" + transient="false" + volatile="false" + value="17170455" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad27" + type="int" + transient="false" + volatile="false" + value="17170454" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad28" + type="int" + transient="false" + volatile="false" + value="17170453" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad29" + type="int" + transient="false" + volatile="false" + value="17170452" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17170478" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad30" + type="int" + transient="false" + volatile="false" + value="17170451" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad31" + type="int" + transient="false" + volatile="false" + value="17170450" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17170477" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17170476" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17170475" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17170474" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17170473" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17170472" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="primary_text_dark" type="int" transient="false" @@ -8739,6 +9949,160 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17104912" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17104903" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17104902" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17104901" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17104900" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17104899" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17104911" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17104910" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17104909" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17104908" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17104907" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17104906" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17104905" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17104904" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="thumbnail_height" type="int" transient="false" @@ -9053,6 +10417,446 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17301712" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17301703" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17301702" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17301701" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17301700" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17301699" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="17301698" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="17301697" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="17301696" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="17301695" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="17301694" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17301711" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="17301693" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad21" + type="int" + transient="false" + volatile="false" + value="17301692" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad22" + type="int" + transient="false" + volatile="false" + value="17301691" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad23" + type="int" + transient="false" + volatile="false" + value="17301690" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad24" + type="int" + transient="false" + volatile="false" + value="17301689" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad25" + type="int" + transient="false" + volatile="false" + value="17301688" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad26" + type="int" + transient="false" + volatile="false" + value="17301687" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad27" + type="int" + transient="false" + volatile="false" + value="17301686" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad28" + type="int" + transient="false" + volatile="false" + value="17301685" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad29" + type="int" + transient="false" + volatile="false" + value="17301684" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17301710" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad30" + type="int" + transient="false" + volatile="false" + value="17301683" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad31" + type="int" + transient="false" + volatile="false" + value="17301682" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad32" + type="int" + transient="false" + volatile="false" + value="17301681" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad33" + type="int" + transient="false" + volatile="false" + value="17301680" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad34" + type="int" + transient="false" + volatile="false" + value="17301679" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad35" + type="int" + transient="false" + volatile="false" + value="17301678" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad36" + type="int" + transient="false" + volatile="false" + value="17301677" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad37" + type="int" + transient="false" + volatile="false" + value="17301676" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad38" + type="int" + transient="false" + volatile="false" + value="17301675" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad39" + type="int" + transient="false" + volatile="false" + value="17301674" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17301709" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad40" + type="int" + transient="false" + volatile="false" + value="17301673" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17301708" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17301707" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17301706" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17301705" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17301704" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="edit_text" type="int" transient="false" @@ -10388,7 +12192,7 @@ type="int" transient="false" volatile="false" - value="17302061" + value="17301671" static="true" final="true" deprecated="not deprecated" @@ -10399,7 +12203,7 @@ type="int" transient="false" volatile="false" - value="17302062" + value="17301672" static="true" final="true" deprecated="not deprecated" @@ -10720,6 +12524,248 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="16908352" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="16908343" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="16908342" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="16908341" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="16908340" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="16908339" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="16908338" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="16908337" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="16908336" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="16908335" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="16908334" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="16908351" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="16908333" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad21" + type="int" + transient="false" + volatile="false" + value="16908332" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad22" + type="int" + transient="false" + volatile="false" + value="16908331" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="16908350" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="16908349" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="16908348" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="16908347" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="16908346" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="16908345" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="16908344" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="edit" type="int" transient="false" @@ -11111,6 +13157,160 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17694736" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17694727" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17694726" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17694725" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17694724" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17694723" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17694735" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17694734" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17694733" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17694732" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17694731" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17694730" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17694729" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17694728" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="R.layout" extends="java.lang.Object" @@ -11150,6 +13350,149 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17367072" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17367063" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17367062" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17367061" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17367060" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17367071" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17367070" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17367069" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17367068" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17367067" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17367066" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17367065" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17367064" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="expandable_list_content" type="int" transient="false" @@ -11520,6 +13863,303 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="17039408" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="17039399" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="17039398" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="17039397" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="17039396" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="17039395" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="17039394" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="17039393" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="17039392" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="17039391" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="17039390" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="17039407" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad20" + type="int" + transient="false" + volatile="false" + value="17039389" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad21" + type="int" + transient="false" + volatile="false" + value="17039388" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad22" + type="int" + transient="false" + volatile="false" + value="17039387" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad23" + type="int" + transient="false" + volatile="false" + value="17039386" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad24" + type="int" + transient="false" + volatile="false" + value="17039385" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad25" + type="int" + transient="false" + volatile="false" + value="17039384" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad26" + type="int" + transient="false" + volatile="false" + value="17039383" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad27" + type="int" + transient="false" + volatile="false" + value="17039382" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="17039406" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="17039405" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="17039404" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="17039403" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="17039402" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="17039401" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="17039400" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="emptyPhoneNumber" type="int" transient="false" @@ -12516,6 +15156,17 @@ visibility="public" > </field> +<field name="Widget_ProgressBar_Inverse" + type="int" + transient="false" + volatile="false" + value="16973915" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="Widget_ProgressBar_Large" type="int" transient="false" @@ -12527,6 +15178,17 @@ visibility="public" > </field> +<field name="Widget_ProgressBar_Large_Inverse" + type="int" + transient="false" + volatile="false" + value="16973916" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="Widget_ProgressBar_Small" type="int" transient="false" @@ -12538,6 +15200,17 @@ visibility="public" > </field> +<field name="Widget_ProgressBar_Small_Inverse" + type="int" + transient="false" + volatile="false" + value="16973917" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="Widget_RatingBar" type="int" transient="false" @@ -12637,6 +15310,215 @@ visibility="public" > </field> +<field name="donut_resource_pad1" + type="int" + transient="false" + volatile="false" + value="16973936" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad10" + type="int" + transient="false" + volatile="false" + value="16973927" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad11" + type="int" + transient="false" + volatile="false" + value="16973926" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad12" + type="int" + transient="false" + volatile="false" + value="16973925" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad13" + type="int" + transient="false" + volatile="false" + value="16973924" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad14" + type="int" + transient="false" + volatile="false" + value="16973923" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad15" + type="int" + transient="false" + volatile="false" + value="16973922" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad16" + type="int" + transient="false" + volatile="false" + value="16973921" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad17" + type="int" + transient="false" + volatile="false" + value="16973920" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad18" + type="int" + transient="false" + volatile="false" + value="16973919" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad19" + type="int" + transient="false" + volatile="false" + value="16973918" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad2" + type="int" + transient="false" + volatile="false" + value="16973935" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad3" + type="int" + transient="false" + volatile="false" + value="16973934" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad4" + type="int" + transient="false" + volatile="false" + value="16973933" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad5" + type="int" + transient="false" + volatile="false" + value="16973932" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad6" + type="int" + transient="false" + volatile="false" + value="16973931" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad7" + type="int" + transient="false" + volatile="false" + value="16973930" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad8" + type="int" + transient="false" + volatile="false" + value="16973929" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="donut_resource_pad9" + type="int" + transient="false" + volatile="false" + value="16973928" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="R.xml" extends="java.lang.Object" @@ -12656,6 +15538,270 @@ </constructor> </class> </package> +<package name="android.accessibilityservice" +> +<class name="AccessibilityService" + extends="android.app.Service" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="AccessibilityService" + type="android.accessibilityservice.AccessibilityService" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="onAccessibilityEvent" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> +<method name="onBind" + return="android.os.IBinder" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +</method> +<method name="onInterrupt" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="onServiceConnected" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +</method> +<method name="setServiceInfo" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="info" type="android.accessibilityservice.AccessibilityServiceInfo"> +</parameter> +</method> +<field name="SERVICE_INTERFACE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.accessibilityservice.AccessibilityService"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="AccessibilityServiceInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<constructor name="AccessibilityServiceInfo" + type="android.accessibilityservice.AccessibilityServiceInfo" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DEFAULT" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FEEDBACK_AUDIBLE" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FEEDBACK_GENERIC" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FEEDBACK_HAPTIC" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FEEDBACK_SPOKEN" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FEEDBACK_VISUAL" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="eventTypes" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="feedbackType" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="flags" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="notificationTimeout" + type="long" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="packageNames" + type="java.lang.String[]" + transient="false" + volatile="false" + value="null" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +</package> <package name="android.app" > <class name="Activity" @@ -12764,6 +15910,19 @@ <parameter name="event" type="android.view.KeyEvent"> </parameter> </method> +<method name="dispatchPopulateAccessibilityEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> <method name="dispatchTouchEvent" return="boolean" abstract="false" @@ -16759,6 +19918,19 @@ <parameter name="event" type="android.view.KeyEvent"> </parameter> </method> +<method name="dispatchPopulateAccessibilityEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> <method name="dispatchTouchEvent" return="boolean" abstract="false" @@ -18916,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" @@ -19024,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" @@ -19798,6 +22993,17 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="getIntentSender" + return="android.content.IntentSender" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getService" return="android.app.PendingIntent" abstract="false" @@ -20669,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" @@ -24312,6 +27529,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="true" @@ -24582,6 +27810,17 @@ <parameter name="modeFlags" type="int"> </parameter> </method> +<method name="isRestricted" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="obtainStyledAttributes" return="android.content.res.TypedArray" abstract="false" @@ -24974,6 +28213,17 @@ <parameter name="receiver" type="android.content.BroadcastReceiver"> </parameter> </method> +<field name="ACCESSIBILITY_SERVICE" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessibility"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTIVITY_SERVICE" type="java.lang.String" transient="false" @@ -25084,6 +28334,17 @@ visibility="public" > </field> +<field name="CONTEXT_RESTRICTED" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="INPUT_METHOD_SERVICE" type="java.lang.String" transient="false" @@ -25653,6 +28914,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="false" @@ -25841,6 +29113,19 @@ <parameter name="mode" type="int"> </parameter> </method> +<method name="getSharedPrefsFile" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</method> <method name="getSystemService" return="java.lang.Object" abstract="false" @@ -26455,6 +29740,70 @@ </parameter> </method> </interface> +<interface name="IIntentReceiver" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.IInterface"> +</implements> +<method name="performReceive" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultCode" type="int"> +</parameter> +<parameter name="data" type="java.lang.String"> +</parameter> +<parameter name="extras" type="android.os.Bundle"> +</parameter> +<parameter name="ordered" type="boolean"> +</parameter> +<exception name="RemoteException" type="android.os.RemoteException"> +</exception> +</method> +</interface> +<interface name="IIntentSender" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.IInterface"> +</implements> +<method name="send" + return="int" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="code" type="int"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resolvedType" type="java.lang.String"> +</parameter> +<parameter name="finishedReceiver" type="android.content.IIntentReceiver"> +</parameter> +<exception name="RemoteException" type="android.os.RemoteException"> +</exception> +</method> +</interface> <class name="Intent" extends="java.lang.Object" abstract="false" @@ -26937,7 +30286,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="uri" type="java.lang.String"> @@ -26988,6 +30337,17 @@ <parameter name="defaultValue" type="long"> </parameter> </method> +<method name="getPackage" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getParcelableArrayExtra" return="android.os.Parcelable[]" abstract="false" @@ -27187,6 +30547,23 @@ <exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException"> </exception> </method> +<method name="parseUri" + return="android.content.Intent" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="java.lang.String"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +<exception name="URISyntaxException" type="java.net.URISyntaxException"> +</exception> +</method> <method name="putExtra" return="android.content.Intent" abstract="false" @@ -27860,6 +31237,19 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="setPackage" + return="android.content.Intent" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +</method> <method name="setType" return="android.content.Intent" abstract="false" @@ -27880,9 +31270,22 @@ synchronized="false" static="false" final="false" + deprecated="deprecated" + visibility="public" +> +</method> +<method name="toUri" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" deprecated="not deprecated" visibility="public" > +<parameter name="flags" type="int"> +</parameter> </method> <method name="writeToParcel" return="void" @@ -27965,6 +31368,17 @@ visibility="public" > </field> +<field name="ACTION_BATTERY_OKAY" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.BATTERY_OKAY"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_BOOT_COMPLETED" type="java.lang.String" transient="false" @@ -28526,6 +31940,17 @@ visibility="public" > </field> +<field name="ACTION_POWER_USAGE_SUMMARY" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.POWER_USAGE_SUMMARY"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_PROVIDER_CHANGED" type="java.lang.String" transient="false" @@ -28625,6 +32050,17 @@ visibility="public" > </field> +<field name="ACTION_SEND_MULTIPLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.SEND_MULTIPLE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_SET_WALLPAPER" type="java.lang.String" transient="false" @@ -29251,6 +32687,17 @@ visibility="public" > </field> +<field name="FILL_IN_PACKAGE" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_ACTIVITY_BROUGHT_TO_FRONT" type="int" transient="false" @@ -29460,6 +32907,17 @@ visibility="public" > </field> +<field name="URI_INTENT_SCHEME" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Intent.FilterComparison" extends="java.lang.Object" @@ -30474,6 +33932,190 @@ </parameter> </constructor> </class> +<class name="IntentSender" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<constructor name="IntentSender" + type="android.content.IntentSender" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="target" type="android.content.IIntentSender"> +</parameter> +</constructor> +<constructor name="IntentSender" + type="android.content.IntentSender" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="target" type="android.os.IBinder"> +</parameter> +</constructor> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="readIntentSenderOrNullFromParcel" + return="android.content.IntentSender" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="in" type="android.os.Parcel"> +</parameter> +</method> +<method name="sendIntent" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="code" type="int"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="onFinished" type="android.content.IntentSender.OnFinished"> +</parameter> +<parameter name="handler" type="android.os.Handler"> +</parameter> +<exception name="IntentSender.SendIntentException" type="android.content.IntentSender.SendIntentException"> +</exception> +</method> +<method name="writeIntentSenderOrNullToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="sender" type="android.content.IntentSender"> +</parameter> +<parameter name="out" type="android.os.Parcel"> +</parameter> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="out" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<interface name="IntentSender.OnFinished" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onSendFinished" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="IntentSender" type="android.content.IntentSender"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultCode" type="int"> +</parameter> +<parameter name="resultData" type="java.lang.String"> +</parameter> +<parameter name="resultExtras" type="android.os.Bundle"> +</parameter> +</method> +</interface> +<class name="IntentSender.SendIntentException" + extends="android.util.AndroidException" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</constructor> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cause" type="java.lang.Exception"> +</parameter> +</constructor> +</class> <class name="MutableContextWrapper" extends="android.content.ContextWrapper" abstract="false" @@ -31212,6 +34854,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" @@ -31676,29 +35329,29 @@ visibility="public" > </field> -<field name="FLAG_SYSTEM" +<field name="FLAG_SUPPORTS_LARGE_SCREENS" type="int" transient="false" volatile="false" - value="1" + value="2048" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FLAG_TARGETS_SDK" +<field name="FLAG_SUPPORTS_NORMAL_SCREENS" type="int" transient="false" volatile="false" - value="256" + value="1024" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FLAG_TEST_ONLY" +<field name="FLAG_SUPPORTS_SMALL_SCREENS" type="int" transient="false" volatile="false" @@ -31709,6 +35362,28 @@ visibility="public" > </field> +<field name="FLAG_SYSTEM" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FLAG_TEST_ONLY" + type="int" + transient="false" + volatile="false" + value="256" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_UPDATED_SYSTEM_APP" type="int" transient="false" @@ -31842,6 +35517,16 @@ visibility="public" > </field> +<field name="targetSdkVersion" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="taskAffinity" type="java.lang.String" transient="false" @@ -32036,6 +35721,17 @@ visibility="public" > </method> +<method name="getGlEsVersion" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="writeToParcel" return="void" abstract="false" @@ -32061,6 +35757,17 @@ visibility="public" > </field> +<field name="GL_ES_VERSION_UNDEFINED" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="INPUT_FEATURE_FIVE_WAY_NAV" type="int" transient="false" @@ -32083,6 +35790,16 @@ visibility="public" > </field> +<field name="reqGlEsVersion" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="reqInputFeatures" type="int" transient="false" @@ -33009,8 +36726,6 @@ > <parameter name="packageName" type="java.lang.String"> </parameter> -<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException"> -</exception> </method> <method name="getNameForUid" return="java.lang.String" @@ -35839,6 +39554,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" @@ -35973,6 +39732,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" @@ -43408,6 +47177,17 @@ visibility="public" > </method> +<method name="prepareToDraw" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="recycle" return="void" abstract="false" @@ -43763,6 +47543,16 @@ visibility="public" > </field> +<field name="inInputShareable" + type="boolean" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inJustDecodeBounds" type="boolean" transient="false" @@ -43783,6 +47573,16 @@ visibility="public" > </field> +<field name="inPurgeable" + type="boolean" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inSampleSize" type="int" transient="false" @@ -52911,6 +56711,47 @@ </package> <package name="android.graphics.drawable" > +<interface name="Animatable" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="isRunning" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="start" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="stop" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</interface> <class name="AnimationDrawable" extends="android.graphics.drawable.DrawableContainer" abstract="false" @@ -52919,6 +56760,8 @@ deprecated="not deprecated" visibility="public" > +<implements name="android.graphics.drawable.Animatable"> +</implements> <implements name="java.lang.Runnable"> </implements> <constructor name="AnimationDrawable" @@ -66536,6 +70379,17 @@ visibility="public" > </constructor> +<method name="getAudioSourceMax" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getMaxAmplitude" return="int" abstract="false" @@ -66937,6 +70791,39 @@ visibility="public" > </field> +<field name="VOICE_CALL" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="VOICE_DOWNLINK" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="VOICE_UPLINK" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <interface name="MediaRecorder.OnErrorListener" abstract="true" @@ -68088,6 +71975,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" @@ -72514,6 +77116,19 @@ <parameter name="rssiB" type="int"> </parameter> </method> +<method name="createMulticastLock" + return="android.net.wifi.WifiManager.MulticastLock" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="tag" type="java.lang.String"> +</parameter> +</method> <method name="createWifiLock" return="android.net.wifi.WifiManager.WifiLock" abstract="false" @@ -73006,6 +77621,48 @@ > </field> </class> +<class name="WifiManager.MulticastLock" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="acquire" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isHeld" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="release" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> <class name="WifiManager.WifiLock" extends="java.lang.Object" abstract="false" @@ -85448,6 +90105,16 @@ visibility="public" > </field> +<field name="CPU_ABI" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="DEVICE" type="java.lang.String" transient="false" @@ -85498,6 +90165,16 @@ visibility="public" > </field> +<field name="MANUFACTURER" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="MODEL" type="java.lang.String" transient="false" @@ -85675,6 +90352,28 @@ visibility="public" > </field> +<field name="CUR_DEVELOPMENT" + type="int" + transient="false" + volatile="false" + value="10000" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DONUT" + type="int" + transient="false" + volatile="false" + value="10000" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Bundle" extends="java.lang.Object" @@ -89121,6 +93820,8 @@ </parameter> <parameter name="length" type="int"> </parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> </constructor> <method name="allowPurging" return="boolean" @@ -99812,7 +104513,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""title"" + value=""title_key"" static="true" final="true" deprecated="not deprecated" @@ -99909,7 +104610,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""title"" + value=""title_key"" static="true" final="true" deprecated="not deprecated" @@ -101868,6 +106569,17 @@ <parameter name="value" type="java.lang.String"> </parameter> </method> +<field name="ACCESSIBILITY_ENABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessibility_enabled"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ADB_ENABLED" type="java.lang.String" transient="false" @@ -101966,6 +106678,17 @@ visibility="public" > </field> +<field name="ENABLED_ACCESSIBILITY_SERVICES" + type="java.lang.String" + transient="false" + volatile="false" + value=""enabled_accessibility_services"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ENABLED_INPUT_METHODS" type="java.lang.String" transient="false" @@ -102087,6 +106810,83 @@ visibility="public" > </field> +<field name="TTS_DEFAULT_COUNTRY" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_country"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_DEFAULT_LANG" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_lang"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_DEFAULT_PITCH" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_pitch"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_DEFAULT_RATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_rate"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_DEFAULT_SYNTH" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_synth"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_DEFAULT_VARIANT" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_variant"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_USE_DEFAULTS" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_use_defaults"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="USB_MASS_STORAGE_ENABLED" type="java.lang.String" transient="false" @@ -103135,6 +107935,17 @@ visibility="public" > </field> +<field name="SHOW_WEB_SUGGESTIONS" + type="java.lang.String" + transient="false" + volatile="false" + value=""show_web_suggestions"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SOUND_EFFECTS_ENABLED" type="java.lang.String" transient="false" @@ -104320,6 +109131,344 @@ </field> </class> </package> +<package name="android.speech.tts" +> +<class name="TextToSpeech" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="TextToSpeech" + type="android.speech.tts.TextToSpeech" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="listener" type="android.speech.tts.TextToSpeech.OnInitListener"> +</parameter> +</constructor> +<method name="addSpeech" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="text" type="java.lang.String"> +</parameter> +<parameter name="packagename" type="java.lang.String"> +</parameter> +<parameter name="resourceId" type="int"> +</parameter> +</method> +<method name="addSpeech" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="text" type="java.lang.String"> +</parameter> +<parameter name="filename" type="java.lang.String"> +</parameter> +</method> +<method name="getLanguage" + return="java.util.Locale" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isLanguageAvailable" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="loc" type="java.util.Locale"> +</parameter> +</method> +<method name="isSpeaking" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="playEarcon" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="earcon" type="java.lang.String"> +</parameter> +<parameter name="queueMode" type="int"> +</parameter> +<parameter name="params" type="java.util.HashMap<java.lang.String, java.lang.String>"> +</parameter> +</method> +<method name="playSilence" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="durationInMs" type="long"> +</parameter> +<parameter name="queueMode" type="int"> +</parameter> +</method> +<method name="setLanguage" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="loc" type="java.util.Locale"> +</parameter> +</method> +<method name="setPitch" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="pitch" type="float"> +</parameter> +</method> +<method name="setSpeechRate" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="speechRate" type="float"> +</parameter> +</method> +<method name="shutdown" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="speak" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="text" type="java.lang.String"> +</parameter> +<parameter name="queueMode" type="int"> +</parameter> +<parameter name="params" type="java.util.HashMap<java.lang.String, java.lang.String>"> +</parameter> +</method> +<method name="stop" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="synthesizeToFile" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="text" type="java.lang.String"> +</parameter> +<parameter name="params" type="java.util.HashMap<java.lang.String, java.lang.String>"> +</parameter> +<parameter name="filename" type="java.lang.String"> +</parameter> +</method> +<field name="TTS_ERROR" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_LANG_AVAILABLE" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_LANG_COUNTRY_AVAILABLE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_LANG_COUNTRY_VAR_AVAILABLE" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_LANG_MISSING_DATA" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_LANG_NOT_SUPPORTED" + type="int" + transient="false" + volatile="false" + value="-2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_QUEUE_ADD" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_QUEUE_FLUSH" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TTS_SUCCESS" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<interface name="TextToSpeech.OnInitListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onInit" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="status" type="int"> +</parameter> +</method> +</interface> +</package> <package name="android.telephony" > <class name="CellLocation" @@ -107290,6 +112439,53 @@ visibility="public" > </constructor> +<method name="assertActivityRequiresPermission" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +<parameter name="className" type="java.lang.String"> +</parameter> +<parameter name="permission" type="java.lang.String"> +</parameter> +</method> +<method name="assertReadingContentUriRequiresPermission" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +<parameter name="permission" type="java.lang.String"> +</parameter> +</method> +<method name="assertWritingContentUriRequiresPermission" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +<parameter name="permission" type="java.lang.String"> +</parameter> +</method> <method name="getContext" return="android.content.Context" abstract="false" @@ -110530,6 +115726,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="false" @@ -110707,6 +115914,19 @@ <parameter name="mode" type="int"> </parameter> </method> +<method name="getSharedPrefsFile" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</method> <method name="getSystemService" return="java.lang.Object" abstract="false" @@ -111482,8 +116702,6 @@ > <parameter name="packageName" type="java.lang.String"> </parameter> -<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException"> -</exception> </method> <method name="getNameForUid" return="java.lang.String" @@ -134522,6 +139740,21 @@ <parameter name="units" type="int"> </parameter> </method> +<method name="computeCurrentVelocity" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="units" type="int"> +</parameter> +<parameter name="maxVelocity" type="float"> +</parameter> +</method> <method name="getXVelocity" return="float" abstract="false" @@ -134575,6 +139808,8 @@ deprecated="not deprecated" visibility="public" > +<implements name="android.view.accessibility.AccessibilityEventSource"> +</implements> <implements name="android.graphics.drawable.Drawable.Callback"> </implements> <implements name="android.view.KeyEvent.Callback"> @@ -134630,6 +139865,23 @@ <parameter name="direction" type="int"> </parameter> </method> +<method name="addFocusables" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="views" type="java.util.ArrayList<android.view.View>"> +</parameter> +<parameter name="direction" type="int"> +</parameter> +<parameter name="focusableMode" type="int"> +</parameter> +</method> <method name="addTouchables" return="void" abstract="false" @@ -134665,6 +139917,19 @@ visibility="public" > </method> +<method name="buildDrawingCache" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="autoScale" type="boolean"> +</parameter> +</method> <method name="cancelLongPress" return="void" abstract="false" @@ -134864,6 +140129,19 @@ <parameter name="event" type="android.view.KeyEvent"> </parameter> </method> +<method name="dispatchPopulateAccessibilityEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> <method name="dispatchRestoreInstanceState" return="void" abstract="false" @@ -135158,6 +140436,17 @@ visibility="protected" > </method> +<method name="getContentDescription" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getContext" return="android.content.Context" abstract="false" @@ -135217,6 +140506,19 @@ visibility="public" > </method> +<method name="getDrawingCache" + return="android.graphics.Bitmap" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="autoScale" type="boolean"> +</parameter> +</method> <method name="getDrawingCacheBackgroundColor" return="int" abstract="false" @@ -137114,6 +142416,32 @@ <parameter name="y" type="int"> </parameter> </method> +<method name="sendAccessibilityEvent" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="eventType" type="int"> +</parameter> +</method> +<method name="sendAccessibilityEventUnchecked" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> <method name="setAnimation" return="void" abstract="false" @@ -137179,6 +142507,19 @@ <parameter name="clickable" type="boolean"> </parameter> </method> +<method name="setContentDescription" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="contentDescription" type="java.lang.CharSequence"> +</parameter> +</method> <method name="setDrawingCacheBackgroundColor" return="void" abstract="false" @@ -137967,6 +143308,28 @@ visibility="protected" > </field> +<field name="FOCUSABLES_ALL" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FOCUSABLES_TOUCH_MODE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FOCUSED_SELECTED_STATE_SET" type="int[]" transient="false" @@ -138795,6 +144158,17 @@ visibility="public" > </method> +<method name="getMaximumFlingVelocity" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="deprecated" + visibility="public" +> +</method> <method name="getMinimumFlingVelocity" return="int" abstract="false" @@ -138861,6 +144235,17 @@ visibility="public" > </method> +<method name="getScaledMaximumFlingVelocity" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getScaledMinimumFlingVelocity" return="int" abstract="false" @@ -142659,6 +148044,19 @@ <parameter name="event" type="android.view.KeyEvent"> </parameter> </method> +<method name="dispatchPopulateAccessibilityEvent" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> <method name="dispatchTouchEvent" return="boolean" abstract="true" @@ -144083,6 +149481,698 @@ </field> </class> </package> +<package name="android.view.accessibility" +> +<class name="AccessibilityEvent" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getAddedCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getBeforeText" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getClassName" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getContentDescription" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getCurrentItemIndex" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEventTime" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEventType" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getFromIndex" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getItemCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getPackageName" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getParcelableData" + return="android.os.Parcelable" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getRemovedCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getText" + return="java.util.List<java.lang.CharSequence>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="initFromParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="parcel" type="android.os.Parcel"> +</parameter> +</method> +<method name="isChecked" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isFullScreen" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isPassword" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="obtain" + return="android.view.accessibility.AccessibilityEvent" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="eventType" type="int"> +</parameter> +</method> +<method name="obtain" + return="android.view.accessibility.AccessibilityEvent" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="recycle" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="setAddedCount" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="addedCount" type="int"> +</parameter> +</method> +<method name="setBeforeText" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="beforeText" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setChecked" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isChecked" type="boolean"> +</parameter> +</method> +<method name="setClassName" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="className" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setContentDescription" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="contentDescription" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setCurrentItemIndex" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="currentItemIndex" type="int"> +</parameter> +</method> +<method name="setEnabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isEnabled" type="boolean"> +</parameter> +</method> +<method name="setEventTime" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="eventTime" type="long"> +</parameter> +</method> +<method name="setEventType" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="eventType" type="int"> +</parameter> +</method> +<method name="setFromIndex" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fromIndex" type="int"> +</parameter> +</method> +<method name="setFullScreen" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isFullScreen" type="boolean"> +</parameter> +</method> +<method name="setItemCount" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="itemCount" type="int"> +</parameter> +</method> +<method name="setPackageName" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setParcelableData" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="parcelableData" type="android.os.Parcelable"> +</parameter> +</method> +<method name="setPassword" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isPassword" type="boolean"> +</parameter> +</method> +<method name="setRemovedCount" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="removedCount" type="int"> +</parameter> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="INVALID_POSITION" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MAX_TEXT_LENGTH" + type="int" + transient="false" + volatile="false" + value="500" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPES_ALL_MASK" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_NOTIFICATION_STATE_CHANGED" + type="int" + transient="false" + volatile="false" + value="64" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_VIEW_CLICKED" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_VIEW_FOCUSED" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_VIEW_LONG_CLICKED" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_VIEW_SELECTED" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_VIEW_TEXT_CHANGED" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_WINDOW_STATE_CHANGED" + type="int" + transient="false" + volatile="false" + value="32" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<interface name="AccessibilityEventSource" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="sendAccessibilityEvent" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="eventType" type="int"> +</parameter> +</method> +<method name="sendAccessibilityEventUnchecked" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> +</interface> +<class name="AccessibilityManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<method name="getAccessibilityServiceList" + return="java.util.List<android.content.pm.ServiceInfo>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="interrupt" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="sendAccessibilityEvent" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.accessibility.AccessibilityEvent"> +</parameter> +</method> +</class> +</package> <package name="android.view.animation" > <class name="AccelerateDecelerateInterpolator" @@ -156658,6 +162748,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" @@ -156881,6 +162982,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" @@ -163442,6 +169556,21 @@ deprecated="not deprecated" visibility="public" > +<parameter name="width" type="int"> +</parameter> +<parameter name="height" type="int"> +</parameter> +</method> +<method name="update" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> <parameter name="x" type="int"> </parameter> <parameter name="y" type="int"> @@ -167532,6 +173661,17 @@ deprecated="not deprecated" visibility="public" > +<method name="getTag" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="setContent" return="android.widget.TabHost.TabSpec" abstract="false" @@ -167599,6 +173739,19 @@ <parameter name="icon" type="android.graphics.drawable.Drawable"> </parameter> </method> +<method name="setIndicator" + return="android.widget.TabHost.TabSpec" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="view" type="android.view.View"> +</parameter> +</method> </class> <class name="TabWidget" extends="android.widget.LinearLayout" @@ -167672,6 +173825,30 @@ <parameter name="index" type="int"> </parameter> </method> +<method name="getChildTabViewAt" + return="android.view.View" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="index" type="int"> +</parameter> +</method> +<method name="getTabCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="onFocusChange" return="void" abstract="false" @@ -167700,6 +173877,32 @@ <parameter name="index" type="int"> </parameter> </method> +<method name="setDividerDrawable" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="drawable" type="android.graphics.drawable.Drawable"> +</parameter> +</method> +<method name="setDividerDrawable" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="resId" type="int"> +</parameter> +</method> </class> <class name="TableLayout" extends="android.widget.LinearLayout" @@ -174961,7 +181164,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 f85ea9f85054..022fe5addcc3 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 { @@ -42,6 +43,7 @@ extern "C" { #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> +#include <signal.h> } // When you enable this, as well as DEBUG_REFS=1 and @@ -63,6 +65,10 @@ extern "C" { static int debug_frame_cnt; #endif +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + // ---------------------------------------------------------------------------- void CameraService::instantiate() { @@ -76,6 +82,7 @@ CameraService::CameraService() : BnCameraService() { LOGI("CameraService started: pid=%d", getpid()); + mUsers = 0; } CameraService::~CameraService() @@ -87,72 +94,105 @@ CameraService::~CameraService() sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) { - LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get()); + int callingPid = getCallingPid(); + 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(); if (currentClient != 0) { sp<ICameraClient> currentCameraClient(currentClient->getCameraClient()); if (cameraClient->asBinder() == currentCameraClient->asBinder()) { - // this is the same client reconnecting... - LOGD("Connect X same client (%p) is reconnecting...", cameraClient->asBinder().get()); + // This is the same client reconnecting... + LOGD("CameraService::connect X (pid %d, same client %p) is reconnecting...", + callingPid, cameraClient->asBinder().get()); return currentClient; } else { - // it's another client... reject it - LOGD("new client (%p) attempting to connect - rejected", cameraClient->asBinder().get()); + // It's another client... reject it + LOGD("CameraService::connect X (pid %d, new client %p) rejected. " + "(old pid %d, old client %p)", + callingPid, cameraClient->asBinder().get(), + currentClient->mClientPid, currentCameraClient->asBinder().get()); + if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) { + LOGD("The old client is dead!"); + } return client; } } else { // can't promote, the previous client has died... - LOGD("new client connecting, old reference was dangling..."); + LOGD("New client (pid %d) connecting, old reference was dangling...", + callingPid); mClient.clear(); } } + if (mUsers > 0) { + LOGD("Still have client, rejected"); + return client; + } + // create a new Client object - client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid()); + client = new Client(this, cameraClient, callingPid); mClient = client; #if DEBUG_CLIENT_REFERENCES // Enable tracking for this object, and track increments and decrements of // the refcount. client->trackMe(true, true); #endif - LOGD("Connect X"); + LOGD("CameraService::connect X"); return client; } void CameraService::removeClient(const sp<ICameraClient>& cameraClient) { - // declar this outside the lock to make absolutely sure the + int callingPid = getCallingPid(); + + // Declare this outside the lock to make absolutely sure the // 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. - LOGV("mClient is null."); + LOGD("removeClient (pid %d): already disconnected", callingPid); return; } - // Promote mClient. It should never fail because we're called from - // a binder call, so someone has to have a strong reference. + // Promote mClient. It can fail if we are called from this path: + // Client::~Client() -> disconnect() -> removeClient(). client = mClient.promote(); if (client == 0) { - LOGW("can't get a strong reference on mClient!"); + LOGD("removeClient (pid %d): no more strong reference", callingPid); mClient.clear(); return; } if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { // ugh! that's not our client!! - LOGW("removeClient() called, but mClient doesn't match!"); + LOGW("removeClient (pid %d): mClient doesn't match!", callingPid); } else { // okay, good, forget about mClient mClient.clear(); } + + 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) @@ -177,7 +217,8 @@ static sp<MediaPlayer> newMediaPlayer(const char *file) CameraService::Client::Client(const sp<CameraService>& cameraService, const sp<ICameraClient>& cameraClient, pid_t clientPid) { - LOGD("Client E constructor"); + int callingPid = getCallingPid(); + LOGD("Client::Client E (pid %d)", callingPid); mCameraService = cameraService; mCameraClient = cameraClient; mClientPid = clientPid; @@ -189,22 +230,28 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, // Callback is disabled by default mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; - LOGD("Client X constructor"); + cameraService->incUsers(); + LOGD("Client::Client X (pid %d)", callingPid); } status_t CameraService::Client::checkPid() { - if (mClientPid == IPCThreadState::self()->getCallingPid()) return NO_ERROR; - LOGW("Attempt to use locked camera (%p) from different process", getCameraClient()->asBinder().get()); + int callingPid = getCallingPid(); + if (mClientPid == callingPid) return NO_ERROR; + LOGW("Attempt to use locked camera (client %p) from different process " + " (old pid %d, new pid %d)", + getCameraClient()->asBinder().get(), mClientPid, callingPid); return -EBUSY; } status_t CameraService::Client::lock() { + int callingPid = getCallingPid(); + LOGD("lock from pid %d (mClientPid %d)", callingPid, mClientPid); Mutex::Autolock _l(mLock); // lock camera to this client if the the camera is unlocked if (mClientPid == 0) { - mClientPid = IPCThreadState::self()->getCallingPid(); + mClientPid = callingPid; return NO_ERROR; } // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise @@ -213,13 +260,14 @@ status_t CameraService::Client::lock() status_t CameraService::Client::unlock() { + int callingPid = getCallingPid(); + LOGD("unlock from pid %d (mClientPid %d)", callingPid, mClientPid); Mutex::Autolock _l(mLock); // allow anyone to use camera - LOGV("unlock (%p)", getCameraClient()->asBinder().get()); status_t result = checkPid(); if (result == NO_ERROR) { mClientPid = 0; - + LOGD("clear mCameraClient (pid %d)", callingPid); // we need to remove the reference so that when app goes // away, the reference count goes to 0. mCameraClient.clear(); @@ -229,15 +277,17 @@ status_t CameraService::Client::unlock() status_t CameraService::Client::connect(const sp<ICameraClient>& client) { + int callingPid = getCallingPid(); + // connect a new process to the camera - LOGV("connect (%p)", client->asBinder().get()); + LOGD("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get()); // I hate this hack, but things get really ugly when the media recorder // service is handing back the camera to the app. The ICameraClient // destructor will be called during the same IPC, making it look like // the remote client is trying to disconnect. This hack temporarily // sets the mClientPid to an invalid pid to prevent the hardware from - // being torn down. + // being torn down. { // hold a reference to the old client or we will deadlock if the client is @@ -245,25 +295,30 @@ status_t CameraService::Client::connect(const sp<ICameraClient>& client) sp<ICameraClient> oldClient; { Mutex::Autolock _l(mLock); - if (mClientPid != 0) { - LOGW("Tried to connect to locked camera"); + if (mClientPid != 0 && checkPid() != NO_ERROR) { + LOGW("Tried to connect to locked camera (old pid %d, new pid %d)", + mClientPid, callingPid); return -EBUSY; } oldClient = mCameraClient; // did the client actually change? - if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR; + if (client->asBinder() == mCameraClient->asBinder()) { + LOGD("Connect to the same client"); + return NO_ERROR; + } mCameraClient = client; mClientPid = -1; mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; - LOGV("connect new process (%d) to existing camera client", mClientPid); + LOGD("Connect to the new client (pid %d, client %p)", + callingPid, mCameraClient->asBinder().get()); } } // the old client destructor is called when oldClient goes out of scope // now we set the new PID to lock the interface again - mClientPid = IPCThreadState::self()->getCallingPid(); + mClientPid = callingPid; return NO_ERROR; } @@ -280,8 +335,11 @@ static void *unregister_surface(void *arg) CameraService::Client::~Client() { + int callingPid = getCallingPid(); + // tear down client - LOGD("Client (%p) E destructor", getCameraClient()->asBinder().get()); + LOGD("Client::~Client E (pid %d, client %p)", + callingPid, getCameraClient()->asBinder().get()); if (mSurface != 0 && !mUseOverlay) { #if HAVE_ANDROID_OS pthread_t thr; @@ -307,49 +365,59 @@ CameraService::Client::~Client() } // make sure we tear down the hardware - mClientPid = IPCThreadState::self()->getCallingPid(); + mClientPid = callingPid; disconnect(); - LOGD("Client X destructor"); + LOGD("Client::~Client X (pid %d)", mClientPid); } void CameraService::Client::disconnect() { - LOGD("Client (%p) E disconnect from (%d)", - getCameraClient()->asBinder().get(), - IPCThreadState::self()->getCallingPid()); + int callingPid = getCallingPid(); + + LOGD("Client::disconnect() E (pid %d client %p)", + callingPid, getCameraClient()->asBinder().get()); + Mutex::Autolock lock(mLock); if (mClientPid <= 0) { - LOGV("camera is unlocked, don't tear down hardware"); + LOGD("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); return; } if (checkPid() != NO_ERROR) { - LOGV("Different client - don't disconnect"); + LOGD("Different client - don't disconnect"); return; } - mCameraService->removeClient(mCameraClient); - if (mHardware != 0) { - LOGV("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(); - LOGD("Client X disconnect"); + + mCameraService->removeClient(mCameraClient); + mCameraService->decUsers(); + + LOGD("Client::disconnect() X (pid %d)", callingPid); } // pass the buffered ISurface to the camera service status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) { - LOGD("setPreviewDisplay(%p)", surface.get()); + LOGD("setPreviewDisplay(%p) (pid %d)", + ((surface == NULL) ? NULL : surface.get()), getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); if (result != NO_ERROR) return result; + Mutex::Autolock surfaceLock(mSurfaceLock); + result = NO_ERROR; // asBinder() is safe on NULL (returns NULL) if (surface->asBinder() != mSurface->asBinder()) { if (mSurface != 0 && !mUseOverlay) { @@ -357,24 +425,35 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) mSurface->unregisterBuffers(); } mSurface = surface; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); + } + } } - return NO_ERROR; + return result; } // set the preview callback flag to affect how the received frames from // preview are handled. void CameraService::Client::setPreviewCallbackFlag(int callback_flag) { - LOGV("setPreviewCallbackFlag"); + LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; mPreviewCallbackFlag = callback_flag; } -// start preview mode, must call setPreviewDisplay first +// start preview mode status_t CameraService::Client::startCameraMode(camera_mode mode) { - LOGD("startCameraMode(%d)", mode); + int callingPid = getCallingPid(); + + LOGD("startCameraMode(%d) (pid %d)", mode, callingPid); /* we cannot call into mHardware with mLock held because * mHardware has callbacks onto us which acquire this lock @@ -389,23 +468,25 @@ status_t CameraService::Client::startCameraMode(camera_mode mode) return INVALID_OPERATION; } - if (mSurface == 0) { - LOGE("setPreviewDisplay must be called before startCameraMode!"); - return INVALID_OPERATION; - } - switch(mode) { case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startRecordingMode."); + return INVALID_OPERATION; + } return startRecordingMode(); default: // CAMERA_PREVIEW_MODE + if (mSurface == 0) { + LOGD("mSurface is not set yet."); + } return startPreviewMode(); } } status_t CameraService::Client::startRecordingMode() { - LOGV("startRecordingMode"); + LOGD("startRecordingMode (pid %d)", getCallingPid()); status_t ret = UNKNOWN_ERROR; @@ -431,9 +512,65 @@ status_t CameraService::Client::startRecordingMode() return ret; } +status_t CameraService::Client::setOverlay() +{ + LOGD("setOverlay"); + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + const char *format = params.getPreviewFormat(); + int fmt; + if (!strcmp(format, "yuv422i")) + fmt = OVERLAY_FORMAT_YCbCr_422_I; + else if (!strcmp(format, "rgb565")) + fmt = OVERLAY_FORMAT_RGB_565; + else { + LOGE("Invalid preview format for overlays"); + return -EINVAL; + } + + status_t ret = NO_ERROR; + if (mSurface != 0) { + sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt); + ret = mHardware->setOverlay(new Overlay(ref)); + } else { + ret = mHardware->setOverlay(NULL); + } + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + } + return ret; +} + +status_t CameraService::Client::registerPreviewBuffers() +{ + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + uint32_t transform = 0; + if (params.getOrientation() == + CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, + transform, + 0, + mHardware->getPreviewHeap()); + + status_t ret = mSurface->registerBuffers(buffers); + if (ret != NO_ERROR) { + LOGE("registerBuffers failed with status %d", ret); + } + return ret; +} + status_t CameraService::Client::startPreviewMode() { - LOGV("startPreviewMode"); + LOGD("startPreviewMode (pid %d)", getCallingPid()); // if preview has been enabled, nothing needs to be done if (mHardware->previewEnabled()) { @@ -444,55 +581,24 @@ status_t CameraService::Client::startPreviewMode() #if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE debug_frame_cnt = 0; #endif - status_t ret = UNKNOWN_ERROR; - int w, h; - CameraParameters params(mHardware->getParameters()); - params.getPreviewSize(&w, &h); + status_t ret = NO_ERROR; if (mUseOverlay) { - const char *format = params.getPreviewFormat(); - int fmt; - LOGD("Use Overlays"); - if (!strcmp(format, "yuv422i")) - fmt = OVERLAY_FORMAT_YCbCr_422_I; - else if (!strcmp(format, "rgb565")) - fmt = OVERLAY_FORMAT_RGB_565; - else { - LOGE("Invalid preview format for overlays"); - return -EINVAL; - } - sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt); - ret = mHardware->setOverlay(new Overlay(ref)); - if (ret != NO_ERROR) { - LOGE("mHardware->setOverlay() failed with status %d\n", ret); - return ret; + // If preview display has been set, set overlay now. + if (mSurface != 0) { + ret = setOverlay(); } + if (ret != NO_ERROR) return ret; ret = mHardware->startPreview(NULL, mCameraService.get()); - if (ret != NO_ERROR) - LOGE("mHardware->startPreview() failed with status %d\n", ret); - } else { ret = mHardware->startPreview(previewCallback, mCameraService.get()); - if (ret == NO_ERROR) { - - mSurface->unregisterBuffers(); - - uint32_t transform = 0; - if (params.getOrientation() == - CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { - LOGV("portrait mode"); - transform = ISurface::BufferHeap::ROT_90; - } - ISurface::BufferHeap buffers(w, h, w, h, - PIXEL_FORMAT_YCbCr_420_SP, - transform, - 0, - mHardware->getPreviewHeap()); - - mSurface->registerBuffers(buffers); - } else { - LOGE("mHardware->startPreview() failed with status %d", ret); + if (ret != NO_ERROR) return ret; + // If preview display has been set, register preview buffers now. + if (mSurface != 0) { + // Unregister here because the surface registered with raw heap. + mSurface->unregisterBuffers(); + ret = registerPreviewBuffers(); } } return ret; @@ -500,11 +606,15 @@ status_t CameraService::Client::startPreviewMode() status_t CameraService::Client::startPreview() { + LOGD("startPreview (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_PREVIEW_MODE); } status_t CameraService::Client::startRecording() { + LOGD("startRecording (pid %d)", getCallingPid()); + if (mMediaPlayerBeep.get() != NULL) { mMediaPlayerBeep->seekTo(0); mMediaPlayerBeep->start(); @@ -515,7 +625,7 @@ status_t CameraService::Client::startRecording() // stop preview mode void CameraService::Client::stopPreview() { - LOGD("stopPreview()"); + LOGD("stopPreview (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; @@ -537,7 +647,7 @@ void CameraService::Client::stopPreview() // stop recording mode void CameraService::Client::stopRecording() { - LOGV("stopRecording()"); + LOGD("stopRecording (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; @@ -552,15 +662,13 @@ void CameraService::Client::stopRecording() mMediaPlayerBeep->start(); } mHardware->stopRecording(); - LOGV("stopRecording(), hardware stopped OK"); + LOGD("stopRecording(), hardware stopped OK"); mPreviewBuffer.clear(); } // release a recording frame void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) { - LOGV("releaseRecordingFrame()"); - Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; @@ -592,7 +700,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) { @@ -704,7 +812,7 @@ void CameraService::Client::recordingCallback(const sp<IMemory>& mem, void* user // take a picture - image is returned in callback status_t CameraService::Client::autoFocus() { - LOGV("autoFocus"); + LOGD("autoFocus (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); @@ -722,7 +830,7 @@ status_t CameraService::Client::autoFocus() // take a picture - image is returned in callback status_t CameraService::Client::takePicture() { - LOGD("takePicture"); + LOGD("takePicture (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); @@ -920,6 +1028,7 @@ void CameraService::Client::postAutoFocus(bool focused) void CameraService::Client::postShutter() { + LOGD("postShutter"); mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); } @@ -1029,12 +1138,12 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) if (checkCallingPermission(String16("android.permission.DUMP")) == false) { snprintf(buffer, SIZE, "Permission Denial: " "can't dump CameraService from pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), + getCallingPid(), IPCThreadState::self()->getCallingUid()); 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", @@ -1052,8 +1161,6 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) } -#if DEBUG_HEAP_LEAKS - #define CHECK_INTERFACE(interface, data, reply) \ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ LOGW("Call incorrectly routed to " #interface); \ @@ -1085,6 +1192,7 @@ status_t CameraService::onTransact( status_t err = BnCameraService::onTransact(code, data, reply, flags); +#if DEBUG_HEAP_LEAKS LOGD("+++ onTransact err %d code %d", err, code); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { @@ -1120,9 +1228,9 @@ status_t CameraService::onTransact( break; } } +#endif // DEBUG_HEAP_LEAKS + return err; } -#endif // DEBUG_HEAP_LEAKS - }; // namespace android diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index 6752f265dabb..0f0767394021 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -58,10 +58,8 @@ public: void removeClient(const sp<ICameraClient>& cameraClient); -#if DEBUG_HEAP_LEAKS virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); -#endif private: @@ -159,6 +157,8 @@ private: status_t startCameraMode(camera_mode mode); status_t startPreviewMode(); status_t startRecordingMode(); + status_t setOverlay(); + status_t registerPreviewBuffers(); // Ensures atomicity among the public methods mutable Mutex mLock; @@ -196,7 +196,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/am b/cmds/am/am index a5b13f9eb5ae..c82363498242 100755 --- a/cmds/am/am +++ b/cmds/am/am @@ -3,5 +3,5 @@ # base=/system export CLASSPATH=$base/framework/am.jar -exec app_process $base/bin com.android.commands.am.Am $* +exec app_process $base/bin com.android.commands.am.Am "$@" diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 6d4b4552bbc0..3782136f5c73 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; @@ -194,18 +197,17 @@ public class Am { if (intent != null) { System.out.println("Starting: " + intent); try { - intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // XXX should do something to determine the MIME type. int res = mAm.startActivity(null, intent, intent.getType(), null, 0, null, null, 0, false, mDebugOption); switch (res) { case IActivityManager.START_SUCCESS: break; - case IActivityManager.START_CLASS_NOT_FOUND: - System.err.println("Error type 3"); - System.err.println("Error: Activity class " + - intent.getComponent().toShortString() - + " does not exist."); + case IActivityManager.START_SWITCHES_CANCELED: + System.err.println( + "Warning: Activity not started because the " + + " current activity is being kept for the user."); break; case IActivityManager.START_DELIVERED_TO_TOP: System.err.println( @@ -213,25 +215,36 @@ public class Am { + "been delivered to currently running " + "top-most instance."); break; - case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: + case IActivityManager.START_RETURN_INTENT_TO_CALLER: System.err.println( - "Error: Activity not started, you requested to " - + "both forward and receive its result"); + "Warning: Activity not started because intent " + + "should be handled by the caller"); + break; + case IActivityManager.START_TASK_TO_FRONT: + System.err.println( + "Warning: Activity not started, its current " + + "task has been brought to the front"); break; case IActivityManager.START_INTENT_NOT_RESOLVED: System.err.println( "Error: Activity not started, unable to " + "resolve " + intent.toString()); break; - case IActivityManager.START_RETURN_INTENT_TO_CALLER: + case IActivityManager.START_CLASS_NOT_FOUND: + System.err.println("Error type 3"); + System.err.println("Error: Activity class " + + intent.getComponent().toShortString() + + " does not exist."); + break; + case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: System.err.println( - "Warning: Activity not started because intent " - + "should be handled by the caller"); + "Error: Activity not started, you requested to " + + "both forward and receive its result"); break; - case IActivityManager.START_TASK_TO_FRONT: + case IActivityManager.START_PERMISSION_DENIED: System.err.println( - "Warning: Activity not started, its current " - + "task has been brought to the front"); + "Error: Activity not started, you do not " + + "have permission to access it."); break; default: System.err.println( @@ -436,6 +449,8 @@ public class Am { return; } + ParcelFileDescriptor fd = null; + String cmd = nextArg(); if ("start".equals(cmd)) { start = true; @@ -445,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(); @@ -452,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) { @@ -516,7 +541,7 @@ public class Am { private void showUsage() { System.err.println("usage: am [start|broadcast|instrument|profile]"); - System.err.println(" am start -D INTENT"); + System.err.println(" am start [-D] INTENT"); System.err.println(" am broadcast INTENT"); System.err.println(" am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>]"); System.err.println(" [-w] <COMPONENT> "); diff --git a/cmds/backup/Android.mk b/cmds/backup/Android.mk new file mode 100644 index 000000000000..508aec073570 --- /dev/null +++ b/cmds/backup/Android.mk @@ -0,0 +1,15 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= backup.cpp + +LOCAL_SHARED_LIBRARIES := libcutils libutils + +LOCAL_MODULE:= btool + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/cmds/backup/NOTICE b/cmds/backup/NOTICE new file mode 100644 index 000000000000..c5b1efa7aac7 --- /dev/null +++ b/cmds/backup/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp new file mode 100644 index 000000000000..d4e669b5d9e8 --- /dev/null +++ b/cmds/backup/backup.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/BackupHelpers.h> +#include <utils/String8.h> + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> + +using namespace android; + +#include <unistd.h> + +int +usage(int argc, const char** argv) +{ + const char* p = argv[0]; + + fprintf(stderr, "%s: Backs up your data.\n" + "\n" + "usage: %s\n" + " Prints all of the data that can be backed up to stdout.\n" + "\n" + "usage: %s list FILE\n" + " Lists the backup entities in the file.\n" + "\n" + "usage: %s print NAME FILE\n" + " Prints the entity named NAME in FILE.\n", + p, p, p, p); + return 1; +} + +int +perform_full_backup() +{ + printf("this would have written all of your data to stdout\n"); + return 0; +} + +int +perform_list(const char* filename) +{ + int err; + int fd; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Error opening: %s\n", filename); + return 1; + } + + BackupDataReader reader(fd); + bool done; + int type; + + while (reader.ReadNextHeader(&done, &type) == 0) { + if (done) { + break; + } + switch (type) { + case BACKUP_HEADER_ENTITY_V1: + { + String8 key; + size_t dataSize; + err = reader.ReadEntityHeader(&key, &dataSize); + if (err == 0) { + printf(" entity: %s (%d bytes)\n", key.string(), dataSize); + } else { + printf(" Error reading entity header\n"); + } + break; + } + default: + { + printf("Unknown chunk type: 0x%08x\n", type); + break; + } + } + } + + return 0; +} + +int perform_print(const char* entityname, const char* filename) +{ + printf("perform_print(%s, %s);", entityname, filename); + return 0; +} + +int +main(int argc, const char** argv) +{ + if (argc <= 1) { + return perform_full_backup(); + } + if (argc == 3 && 0 == strcmp(argv[1], "list")) { + return perform_list(argv[2]); + } + if (argc == 4 && 0 == strcmp(argv[1], "print")) { + return perform_print(argv[2], argv[3]); + } + return usage(argc, argv); +} + diff --git a/cmds/bmgr/Android.mk b/cmds/bmgr/Android.mk new file mode 100644 index 000000000000..8a1670b2ccff --- /dev/null +++ b/cmds/bmgr/Android.mk @@ -0,0 +1,15 @@ +# Copyright 2007 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := bmgr +include $(BUILD_JAVA_LIBRARY) + + +include $(CLEAR_VARS) +ALL_PREBUILT += $(TARGET_OUT)/bin/bmgr +$(TARGET_OUT)/bin/bmgr : $(LOCAL_PATH)/bmgr | $(ACP) + $(transform-prebuilt-to-target) + diff --git a/cmds/bmgr/MODULE_LICENSE_APACHE2 b/cmds/bmgr/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/cmds/bmgr/MODULE_LICENSE_APACHE2 diff --git a/cmds/bmgr/NOTICE b/cmds/bmgr/NOTICE new file mode 100644 index 000000000000..64aaa8dbd68e --- /dev/null +++ b/cmds/bmgr/NOTICE @@ -0,0 +1,190 @@ + + 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. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr new file mode 100755 index 000000000000..6b4bbe2d9032 --- /dev/null +++ b/cmds/bmgr/bmgr @@ -0,0 +1,7 @@ +# Script to start "bmgr" on the device, which has a very rudimentary +# shell. +# +base=/system +export CLASSPATH=$base/framework/bmgr.jar +exec app_process $base/bin com.android.commands.bmgr.Bmgr "$@" + diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java new file mode 100644 index 000000000000..ee3ec1aa18fb --- /dev/null +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -0,0 +1,403 @@ +/* + * 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.commands.bmgr; + +import android.backup.IBackupManager; +import android.backup.IRestoreObserver; +import android.backup.IRestoreSession; +import android.backup.RestoreSet; +import android.os.RemoteException; +import android.os.ServiceManager; + +public final class Bmgr { + IBackupManager mBmgr; + IRestoreSession mRestore; + + static final String BMGR_NOT_RUNNING_ERR = + "Error: Could not access the Backup Manager. Is the system running?"; + static final String TRANSPORT_NOT_RUNNING_ERR = + "Error: Could not access the backup transport. Is the system running?"; + + private String[] mArgs; + private int mNextArg; + private String mCurArgData; + + public static void main(String[] args) { + try { + new Bmgr().run(args); + } catch (Exception e) { + System.err.println("Exception caught:"); + e.printStackTrace(); + } + } + + public void run(String[] args) { + boolean validCommand = false; + if (args.length < 1) { + showUsage(); + return; + } + + mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup")); + if (mBmgr == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + + mArgs = args; + String op = args[0]; + mNextArg = 1; + + if ("enabled".equals(op)) { + doEnabled(); + return; + } + + if ("enable".equals(op)) { + doEnable(); + return; + } + + if ("run".equals(op)) { + doRun(); + return; + } + + if ("backup".equals(op)) { + doBackup(); + return; + } + + if ("list".equals(op)) { + doList(); + return; + } + + if ("restore".equals(op)) { + doRestore(); + return; + } + + if ("transport".equals(op)) { + doTransport(); + return; + } + + if ("wipe".equals(op)) { + doWipe(); + return; + } + + System.err.println("Unknown command"); + showUsage(); + } + + private String enableToString(boolean enabled) { + return enabled ? "enabled" : "disabled"; + } + + private void doEnabled() { + try { + boolean isEnabled = mBmgr.isBackupEnabled(); + System.out.println("Backup Manager currently " + + enableToString(isEnabled)); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doEnable() { + String arg = nextArg(); + if (arg == null) { + showUsage(); + return; + } + + try { + boolean enable = Boolean.parseBoolean(arg); + mBmgr.setBackupEnabled(enable); + System.out.println("Backup Manager now " + enableToString(enable)); + } catch (NumberFormatException e) { + showUsage(); + return; + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doRun() { + try { + mBmgr.backupNow(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doBackup() { + boolean isFull = false; + String pkg = nextArg(); + if ("-f".equals(pkg)) { + isFull = true; + pkg = nextArg(); + } + + if (pkg == null || pkg.startsWith("-")) { + showUsage(); + return; + } + + try { + // !!! TODO: handle full backup + mBmgr.dataChanged(pkg); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doTransport() { + try { + String which = nextArg(); + String old = mBmgr.selectBackupTransport(which); + if (old == null) { + System.out.println("Unknown transport '" + which + + "' specified; no changes made."); + } else { + System.out.println("Selected transport " + which + " (formerly " + old + ")"); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doWipe() { + String pkg = nextArg(); + if (pkg == null) { + showUsage(); + return; + } + + try { + mBmgr.clearBackupData(pkg); + System.out.println("Wiped backup data for " + pkg); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doList() { + String arg = nextArg(); // sets, transports, packages set# + if ("transports".equals(arg)) { + doListTransports(); + return; + } + + // The rest of the 'list' options work with a restore session on the current transport + try { + String curTransport = mBmgr.getCurrentTransport(); + mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + + if ("sets".equals(arg)) { + doListRestoreSets(); + } else if ("transports".equals(arg)) { + doListTransports(); + } + + mRestore.endRestoreSession(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doListTransports() { + try { + String current = mBmgr.getCurrentTransport(); + String[] transports = mBmgr.listAllTransports(); + if (transports == null || transports.length == 0) { + System.out.println("No transports available."); + return; + } + + for (String t : transports) { + String pad = (t.equals(current)) ? " * " : " "; + System.out.println(pad + t); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doListRestoreSets() { + try { + RestoreSet[] sets = mRestore.getAvailableRestoreSets(); + if (sets == null || sets.length == 0) { + System.out.println("No restore sets available"); + } else { + printRestoreSets(sets); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(TRANSPORT_NOT_RUNNING_ERR); + } + } + + 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() { + long token; + try { + token = Long.parseLong(nextArg()); + } catch (NumberFormatException e) { + showUsage(); + return; + } + + RestoreObserver observer = new RestoreObserver(); + + try { + boolean didRestore = false; + String curTransport = mBmgr.getCurrentTransport(); + mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + RestoreSet[] sets = mRestore.getAvailableRestoreSets(); + for (RestoreSet s : sets) { + if (s.token == token) { + System.out.println("Scheduling restore: " + s.name); + 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() { + if (mNextArg >= mArgs.length) { + return null; + } + String arg = mArgs[mNextArg]; + mNextArg++; + return arg; + } + + private static void showUsage() { + System.err.println("usage: bmgr [backup|restore|list|transport|run]"); + System.err.println(" bmgr backup PACKAGE"); + System.err.println(" bmgr enable BOOL"); + System.err.println(" bmgr enabled"); + System.err.println(" bmgr list transports"); + System.err.println(" bmgr list sets"); + System.err.println(" bmgr transport WHICH"); + System.err.println(" bmgr restore TOKEN"); + System.err.println(" bmgr run"); + System.err.println(" bmgr wipe PACKAGE"); + System.err.println(""); + System.err.println("The 'backup' command schedules a backup pass for the named package."); + System.err.println("Note that the backup pass will effectively be a no-op if the package"); + System.err.println("does not actually have changed data to store."); + System.err.println(""); + System.err.println("The 'enable' command enables or disables the entire backup mechanism."); + System.err.println("If the argument is 'true' it will be enabled, otherwise it will be"); + System.err.println("disabled. When disabled, neither backup or restore operations will"); + System.err.println("be performed."); + System.err.println(""); + System.err.println("The 'enabled' command reports the current enabled/disabled state of"); + System.err.println("the backup mechanism."); + System.err.println(""); + System.err.println("The 'list transports' command reports the names of the backup transports"); + System.err.println("currently available on the device. These names can be passed as arguments"); + System.err.println("to the 'transport' command. The currently selected transport is indicated"); + System.err.println("with a '*' character."); + System.err.println(""); + System.err.println("The 'list sets' command reports the token and name of each restore set"); + System.err.println("available to the device via the current transport."); + System.err.println(""); + System.err.println("The 'transport' command designates the named transport as the currently"); + System.err.println("active one. This setting is persistent across reboots."); + System.err.println(""); + System.err.println("The 'restore' command initiates a restore operation, using the restore set"); + System.err.println("from the current transport whose token matches the argument."); + System.err.println(""); + System.err.println("The 'run' command causes any scheduled backup operation to be initiated"); + System.err.println("immediately, without the usual waiting period for batching together"); + System.err.println("data changes."); + System.err.println(""); + System.err.println("The 'wipe' command causes all backed-up data for the given package to be"); + System.err.println("erased from the current transport's storage. The next backup operation"); + System.err.println("that the given application performs will rewrite its entire data set."); + } +} diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk new file mode 100644 index 000000000000..9c94c2ef3cf9 --- /dev/null +++ b/cmds/bootanimation/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + bootanimation_main.cpp \ + BootAnimation.cpp + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libcorecg \ + libsgl \ + libEGL \ + libGLESv1_CM + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) + +LOCAL_MODULE:= bootanimation + + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index db403857323a..3b9db8db496f 100644 --- a/libs/surfaceflinger/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -22,6 +22,7 @@ #include <fcntl.h> #include <utils/misc.h> +#include <utils/IPCThreadState.h> #include <utils/threads.h> #include <utils/Atomic.h> #include <utils/Errors.h> @@ -49,10 +50,9 @@ namespace android { // --------------------------------------------------------------------------- -BootAnimation::BootAnimation(const sp<ISurfaceComposer>& composer) : - Thread(false) { - mSession = SurfaceComposerClient::clientForConnection( - composer->createConnection()->asBinder()); +BootAnimation::BootAnimation() : Thread(false) +{ + mSession = new SurfaceComposerClient(); } BootAnimation::~BootAnimation() { @@ -131,7 +131,7 @@ status_t BootAnimation::readyToRun() { // create the native surface sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, - PIXEL_FORMAT_RGB_565); + PIXEL_FORMAT_RGB_565, ISurfaceComposer::eGPU); session()->openTransaction(); s->setLayer(0x40000000); session()->closeTransaction(); @@ -144,7 +144,10 @@ status_t BootAnimation::readyToRun() { EGLConfig config; EGLSurface surface; EGLContext context; + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglInitialize(display, 0, 0); eglChooseConfig(display, attribs, &config, 1, &numConfigs); mNativeWindowSurface = new EGLNativeWindowSurface(s); @@ -170,17 +173,15 @@ status_t BootAnimation::readyToRun() { return NO_ERROR; } -void BootAnimation::requestExit() { - mBarrier.open(); - Thread::requestExit(); -} - bool BootAnimation::threadLoop() { bool r = android(); eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); mNativeWindowSurface.clear(); + mFlingerSurface.clear(); + eglTerminate(mDisplay); + IPCThreadState::self()->stopProcess(); return r; } @@ -227,8 +228,10 @@ bool BootAnimation::android() { glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); - eglSwapBuffers(mDisplay, mSurface); - + EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); + if (res == EGL_FALSE) + break; + // 12fps: don't animate too fast to preserve CPU const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); if (sleepTime > 0) diff --git a/libs/surfaceflinger/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 3fb6670b3ef4..42e9eed7d3eb 100644 --- a/libs/surfaceflinger/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -29,8 +29,6 @@ #include <EGL/egl.h> #include <GLES/gl.h> -#include "Barrier.h" - class SkBitmap; namespace android { @@ -43,11 +41,10 @@ class EGLNativeWindowSurface; class BootAnimation : public Thread { public: - BootAnimation(const sp<ISurfaceComposer>& composer); + BootAnimation(); virtual ~BootAnimation(); const sp<SurfaceComposerClient>& session() const; - virtual void requestExit(); private: virtual bool threadLoop(); @@ -73,7 +70,6 @@ private: EGLDisplay mSurface; sp<Surface> mFlingerSurface; sp<EGLNativeWindowSurface> mNativeWindowSurface; - Barrier mBarrier; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp new file mode 100644 index 000000000000..675ea8167a0e --- /dev/null +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BootAnimation" + +#include <utils/IPCThreadState.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/threads.h> + +#include <ui/ISurfaceComposer.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +# include <sys/resource.h> +#endif + +#include "BootAnimation.h" + +using namespace android; + +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); +#endif + + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create the boot animation object + sp<BootAnimation> boot = new BootAnimation(); + + IPCThreadState::self()->joinThreadPool(); + return 0; +} diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index eabf98e8d0e0..cc951c1bcbd1 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -165,6 +165,7 @@ int main(int argc, char *argv[]) { int c, fd, vibrate_fd, fds[2]; char path[PATH_MAX]; pid_t pid; + gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); @@ -207,7 +208,7 @@ int main(int argc, char *argv[]) { vibrate_fd = -1; /* switch to non-root user and group */ - setgid(AID_LOG); + setgroups(sizeof(groups)/sizeof(groups[0]), groups); setuid(AID_SHELL); /* make it safe to use both printf and STDOUT_FILENO */ diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk new file mode 100644 index 000000000000..3daf44e40a00 --- /dev/null +++ b/cmds/keystore/Android.mk @@ -0,0 +1,22 @@ +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + netkeystore.c keymgmt.c + +LOCAL_C_INCLUDES := \ + $(call include-path-for, system-core)/cutils \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := \ + libcutils libssl + +LOCAL_STATIC_LIBRARIES := + +LOCAL_MODULE:= keystore + +include $(BUILD_EXECUTABLE) + +endif # !simulator)) diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h new file mode 100644 index 000000000000..aefad668d160 --- /dev/null +++ b/cmds/keystore/certtool.h @@ -0,0 +1,91 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __CERTTOOL_H__ +#define __CERTTOOL_H__ + +#include <stdio.h> +#include <string.h> +#include <cutils/sockets.h> +#include <cutils/log.h> + +#include "common.h" +#include "netkeystore.h" + +#define CERT_NAME_LEN (2 * MAX_KEY_NAME_LENGTH + 2) + +/* + * The specific function 'get_cert' is used in daemons to get the key value + * from keystore. Caller should allocate the buffer and the length of the buffer + * should be MAX_KEY_VALUE_LENGTH. + */ +static inline int get_cert(const char *certname, unsigned char *value, int *size) +{ + int count, fd, ret = -1; + LPC_MARSHAL cmd; + char delimiter[] = "_"; + char *namespace, *keyname; + char *context = NULL; + char cname[CERT_NAME_LEN]; + + if ((certname == NULL) || (value == NULL)) { + LOGE("get_cert: certname or value is null\n"); + return -1; + } + + if (strlcpy(cname, certname, CERT_NAME_LEN) >= CERT_NAME_LEN) { + LOGE("get_cert: keyname is too long\n"); + return -1; + } + + fd = socket_local_client(SOCKET_PATH, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd == -1) { + LOGE("Keystore service is not up and running.\n"); + return -1; + } + + cmd.opcode = GET; + if (((namespace = strtok_r(cname, delimiter, &context)) == NULL) || + ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) { + goto err; + } + if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname)) + > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err; + + if (write_marshal(fd, &cmd)) { + LOGE("Incorrect command or command line is too long.\n"); + goto err; + } + if (read_marshal(fd, &cmd)) { + LOGE("Failed to read the result.\n"); + goto err; + } + + // copy the result if succeeded. + if (!cmd.retcode && cmd.len <= BUFFER_MAX) { + memcpy(value, cmd.data, cmd.len); + ret = 0; + *size = cmd.len; + } +err: + close(fd); + return ret; +} + +#endif diff --git a/cmds/keystore/common.h b/cmds/keystore/common.h new file mode 100644 index 000000000000..a18114e91abc --- /dev/null +++ b/cmds/keystore/common.h @@ -0,0 +1,60 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#define SOCKET_PATH "keystore" +#define KEYSTORE_DIR "/data/misc/keystore/" + +#define READ_TIMEOUT 3 +#define MAX_KEY_NAME_LENGTH 64 +#define MAX_NAMESPACE_LENGTH MAX_KEY_NAME_LENGTH +#define MAX_KEY_VALUE_LENGTH 4096 + +#define BUFFER_MAX MAX_KEY_VALUE_LENGTH + +typedef enum { + BOOTUP, + UNINITIALIZED, + LOCKED, + UNLOCKED, +} KEYSTORE_STATE; + +typedef enum { + LOCK, + UNLOCK, + PASSWD, + GETSTATE, + LISTKEYS, + GET, + PUT, + REMOVE, + RESET, + MAX_OPCODE +} KEYSTORE_OPCODE; + +typedef struct { + uint32_t len; + union { + uint32_t opcode; + uint32_t retcode; + }; + unsigned char data[BUFFER_MAX + 1]; +} LPC_MARSHAL; + +#endif diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c new file mode 100644 index 000000000000..66edd566166a --- /dev/null +++ b/cmds/keystore/keymgmt.c @@ -0,0 +1,372 @@ +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <ctype.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <openssl/aes.h> +#include <openssl/evp.h> +#include <cutils/log.h> + +#include "common.h" +#include "keymgmt.h" + +static int retry_count = 0; +static unsigned char iv[IV_LEN]; +static KEYSTORE_STATE state = BOOTUP; +static AES_KEY encryptKey, decryptKey; + +inline void unlock_keystore(unsigned char *master_key) +{ + AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey); + AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey); + memset(master_key, 0, sizeof(master_key)); + state = UNLOCKED; +} + +inline void lock_keystore() +{ + memset(&encryptKey, 0 , sizeof(AES_KEY)); + memset(&decryptKey, 0 , sizeof(AES_KEY)); + state = LOCKED; +} + +inline void get_encrypt_key(char *passwd, AES_KEY *key) +{ + unsigned char user_key[USER_KEY_LEN]; + gen_key(passwd, user_key, USER_KEY_LEN); + AES_set_encrypt_key(user_key, AES_KEY_LEN, key); +} + +inline void get_decrypt_key(char *passwd, AES_KEY *key) +{ + unsigned char user_key[USER_KEY_LEN]; + gen_key(passwd, user_key, USER_KEY_LEN); + AES_set_decrypt_key(user_key, AES_KEY_LEN, key); +} + +static int gen_random_blob(unsigned char *key, int size) +{ + int ret = 0; + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) return -1; + if (read(fd, key, size) != size) ret = -1; + close(fd); + return ret; +} + +static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob, + const char *keyfile) +{ + int size, fd, ret = -1; + unsigned char enc_blob[MAX_BLOB_LEN]; + + char tmpfile[KEYFILE_LEN]; + strcpy(tmpfile, keyfile); + strcat(tmpfile, ".tmp"); + + // prepare the blob + memcpy(blob->iv, iv, IV_LEN); + blob->blob_size = get_blob_size(blob); + memcpy(enc_blob, blob->blob, blob->blob_size); + AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob, + blob->blob_size, enc_key, iv, AES_ENCRYPT); + // write to keyfile + size = data_blob_size(blob); + if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1; + if (write(fd, blob, size) == size) ret = 0; + close(fd); + if (!ret) { + unlink(keyfile); + rename(tmpfile, keyfile); + chmod(keyfile, 0440); + } + return ret; +} + +static int load_n_decrypt(const char *keyname, const char *keyfile, + AES_KEY *key, DATA_BLOB *blob) +{ + int fd, ret = -1; + if ((fd = open(keyfile, O_RDONLY)) == -1) return -1; + // get the encrypted blob and iv + if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) || + (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) || + (blob->blob_size > MAX_BLOB_LEN)) { + goto err; + } else { + unsigned char enc_blob[MAX_BLOB_LEN]; + if (read(fd, enc_blob, blob->blob_size) != + (int) blob->blob_size) goto err; + // decrypt the blob + AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob, + blob->blob_size, key, blob->iv, AES_DECRYPT); + if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0; + } +err: + close(fd); + return ret; +} + +static int store_master_key(char *upasswd, unsigned char *master_key) +{ + AES_KEY key; + DATA_BLOB blob; + + // prepare the blob + strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN); + blob.value_size = USER_KEY_LEN; + memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN); + + // generate the encryption key + get_encrypt_key(upasswd, &key); + return encrypt_n_save(&key, &blob, MASTER_KEY); +} + +static int get_master_key(char *upasswd, unsigned char *master_key) +{ + AES_KEY key; + int size, ret = 0; + DATA_BLOB blob; + + get_decrypt_key(upasswd, &key); + ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob); + if (!ret) memcpy(master_key, blob.value, blob.value_size); + return ret; +} + +static int create_master_key(char *upasswd) +{ + int ret; + unsigned char mpasswd[AES_KEY_LEN]; + unsigned char master_key[USER_KEY_LEN]; + + gen_random_blob(mpasswd, AES_KEY_LEN); + gen_key((char*)mpasswd, master_key, USER_KEY_LEN); + if ((ret = store_master_key(upasswd, master_key)) == 0) { + unlock_keystore(master_key); + } + memset(master_key, 0, USER_KEY_LEN); + memset(mpasswd, 0, AES_KEY_LEN); + + return ret; +} + +static int change_passwd(char *data) +{ + unsigned char master_key[USER_KEY_LEN]; + char *old_pass, *new_pass = NULL, *p, *delimiter=" "; + int ret, count = 0; + char *context = NULL; + + old_pass = p = strtok_r(data, delimiter, &context); + while (p != NULL) { + count++; + new_pass = p; + p = strtok_r(NULL, delimiter, &context); + } + if (count != 2) return -1; + if (strlen(new_pass) < MIN_PASSWD_LENGTH) return -1; + if ((ret = get_master_key(old_pass, master_key)) == 0) { + ret = store_master_key(new_pass, master_key); + retry_count = 0; + } else { + ret = MAX_RETRY_COUNT - ++retry_count; + if (ret == 0) { + retry_count = 0; + LOGE("passwd:reach max retry count, reset the keystore now."); + reset_keystore(); + return -1; + } + + } + return ret; +} + +int remove_key(const char *namespace, const char *keyname) +{ + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) return -state; + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + return unlink(keyfile); +} + +int put_key(const char *namespace, const char *keyname, + unsigned char *data, int size) +{ + DATA_BLOB blob; + uint32_t real_size; + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) { + LOGE("Can not store key with current state %d\n", state); + return -state; + } + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + // flatten the args + strcpy(blob.keyname, keyname); + blob.value_size = size; + memcpy(blob.value, data, size); + return encrypt_n_save(&encryptKey, &blob, keyfile); +} + +int get_key(const char *namespace, const char *keyname, + unsigned char *data, int *size) +{ + int ret; + DATA_BLOB blob; + uint32_t blob_size; + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) { + LOGE("Can not retrieve key value with current state %d\n", state); + return -state; + } + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob); + if (!ret) { + if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) { + ret = -1; + } else { + *size = blob.value_size; + memcpy(data, blob.value, *size); + } + } + return ret; +} + +int list_keys(const char *namespace, char reply[BUFFER_MAX]) +{ + DIR *d; + struct dirent *de; + + if (state != UNLOCKED) { + LOGE("Can not list key with current state %d\n", state); + return -1; + } + + if (!namespace || ((d = opendir("."))) == NULL) { + LOGE("cannot open keystore dir or namespace is null\n"); + return -1; + } + while ((de = readdir(d))) { + char *prefix, *name, *keyfile = de->d_name; + char *context = NULL; + + if (de->d_type != DT_REG) continue; + if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context)) + == NULL) continue; + if (strcmp(prefix, namespace)) continue; + if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue; + // append the key name into reply + if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX); + if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) { + LOGE("too many files under keystore directory\n"); + return -1; + } + } + closedir(d); + return 0; +} + +int passwd(char *data) +{ + if (state == UNINITIALIZED) { + if (strchr(data, ' ')) return -1; + if (strlen(data) < MIN_PASSWD_LENGTH) return -1; + return create_master_key(data); + } + return change_passwd(data); +} + +int lock() +{ + switch(state) { + case UNLOCKED: + lock_keystore(); + case LOCKED: + return 0; + default: + return -1; + } +} + +int unlock(char *passwd) +{ + unsigned char master_key[USER_KEY_LEN]; + int ret = get_master_key(passwd, master_key); + if (!ret) { + unlock_keystore(master_key); + retry_count = 0; + } else { + ret = MAX_RETRY_COUNT - ++retry_count; + if (ret == 0) { + retry_count = 0; + LOGE("unlock:reach max retry count, reset the keystore now."); + reset_keystore(); + return -1; + } + } + return ret; +} + +KEYSTORE_STATE get_state() +{ + return state; +} + +int reset_keystore() +{ + DIR *d; + struct dirent *de; + + if ((d = opendir(".")) == NULL) { + LOGE("cannot open keystore dir\n"); + return -1; + } + while ((de = readdir(d))) unlink(de->d_name); + closedir(d); + state = UNINITIALIZED; + LOGI("keystore is reset."); + return 0; +} + +int init_keystore(const char *dir) +{ + int fd; + + if (!dir) mkdir(dir, 0770); + if (!dir || chdir(dir)) { + LOGE("Can not open/create the keystore directory %s\n", + dir ? dir : "(null)"); + return -1; + } + gen_random_blob(iv, IV_LEN); + if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) { + state = UNINITIALIZED; + return 0; + } + close(fd); + state = LOCKED; + return 0; +} diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h new file mode 100644 index 000000000000..0e928db494fd --- /dev/null +++ b/cmds/keystore/keymgmt.h @@ -0,0 +1,82 @@ +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __KEYMGMT_H__ +#define __KEYMGMT_H__ + +#define MASTER_KEY_TAG "master_key" +#define MASTER_KEY ".keymaster" +#define MAX_PATH_LEN 128 +#define SALT "Android Keystore 0.1" +#define NAME_DELIMITER "_" +#define KEYFILE_NAME "%s"NAME_DELIMITER"%s" +#define KEYGEN_ITER 1024 +#define AES_KEY_LEN 128 +#define USER_KEY_LEN (AES_KEY_LEN/8) +#define IV_LEN USER_KEY_LEN +#define MAX_RETRY_COUNT 6 +#define MIN_PASSWD_LENGTH 8 + +#define gen_key(passwd, key, len) \ + PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), \ + (unsigned char*)SALT, \ + strlen(SALT), KEYGEN_ITER, \ + len, key) + +#define KEYFILE_LEN MAX_NAMESPACE_LENGTH + MAX_KEY_NAME_LENGTH + 6 + +#define get_blob_size(blob) \ + (((blob->value_size + sizeof(uint32_t) + MAX_KEY_NAME_LENGTH \ + + USER_KEY_LEN - 1) / USER_KEY_LEN) * USER_KEY_LEN) + +#define MAX_BLOB_LEN ((MAX_KEY_VALUE_LENGTH + MAX_KEY_NAME_LENGTH + \ + sizeof(uint32_t) + USER_KEY_LEN - 1) / USER_KEY_LEN)\ + * USER_KEY_LEN + +#define data_blob_size(blob) USER_KEY_LEN + sizeof(uint32_t) + blob->blob_size + +typedef struct { + unsigned char iv[USER_KEY_LEN]; + uint32_t blob_size; + union { + unsigned char blob[1]; + struct { + uint32_t value_size; + char keyname[MAX_KEY_NAME_LENGTH]; + unsigned char value[MAX_KEY_VALUE_LENGTH]; + } __attribute__((packed)); + }; +} DATA_BLOB; + +typedef struct { + char tag[USER_KEY_LEN]; + unsigned char master_key[USER_KEY_LEN]; +} MASTER_BLOB; + +int put_key(const char *namespace, const char *keyname, + unsigned char *data, int size); +int get_key(const char *namespace, const char *keyname, + unsigned char *data, int *size); +int remove_key(const char *namespace, const char *keyname); +int list_keys(const char *namespace, char reply[BUFFER_MAX]); +int passwd(char *data); +int lock(); +int unlock(char *passwd); +KEYSTORE_STATE get_state(); +int reset_keystore(); +int init_keystore(const char *dir); + +#endif diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h new file mode 100644 index 000000000000..a7fd9a556af8 --- /dev/null +++ b/cmds/keystore/keystore_get.h @@ -0,0 +1,53 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __KEYSTORE_GET_H__ +#define __KEYSTORE_GET_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "certtool.h" + +/* This function is provided to native components to get values from keystore. + * Users are required to link against libcutils. If something goes wrong, NULL + * is returned. Otherwise it returns the value in dynamically allocated memory + * and sets the size if the pointer is not NULL. One can release the memory by + * calling free(). */ +static char *keystore_get(char *key, int *size) +{ + char buffer[MAX_KEY_VALUE_LENGTH]; + char *value; + int length; + + if (get_cert(key, (unsigned char *)buffer, &length) != 0) { + return NULL; + } + value = malloc(length + 1); + if (!value) { + return NULL; + } + memcpy(value, buffer, length); + value[length] = 0; + if (size) { + *size = length; + } + return value; +} + +#endif diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c new file mode 100644 index 000000000000..eac455e0c544 --- /dev/null +++ b/cmds/keystore/netkeystore.c @@ -0,0 +1,410 @@ +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "keystore" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <utime.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <private/android_filesystem_config.h> + +#include <cutils/sockets.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "netkeystore.h" +#include "keymgmt.h" + +#define CMD_PUT_WITH_FILE "putfile" + +typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply); + +struct cmdinfo { + const char *name; + CMD_FUNC *func; +}; + +static CMD_FUNC do_lock; +static CMD_FUNC do_unlock; +static CMD_FUNC do_passwd; +static CMD_FUNC do_get_state;; +static CMD_FUNC do_listkeys; +static CMD_FUNC do_get_key; +static CMD_FUNC do_put_key; +static CMD_FUNC do_remove_key; +static CMD_FUNC do_reset_keystore; + +#define str(x) #x + +struct cmdinfo cmds[] = { + { str(LOCK), do_lock }, + { str(UNLOCK), do_unlock }, + { str(PASSWD), do_passwd }, + { str(GETSTATE), do_get_state }, + { str(LISTKEYS), do_listkeys }, + { str(GET), do_get_key }, + { str(PUT), do_put_key }, + { str(REMOVE), do_remove_key }, + { str(RESET), do_reset_keystore }, +}; + +static struct ucred cr; + +static int check_get_perm(int uid) +{ + if (uid == AID_WIFI || uid == AID_VPN) return 0; + return -1; +} + +static int check_reset_perm(int uid) +{ + if (uid == AID_SYSTEM) return 0; + return -1; +} + +static int parse_keyname(char *name, uint32_t len, + char *namespace, char *keyname) +{ + int count = 0; + char *c = namespace, *p = namespace, *t = name; + + if (!name || !namespace || !keyname) return -1; + while (t < name + len && (*t != 0)) { + if (*t == ' ') { + if (c == keyname) return -1; + *p = count = 0; + c = p = keyname; + t++; + } else { + if (!isalnum(*t)) return -1; + *p++ = *t++; + // also check if the keyname/namespace is too long. + if (count++ == MAX_KEY_NAME_LENGTH) return -1; + } + } + *p = 0; + return 0; +} + +// args of passwd(): +// firstPassword - for the first time +// oldPassword newPassword - for changing the password +static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = passwd((char*)cmd->data); +} + +// args of lock(): +// no argument +static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = lock(); +} + +// args of unlock(): +// password +static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = unlock((char*)cmd->data); +} + +// args of get_state(): +// no argument +static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = get_state(); +} + +// args of listkeys(): +// namespace +static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data); + if (!reply->retcode) reply->len = strlen((char*)reply->data); +} + +// args of get(): +// namespace keyname +static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + if (check_get_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to get key value\n", cr.uid); + reply->retcode = -1; + return; + } + + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + } else { + reply->retcode = get_key(namespace, keyname, reply->data, + (int*)&reply->len); + } +} + +static int get_value_index(LPC_MARSHAL *cmd) +{ + uint32_t count = 0, i; + for (i = 0 ; i < cmd->len ; ++i) { + if (cmd->data[i] == ' ') { + if (++count == 2) return ++i; + } + } + return -1; +} + +// args of put(): +// namespace keyname keyvalue +static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + int p = get_value_index(cmd); + if (p == -1) { + reply->retcode = -1; + } else { + unsigned char *value; + if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) { + reply->retcode = -1; + return; + } + value = &cmd->data[p]; + int len = cmd->len - p; + reply->retcode = put_key(namespace, keyname, value, len); + } +} + +// args of remove_key(): +// namespace keyname +static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + return; + } + reply->retcode = remove_key(namespace, keyname); +} + +// args of reset_keystore(): +// no argument +static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + if (check_reset_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to reset the keystore\n", + cr.uid); + reply->retcode = -1; + return; + } + reply->retcode = reset_keystore(); +} + +static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo); + + if (cmd->opcode >= cmd_max) { + LOGE("the opcode (%d) is not valid", cmd->opcode); + reply->retcode = -1; + return; + } + cmds[cmd->opcode].func(cmd, reply); +} + +static int set_read_timeout(int socket) +{ + struct timeval tv; + tv.tv_sec = READ_TIMEOUT; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) + { + LOGE("setsockopt failed"); + return -1; + } + return 0; +} + +static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd) +{ + int fd, len, ret = 0; + + // get opcode of the function put() + if ((fd = open(filename, O_RDONLY)) == -1) { + fprintf(stderr, "Can not open file %s\n", filename); + return -1; + } + cmd->data[cmd->len] = ' '; + cmd->len++; + len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len); + if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) { + ret = -1; + } else { + cmd->len += len; + } + close(fd); + return ret; +} + +static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + int i, len = 0; + char *buf = (char*)cmd->data; + buf[0] = 0; + for (i = 0 ; i < argc ; ++i) { + if (i == 0) { + len = strlcpy(buf, argv[i], BUFFER_MAX); + } else { + len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]); + } + if (len >= BUFFER_MAX) return -1; + } + if (len) cmd->len = len; + return 0; +} + +static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + uint32_t i, len = 0; + uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]); + + for (i = 0 ; i < cmd_max ; ++i) { + if (!strcasecmp(argv[0], cmds[i].name)) break; + } + + if (i == cmd_max) { + // check if this is a command to put the key value with a file. + if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1; + cmd->opcode = PUT; + if (argc != 4) { + fprintf(stderr, "%s args\n\tnamespace keyname filename\n", + argv[0]); + return -1; + } + if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1; + return append_input_from_file(argv[3], cmd); + } else { + cmd->opcode = i; + return flatten_str_args(argc - 1, argv + 1, cmd); + } +} + +static int shell_command(const int argc, const char **argv) +{ + int fd, i; + LPC_MARSHAL cmd; + + if (parse_cmd(argc, argv , &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + fd = socket_local_client(SOCKET_PATH, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd == -1) { + fprintf(stderr, "Keystore service is not up and running.\n"); + exit(1); + } + + if (write_marshal(fd, &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + if (read_marshal(fd, &cmd)) { + fprintf(stderr, "Failed to read the result.\n"); + exit(1); + } + cmd.data[cmd.len] = 0; + fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!"); + if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data); + close(fd); + return 0; +} + +int main(const int argc, const char *argv[]) +{ + struct sockaddr addr; + socklen_t alen; + int lsocket, s; + LPC_MARSHAL cmd, reply; + + if (argc > 1) { + return shell_command(argc - 1, argv + 1); + } + + if (init_keystore(KEYSTORE_DIR)) { + LOGE("Can not initialize the keystore, the directory exist?\n"); + exit(1); + } + + lsocket = android_get_control_socket(SOCKET_PATH); + if (lsocket < 0) { + LOGE("Failed to get socket from environment: %s\n", strerror(errno)); + exit(1); + } + if (listen(lsocket, 5)) { + LOGE("Listen on socket failed: %s\n", strerror(errno)); + exit(1); + } + fcntl(lsocket, F_SETFD, FD_CLOEXEC); + memset(&reply, 0, sizeof(LPC_MARSHAL)); + + for (;;) { + socklen_t cr_size = sizeof(cr); + alen = sizeof(addr); + s = accept(lsocket, &addr, &alen); + + /* retrieve the caller info here */ + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { + close(s); + LOGE("Unable to recieve socket options\n"); + continue; + } + + if (s < 0) { + LOGE("Accept failed: %s\n", strerror(errno)); + continue; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + if (set_read_timeout(s)) { + close(s); + continue; + } + + // read the command, execute and send the result back. + if(read_marshal(s, &cmd)) goto err; + LOGI("new connection\n"); + execute(&cmd, &reply); + write_marshal(s, &reply); +err: + memset(&reply, 0, sizeof(LPC_MARSHAL)); + LOGI("closing connection\n"); + close(s); + } + + return 0; +} diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h new file mode 100644 index 000000000000..a87a667e9123 --- /dev/null +++ b/cmds/keystore/netkeystore.h @@ -0,0 +1,96 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __NETKEYSTORE_H__ +#define __NETKEYSTORE_H__ + +#include <stdio.h> +#include <cutils/sockets.h> +#include <cutils/log.h> + +#include "common.h" + +static inline int readx(int s, void *_buf, int count) +{ + char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = read(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("read error: %s\n", strerror(errno)); + return -1; + } + if (r == 0) { + LOGE("eof\n"); + return -1; /* EOF */ + } + n += r; + } + return 0; +} + +static inline int writex(int s, const void *_buf, int count) +{ + const char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = write(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("write error: %s\n", strerror(errno)); + return -1; + } + n += r; + } + return 0; +} + +static inline int read_marshal(int s, LPC_MARSHAL *cmd) +{ + if (readx(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to read header\n"); + return -1; + } + if (cmd->len > BUFFER_MAX) { + LOGE("invalid size %d\n", cmd->len); + return -1; + } + if (readx(s, cmd->data, cmd->len)) { + LOGE("failed to read data\n"); + return -1; + } + cmd->data[cmd->len] = 0; + return 0; +} + +static inline int write_marshal(int s, LPC_MARSHAL *cmd) +{ + if (writex(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to write marshal header\n"); + return -1; + } + if (writex(s, cmd->data, cmd->len)) { + LOGE("failed to write marshal data\n"); + return -1; + } + return 0; +} + +#endif diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 8212b9212af8..fd9e70884e68 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -543,6 +543,9 @@ public final class Pm { case PackageManager.INSTALL_FAILED_TEST_ONLY: s = "INSTALL_FAILED_TEST_ONLY"; break; + case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: + s = "INSTALL_FAILED_CPU_ABI_INCOMPATIBLE"; + break; case PackageManager.INSTALL_PARSE_FAILED_NOT_APK: s = "INSTALL_PARSE_FAILED_NOT_APK"; break; diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp index 1531a9efd154..476f38a4d073 100644 --- a/cmds/runtime/main_runtime.cpp +++ b/cmds/runtime/main_runtime.cpp @@ -45,9 +45,9 @@ static const char* ZYGOTE_ARGV[] = { "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & - * CAP_SYS_BOOT + * CAP_SYS_BOOT CAP_SYS_NICE */ - "--capabilities=88161312,88161312", + "--capabilities=96549920,96549920", "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer" diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java new file mode 100644 index 000000000000..a3456c756404 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -0,0 +1,225 @@ +/* + * 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.accessibilityservice; + +import com.android.internal.os.HandlerCaller; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; + +/** + * An accessibility service runs in the background and receives callbacks by the system + * when {@link AccessibilityEvent}s are fired. Such events denote some state transition + * in the user interface, for example, the focus has changed, a button has been clicked, + * etc. + * <p> + * An accessibility service extends this class and implements its abstract methods. Such + * a service is declared as any other service in an AndroidManifest.xml but it must also + * specify that it handles the "android.accessibilityservice.AccessibilityService" + * {@link android.content.Intent}. Following is an example of such a declaration: + * <p> + * <code> + * <service android:name=".MyAccessibilityService"><br> + * <intent-filter><br> + * <action android:name="android.accessibilityservice.AccessibilityService" /><br> + * </intent-filter><br> + * </service><br> + * </code> + * <p> + * The lifecycle of an accessibility service is managed exclusively by the system. Starting + * or stopping an accessibility service is triggered by an explicit user action through + * enabling or disabling it in the device settings. After the system binds to a service it + * calls {@link AccessibilityService#onServiceConnected()}. This method can be + * overriden by clients that want to perform post binding setup. An accessibility service + * is configured though setting an {@link AccessibilityServiceInfo} by calling + * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. You can call this + * method any time to change the service configuration but it is good practice to do that + * in the overriden {@link AccessibilityService#onServiceConnected()}. + * <p> + * An accessibility service can be registered for events in specific packages to provide a + * specific type of feedback and is notified with a certain timeout after the last event + * of interest has been fired. + * <p> + * <b>Notification strategy</b> + * <p> + * For each feedback type only one accessibility service is notified. Services are notified + * in the order of registration. Hence, if two services are registered for the same + * feedback type in the same package the first one wins. It is possible however, to + * register a service as the default one for a given feedback type. In such a case this + * service is invoked if no other service was interested in the event. In other words, default + * services do not compete with other services and are notified last regardless of the + * registration order. This enables "generic" accessibility services that work reasonably + * well with most applications to coexist with "polished" ones that are targeted for + * specific applications. + * <p> + * <b>Event types</b> + * <p> + * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} + * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} + * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED} + * {@link AccessibilityEvent#TYPE_VIEW_SELECTED} + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} + * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} + * <p> + * <b>Feedback types</b> + * <p> + * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} + * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} + * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} + * {@link AccessibilityServiceInfo#FEEDBACK_VISUAL} + * {@link AccessibilityServiceInfo#FEEDBACK_GENERIC} + * + * @see AccessibilityEvent + * @see AccessibilityServiceInfo + * @see android.view.accessibility.AccessibilityManager + * + * Note: The event notification timeout is useful to avoid propagating events to the client + * too frequently since this is accomplished via an expensive interprocess call. + * One can think of the timeout as a criteria to determine when event generation has + * settled down. + */ +public abstract class AccessibilityService extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + */ + public static final String SERVICE_INTERFACE = + "android.accessibilityservice.AccessibilityService"; + + private static final String LOG_TAG = "AccessibilityService"; + + private AccessibilityServiceInfo mInfo; + + IAccessibilityServiceConnection mConnection; + + /** + * Callback for {@link android.view.accessibility.AccessibilityEvent}s. + * + * @param event An event. + */ + public abstract void onAccessibilityEvent(AccessibilityEvent event); + + /** + * Callback for interrupting the accessibility feedback. + */ + public abstract void onInterrupt(); + + /** + * This method is a part of the {@link AccessibilityService} lifecycle and is + * called after the system has successfully bound to the service. If is + * convenient to use this method for setting the {@link AccessibilityServiceInfo}. + * + * @see AccessibilityServiceInfo + * @see #setServiceInfo(AccessibilityServiceInfo) + */ + protected void onServiceConnected() { + + } + + /** + * Sets the {@link AccessibilityServiceInfo} that describes this service. + * <p> + * Note: You can call this method any time but the info will be picked up after + * the system has bound to this service and when this method is called thereafter. + * + * @param info The info. + */ + public final void setServiceInfo(AccessibilityServiceInfo info) { + mInfo = info; + sendServiceInfo(); + } + + /** + * Sets the {@link AccessibilityServiceInfo} for this service if the latter is + * properly set and there is an {@link IAccessibilityServiceConnection} to the + * AccessibilityManagerService. + */ + private void sendServiceInfo() { + if (mInfo != null && mConnection != null) { + try { + mConnection.setServiceInfo(mInfo); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); + } + } + } + + @Override + public final IBinder onBind(Intent intent) { + return new IEventListenerWrapper(this); + } + + /** + * Implements the internal {@link IEventListener} interface to convert + * incoming calls to it back to calls on an {@link AccessibilityService}. + */ + class IEventListenerWrapper extends IEventListener.Stub + implements HandlerCaller.Callback { + + private static final int DO_SET_SET_CONNECTION = 10; + private static final int DO_ON_INTERRUPT = 20; + private static final int DO_ON_ACCESSIBILITY_EVENT = 30; + + private final HandlerCaller mCaller; + + private AccessibilityService mTarget; + + public IEventListenerWrapper(AccessibilityService context) { + mTarget = context; + mCaller = new HandlerCaller(context, this); + } + + public void setConnection(IAccessibilityServiceConnection connection) { + Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection); + mCaller.sendMessage(message); + } + + public void onInterrupt() { + Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); + mCaller.sendMessage(message); + } + + public void onAccessibilityEvent(AccessibilityEvent event) { + Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); + mCaller.sendMessage(message); + } + + public void executeMessage(Message message) { + switch (message.what) { + case DO_ON_ACCESSIBILITY_EVENT : + AccessibilityEvent event = (AccessibilityEvent) message.obj; + mTarget.onAccessibilityEvent(event); + event.recycle(); + return; + case DO_ON_INTERRUPT : + mTarget.onInterrupt(); + return; + case DO_SET_SET_CONNECTION : + mConnection = ((IAccessibilityServiceConnection) message.obj); + mTarget.onServiceConnected(); + return; + default : + Log.w(LOG_TAG, "Unknown message type " + message.what); + } + } + } +} diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl new file mode 100644 index 000000000000..1f5d3850d46f --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.accessibilityservice; + +parcelable AccessibilityServiceInfo; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java new file mode 100644 index 000000000000..4761f98ed7c2 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -0,0 +1,145 @@ +/* + * 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.accessibilityservice; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class describes an {@link AccessibilityService}. The system + * notifies an {@link AccessibilityService} for + * {@link android.view.accessibility.AccessibilityEvent}s + * according to the information encapsulated in this class. + * + * @see AccessibilityService + * @see android.view.accessibility.AccessibilityEvent + */ +public class AccessibilityServiceInfo implements Parcelable { + + /** + * Denotes spoken feedback. + */ + public static final int FEEDBACK_SPOKEN = 0x0000001; + + /** + * Denotes haptic feedback. + */ + public static final int FEEDBACK_HAPTIC = 0x0000002; + + /** + * Denotes audible (not spoken) feedback. + */ + public static final int FEEDBACK_AUDIBLE = 0x0000004; + + /** + * Denotes visual feedback. + */ + public static final int FEEDBACK_VISUAL = 0x0000008; + + /** + * Denotes generic feedback. + */ + public static final int FEEDBACK_GENERIC = 0x0000010; + + /** + * If an {@link AccessibilityService} is the default for a given type. + * Default service is invoked only if no package specific one exists. In case of + * more than one package specific service only the earlier registered is notified. + */ + public static final int DEFAULT = 0x0000001; + + /** + * The event types an {@link AccessibilityService} is interested in. + * + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_ACTIVITY_STARTED + * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED + */ + public int eventTypes; + + /** + * The package names an {@link AccessibilityService} is interested in. Setting + * to null is equivalent to all packages. + */ + public String[] packageNames; + + /** + * The feedback type an {@link AccessibilityService} provides. + * + * @see #FEEDBACK_AUDIBLE + * @see #FEEDBACK_GENERIC + * @see #FEEDBACK_HAPTIC + * @see #FEEDBACK_SPOKEN + * @see #FEEDBACK_VISUAL + */ + public int feedbackType; + + /** + * The timeout after the most recent event of a given type before an + * {@link AccessibilityService} is notified. + * <p> + * Note: The event notification timeout is useful to avoid propagating events to the client + * too frequently since this is accomplished via an expensive interprocess call. + * One can think of the timeout as a criteria to determine when event generation has + * settled down + */ + public long notificationTimeout; + + /** + * This field represents a set of flags used for configuring an + * {@link AccessibilityService}. + * + * @see #DEFAULT + */ + public int flags; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(eventTypes); + parcel.writeStringArray(packageNames); + parcel.writeInt(feedbackType); + parcel.writeLong(notificationTimeout); + parcel.writeInt(flags); + } + + /** + * @see Parcelable.Creator + */ + public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR = + new Parcelable.Creator<AccessibilityServiceInfo>() { + public AccessibilityServiceInfo createFromParcel(Parcel parcel) { + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = parcel.readInt(); + info.packageNames = parcel.readStringArray(); + info.feedbackType = parcel.readInt(); + info.notificationTimeout = parcel.readLong(); + info.flags = parcel.readInt(); + return info; + } + + public AccessibilityServiceInfo[] newArray(int size) { + return new AccessibilityServiceInfo[size]; + } + }; +} diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl new file mode 100644 index 000000000000..7157def606fa --- /dev/null +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -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.accessibilityservice; + +import android.accessibilityservice.AccessibilityServiceInfo; + +/** + * Interface AccessibilityManagerService#Service implements, and passes to an + * AccessibilityService so it can dynamically configure how the system handles it. + * + * @hide + */ +oneway interface IAccessibilityServiceConnection { + + void setServiceInfo(in AccessibilityServiceInfo info); +} diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IEventListener.aidl new file mode 100644 index 000000000000..5b849f1e40fd --- /dev/null +++ b/core/java/android/accessibilityservice/IEventListener.aidl @@ -0,0 +1,34 @@ +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.accessibilityservice; + +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.view.accessibility.AccessibilityEvent; + +/** + * Top-level interface to accessibility service component (implemented in Service). + * + * @hide + */ + oneway interface IEventListener { + + void setConnection(in IAccessibilityServiceConnection connection); + + void onAccessibilityEvent(in AccessibilityEvent event); + + void onInterrupt(); +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9b1f0f97171f..ca9632a490b1 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,11 +16,14 @@ package android.app; +import com.android.internal.policy.PolicyManager; + import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IIntentSender; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -32,11 +35,12 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.text.Selection; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.AttributeSet; import android.util.Config; @@ -58,10 +62,10 @@ import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnCreateContextMenuListener; +import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; -import com.android.internal.policy.PolicyManager; - import java.util.ArrayList; import java.util.HashMap; @@ -625,6 +629,8 @@ public class Activity extends ContextThemeWrapper boolean mStartedActivity; /*package*/ int mConfigChangeFlags; /*package*/ Configuration mCurrentConfig; + private SearchManager mSearchManager; + private Bundle mSearchDialogState = null; private Window mWindow; @@ -785,6 +791,9 @@ public class Activity extends ContextThemeWrapper protected void onCreate(Bundle savedInstanceState) { mVisibleFromClient = mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, true); + // uses super.getSystemService() since this.getSystemService() looks at the + // mSearchManager field. + mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); mCalled = true; } @@ -802,9 +811,10 @@ public class Activity extends ContextThemeWrapper // Also restore the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.restoreSearchDialog(savedInstanceState, SAVED_SEARCH_DIALOG_KEY); + Bundle searchState = savedInstanceState.getBundle(SAVED_SEARCH_DIALOG_KEY); + if (searchState != null) { + mSearchManager.restoreSearchDialog(searchState); + } } /** @@ -854,13 +864,26 @@ public class Activity extends ContextThemeWrapper final Integer dialogId = ids[i]; Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId)); if (dialogState != null) { - final Dialog dialog = onCreateDialog(dialogId); - dialog.onRestoreInstanceState(dialogState); + // Calling onRestoreInstanceState() below will invoke dispatchOnCreate + // so tell createDialog() not to do it, otherwise we get an exception + final Dialog dialog = createDialog(dialogId, false); mManagedDialogs.put(dialogId, dialog); + onPrepareDialog(dialogId, dialog); + dialog.onRestoreInstanceState(dialogState); } } } + private Dialog createDialog(Integer dialogId, boolean dispatchOnCreate) { + final Dialog dialog = onCreateDialog(dialogId); + if (dialog == null) { + throw new IllegalArgumentException("Activity#onCreateDialog did " + + "not create a dialog for id " + dialogId); + } + if (dispatchOnCreate) dialog.dispatchOnCreate(null); + return dialog; + } + private String savedDialogKeyFor(int key) { return SAVED_DIALOG_KEY_PREFIX + key; } @@ -1010,9 +1033,11 @@ public class Activity extends ContextThemeWrapper // Also save the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.saveSearchDialog(outState, SAVED_SEARCH_DIALOG_KEY); + // onPause() should always be called before this method, so mSearchManagerState + // should be up to date. + if (mSearchDialogState != null) { + outState.putBundle(SAVED_SEARCH_DIALOG_KEY, mSearchDialogState); + } } /** @@ -1283,12 +1308,6 @@ public class Activity extends ContextThemeWrapper } } } - - // also dismiss search dialog if showing - // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.stopSearch(); // close any cursors we are managing. int numCursors = mManagedCursors.size(); @@ -1298,6 +1317,10 @@ public class Activity extends ContextThemeWrapper c.mCursor.close(); } } + + // Clear any search state saved in performPause(). If the state may be needed in the + // future, it will have been saved by performSaveInstanceState() + mSearchDialogState = null; } /** @@ -1321,9 +1344,7 @@ public class Activity extends ContextThemeWrapper // also update search dialog if showing // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.onConfigurationChanged(newConfig); + mSearchManager.onConfigurationChanged(newConfig); if (mWindow != null) { // Pass the configuration changed event to the window @@ -2013,7 +2034,24 @@ public class Activity extends ContextThemeWrapper } return onTrackballEvent(ev); } - + + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(getPackageName()); + + LayoutParams params = getWindow().getAttributes(); + boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && + (params.height == LayoutParams.FILL_PARENT); + event.setFullScreen(isFullScreen); + + CharSequence title = getTitle(); + if (!TextUtils.isEmpty(title)) { + event.getText().add(title); + } + + return true; + } + /** * Default implementation of * {@link android.view.Window.Callback#onCreatePanelView} @@ -2394,12 +2432,7 @@ public class Activity extends ContextThemeWrapper } Dialog dialog = mManagedDialogs.get(id); if (dialog == null) { - dialog = onCreateDialog(id); - if (dialog == null) { - throw new IllegalArgumentException("Activity#onCreateDialog did " - + "not create a dialog for id " + id); - } - dialog.dispatchOnCreate(null); + dialog = createDialog(id, true); mManagedDialogs.put(id, dialog); } @@ -2523,10 +2556,7 @@ public class Activity extends ContextThemeWrapper */ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { - // activate the search manager and start it up! - SearchManager searchManager = (SearchManager) - getSystemService(Context.SEARCH_SERVICE); - searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), + mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), appSearchData, globalSearch); } @@ -3245,6 +3275,8 @@ public class Activity extends ContextThemeWrapper if (WINDOW_SERVICE.equals(name)) { return mWindowManager; + } else if (SEARCH_SERVICE.equals(name)) { + return mSearchManager; } return super.getSystemService(name); } @@ -3543,10 +3575,21 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } + + // restore search dialog, if any + if (mSearchDialogState != null) { + mSearchManager.restoreSearchDialog(mSearchDialogState); + } + mSearchDialogState = null; } final void performPause() { onPause(); + + // save search dialog state if the search dialog is open, + // and then dismiss the search dialog + mSearchDialogState = mSearchManager.saveSearchDialog(); + mSearchManager.stopSearch(); } final void performUserLeaving() { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 541f413676c8..dfa8139eecf4 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -17,9 +17,11 @@ package android.app; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.res.Configuration; @@ -984,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; @@ -998,6 +1002,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case STOP_APP_SWITCHES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + stopAppSwitches(); + reply.writeNoException(); + return true; + } + + case RESUME_APP_SWITCHES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + resumeAppSwitches(); + reply.writeNoException(); + return true; + } + case PEEK_SERVICE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Intent service = Intent.CREATOR.createFromParcel(data); @@ -1007,6 +1025,33 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeStrongBinder(binder); return true; } + + case START_BACKUP_AGENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + int backupRestoreMode = data.readInt(); + boolean success = bindBackupAgent(info, backupRestoreMode); + reply.writeNoException(); + reply.writeInt(success ? 1 : 0); + return true; + } + + case BACKUP_AGENT_CREATED_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + IBinder agent = data.readStrongBinder(); + backupAgentCreated(packageName, agent); + reply.writeNoException(); + return true; + } + + case UNBIND_BACKUP_AGENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + unbindBackupAgent(info); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1667,6 +1712,43 @@ class ActivityManagerProxy implements IActivityManager return binder; } + public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + app.writeToParcel(data, 0); + data.writeInt(backupRestoreMode); + mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0); + reply.readException(); + boolean success = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return success; + } + + public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + data.writeStrongBinder(agent); + mRemote.transact(BACKUP_AGENT_CREATED_TRANSACTION, data, reply, 0); + reply.recycle(); + data.recycle(); + } + + public void unbindBackupAgent(ApplicationInfo app) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + app.writeToParcel(data, 0); + mRemote.transact(UNBIND_BACKUP_AGENT_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException { @@ -2152,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(); @@ -2160,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; @@ -2182,5 +2270,25 @@ class ActivityManagerProxy implements IActivityManager return res; } + public void stopAppSwitches() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(STOP_APP_SWITCHES_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + + public void resumeAppSwitches() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(RESUME_APP_SWITCHES_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1e15d14271d3..5ee29ac4f473 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -23,6 +23,7 @@ import android.content.ContentProvider; import android.content.Context; import android.content.IContentProvider; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -31,6 +32,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; +import android.content.pm.PackageParser.Component; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,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; @@ -72,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; @@ -115,6 +119,7 @@ public final class ActivityThread { private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; + private static final boolean DEBUG_BACKUP = true; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; @@ -161,7 +166,7 @@ public final class ActivityThread { return metrics; } - Resources getTopLevelResources(String appDir, float applicationScale) { + Resources getTopLevelResources(String appDir, PackageInfo pkgInfo) { synchronized (mPackages) { //Log.w(TAG, "getTopLevelResources: " + appDir); WeakReference<Resources> wr = mActiveResources.get(appDir); @@ -180,23 +185,17 @@ public final class ActivityThread { if (assets.addAssetPath(appDir) == 0) { return null; } - DisplayMetrics metrics = getDisplayMetricsLocked(false); - // density used to load resources - // scaledDensity is calculated in Resources constructor - // - boolean usePreloaded = true; - - // TODO: use explicit flag to indicate the compatibility mode. - if (applicationScale != 1.0f) { - usePreloaded = false; - DisplayMetrics newMetrics = new DisplayMetrics(); - newMetrics.setTo(metrics); - float newDensity = metrics.density / applicationScale; - newMetrics.updateDensity(newDensity); - metrics = newMetrics; + ApplicationInfo appInfo; + try { + appInfo = getPackageManager().getApplicationInfo( + pkgInfo.getPackageName(), + PackageManager.GET_SUPPORTS_DENSITIES); + } catch (RemoteException e) { + throw new AssertionError(e); } //Log.i(TAG, "Resource:" + appDir + ", display metrics=" + metrics); - r = new Resources(assets, metrics, getConfiguration(), usePreloaded); + DisplayMetrics metrics = getDisplayMetricsLocked(false); + r = new Resources(assets, metrics, getConfiguration(), appInfo); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away mActiveResources.put(appDir, new WeakReference<Resources>(r)); @@ -224,7 +223,6 @@ public final class ActivityThread { private Resources mResources; private ClassLoader mClassLoader; private Application mApplication; - private float mApplicationScale; private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers = new HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>>(); @@ -267,8 +265,6 @@ public final class ActivityThread { mClassLoader = mSystemContext.getClassLoader(); mResources = mSystemContext.getResources(); } - - mApplicationScale = -1.0f; } public PackageInfo(ActivityThread activityThread, String name, @@ -287,56 +283,20 @@ public final class ActivityThread { mIncludeCode = true; mClassLoader = systemContext.getClassLoader(); mResources = systemContext.getResources(); - mApplicationScale = systemContext.getApplicationScale(); } public String getPackageName() { return mPackageName; } + public ApplicationInfo getApplicationInfo() { + return mApplicationInfo; + } + public boolean isSecurityViolation() { return mSecurityViolation; } - public float getApplicationScale() { - if (mApplicationScale > 0.0f) { - return mApplicationScale; - } - DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false); - // Find out the density scale (relative to 160) of the supported density that - // is closest to the system's density. - try { - ApplicationInfo ai = getPackageManager().getApplicationInfo( - mPackageName, PackageManager.GET_SUPPORTS_DENSITIES); - - float appScale = -1.0f; - if (ai.supportsDensities != null) { - int minDiff = Integer.MAX_VALUE; - for (int density : ai.supportsDensities) { - int tmpDiff = (int) Math.abs(DisplayMetrics.DEVICE_DENSITY - density); - if (tmpDiff == 0) { - appScale = 1.0f; - break; - } - // prefer higher density (appScale>1.0), unless that's only option. - if (tmpDiff < minDiff && appScale < 1.0f) { - appScale = DisplayMetrics.DEVICE_DENSITY / density; - minDiff = tmpDiff; - } - } - } - if (appScale < 0.0f) { - mApplicationScale = metrics.density; - } else { - mApplicationScale = appScale; - } - } catch (RemoteException e) { - throw new AssertionError(e); - } - if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName); - return mApplicationScale; - } - /** * Gets the array of shared libraries that are listed as * used by the given package. @@ -494,12 +454,12 @@ public final class ActivityThread { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale()); + mResources = mainThread.getTopLevelResources(mResDir, this); } return mResources; } - public Application makeApplication() { + public Application makeApplication(boolean forceDefaultAppClass) { if (mApplication != null) { return mApplication; } @@ -507,7 +467,7 @@ public final class ActivityThread { Application app = null; String appClass = mApplicationInfo.className; - if (appClass == null) { + if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } @@ -1199,6 +1159,16 @@ public final class ActivityThread { } } + private static final class CreateBackupAgentData { + ApplicationInfo appInfo; + int backupMode; + public String toString() { + return "CreateBackupAgentData{appInfo=" + appInfo + + " backupAgent=" + appInfo.backupAgentName + + " mode=" + backupMode + "}"; + } + } + private static final class CreateServiceData { IBinder token; ServiceInfo info; @@ -1239,6 +1209,7 @@ public final class ActivityThread { Bundle instrumentationArgs; IInstrumentationWatcher instrumentationWatcher; int debugMode; + boolean restrictedBackupMode; Configuration config; boolean handlingProfiling; public String toString() { @@ -1267,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"; @@ -1374,6 +1350,21 @@ public final class ActivityThread { queueOrSendMessage(H.RECEIVER, r); } + public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + d.backupMode = backupMode; + + queueOrSendMessage(H.CREATE_BACKUP_AGENT, d); + } + + public final void scheduleDestroyBackupAgent(ApplicationInfo app) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + + queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d); + } + public final void scheduleCreateService(IBinder token, ServiceInfo info) { CreateServiceData s = new CreateServiceData(); @@ -1419,7 +1410,7 @@ public final class ActivityThread { ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, String profileFile, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, - int debugMode, Configuration config, + int debugMode, boolean isRestrictedBackupMode, Configuration config, Map<String, IBinder> services) { Process.setArgV0(processName); @@ -1437,6 +1428,7 @@ public final class ActivityThread { data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.debugMode = debugMode; + data.restrictedBackupMode = isRestrictedBackupMode; data.config = config; queueOrSendMessage(H.BIND_APPLICATION, data); } @@ -1509,10 +1501,25 @@ 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) { + // Note: do this immediately, since going into the foreground + // should happen regardless of what pending work we have to do + // and the activity manager will wait for us to report back that + // we are done before sending us to the background. + try { + Process.setProcessGroup(Process.myPid(), group); + } catch (Exception e) { + Log.w(TAG, "Failed setting process group to " + group, e); + } } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; @@ -1706,6 +1713,8 @@ public final class ActivityThread { public static final int ACTIVITY_CONFIGURATION_CHANGED = 125; public static final int RELAUNCH_ACTIVITY = 126; public static final int PROFILER_CONTROL = 127; + public static final int CREATE_BACKUP_AGENT = 128; + public static final int DESTROY_BACKUP_AGENT = 129; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -1737,6 +1746,8 @@ public final class ActivityThread { case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED"; case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; case PROFILER_CONTROL: return "PROFILER_CONTROL"; + case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; + case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; } } return "(unknown)"; @@ -1837,7 +1848,13 @@ 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); + break; + case DESTROY_BACKUP_AGENT: + handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); break; } } @@ -1896,6 +1913,8 @@ public final class ActivityThread { Application mInitialApplication; final ArrayList<Application> mAllApplications = new ArrayList<Application>(); + // set of instantiated backup agents, keyed by package name + final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>(); static final ThreadLocal sThreadLocal = new ThreadLocal(); Instrumentation mInstrumentation; String mInstrumentationAppDir = null; @@ -2079,6 +2098,10 @@ public final class ActivityThread { return mInitialApplication; } + public String getProcessName() { + return mBoundApplication.processName; + } + public ApplicationContext getSystemContext() { synchronized (this) { if (mSystemContext == null) { @@ -2257,7 +2280,7 @@ public final class ActivityThread { } try { - Application app = r.packageInfo.makeApplication(); + Application app = r.packageInfo.makeApplication(false); if (localLOGV) Log.v(TAG, "Performing launch of " + r); if (localLOGV) Log.v( @@ -2452,7 +2475,7 @@ public final class ActivityThread { } try { - Application app = packageInfo.makeApplication(); + Application app = packageInfo.makeApplication(false); if (localLOGV) Log.v( TAG, "Performing receive of " + data.intent @@ -2495,6 +2518,85 @@ public final class ActivityThread { } } + // Instantiate a BackupAgent and tell it that it's alive + private final void handleCreateBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Log.v(TAG, "handleCreateBackupAgent: " + data); + + // no longer idle; we have backup work to do + unscheduleGcIdler(); + + // instantiate the BackupAgent class named in the manifest + PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + if (mBackupAgents.get(packageName) != null) { + Log.d(TAG, "BackupAgent " + " for " + packageName + + " already exists"); + return; + } + + BackupAgent agent = null; + String classname = data.appInfo.backupAgentName; + if (classname == null) { + if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) { + Log.e(TAG, "Attempted incremental backup but no defined agent for " + + packageName); + return; + } + classname = "android.app.FullBackupAgent"; + } + try { + java.lang.ClassLoader cl = packageInfo.getClassLoader(); + agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance(); + } catch (Exception e) { + throw new RuntimeException("Unable to instantiate backup agent " + + data.appInfo.backupAgentName + ": " + e.toString(), e); + } + + // set up the agent's context + try { + if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent " + + data.appInfo.backupAgentName); + + ApplicationContext context = new ApplicationContext(); + context.init(packageInfo, null, this); + context.setOuterContext(agent); + agent.attach(context); + agent.onCreate(); + + // tell the OS that we're live now + IBinder binder = agent.onBind(); + try { + ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder); + } catch (RemoteException e) { + // nothing to do. + } + mBackupAgents.put(packageName, agent); + } catch (Exception e) { + throw new RuntimeException("Unable to create BackupAgent " + + data.appInfo.backupAgentName + ": " + e.toString(), e); + } + } + + // Tear down a BackupAgent + private final void handleDestroyBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data); + + PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + BackupAgent agent = mBackupAgents.get(packageName); + if (agent != null) { + try { + agent.onDestroy(); + } catch (Exception e) { + Log.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo); + e.printStackTrace(); + } + mBackupAgents.remove(packageName); + } else { + Log.w(TAG, "Attempt to destroy unknown backup agent " + data); + } + } + private final void handleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. @@ -2520,7 +2622,7 @@ public final class ActivityThread { ApplicationContext context = new ApplicationContext(); context.init(packageInfo, null, this); - Application app = packageInfo.makeApplication(); + Application app = packageInfo.makeApplication(false); context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); @@ -3134,7 +3236,7 @@ public final class ActivityThread { r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { @@ -3143,7 +3245,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to pause activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3158,7 +3260,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to stop activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3183,7 +3285,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain child activities " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3194,7 +3296,7 @@ public final class ActivityThread { r.activity.onDestroy(); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onDestroy()"); } if (r.window != null) { @@ -3205,8 +3307,7 @@ public final class ActivityThread { } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( - "Unable to destroy activity " - + r.intent.getComponent().toShortString() + "Unable to destroy activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3216,6 +3317,11 @@ public final class ActivityThread { return r; } + private static String safeToComponentShortString(Intent intent) { + ComponentName component = intent.getComponent(); + return component == null ? "[Unknown]" : component.toShortString(); + } + private final void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityRecord r = performDestroyActivity(token, finishing, @@ -3475,8 +3581,6 @@ public final class ActivityThread { } mConfiguration.updateFrom(config); DisplayMetrics dm = getDisplayMetricsLocked(true); - DisplayMetrics appDm = new DisplayMetrics(); - appDm.setTo(dm); // set it for java, this also affects newly created Resources if (config.locale != null) { @@ -3496,11 +3600,7 @@ public final class ActivityThread { WeakReference<Resources> v = it.next(); Resources r = v.get(); if (r != null) { - // keep the original density based on application cale. - appDm.updateDensity(r.getDisplayMetrics().density); - r.updateConfiguration(config, appDm); - // reset - appDm.setTo(dm); + r.updateConfiguration(config, dm); //Log.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -3528,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(); @@ -3592,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) { @@ -3682,7 +3794,9 @@ public final class ActivityThread { mInstrumentation = new Instrumentation(); } - Application app = data.info.makeApplication(); + // If the app is being launched for full backup or restore, bring it up in + // a restricted environment with the base application class. + Application app = data.info.makeApplication(data.restrictedBackupMode); mInitialApplication = app; List<ProviderInfo> providers = data.providers; @@ -3867,7 +3981,10 @@ public final class ActivityThread { ProviderRecord pr = mProviderMap.get(name); if (pr.mProvider.asBinder() == provider.asBinder()) { Log.i(TAG, "Removing dead content provider: " + name); - mProviderMap.remove(name); + ProviderRecord removed = mProviderMap.remove(name); + if (removed != null) { + removed.mProvider.asBinder().unlinkToDeath(removed, 0); + } } } } @@ -3876,7 +3993,10 @@ public final class ActivityThread { ProviderRecord pr = mProviderMap.get(name); if (pr.mProvider.asBinder() == provider.asBinder()) { Log.i(TAG, "Removing dead content provider: " + name); - mProviderMap.remove(name); + ProviderRecord removed = mProviderMap.remove(name); + if (removed != null) { + removed.mProvider.asBinder().unlinkToDeath(removed, 0); + } } } diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index bb17dc35b4e0..38ea686f7077 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -16,8 +16,11 @@ package android.app; -import com.google.android.collect.Maps; +import com.android.internal.policy.PolicyManager; import com.android.internal.util.XmlUtils; +import com.google.android.collect.Maps; + +import org.xmlpull.v1.XmlPullParserException; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothDevice; @@ -29,6 +32,8 @@ import android.content.ContextWrapper; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IntentSender; import android.content.ReceiverCallNotAllowedException; import android.content.ServiceConnection; import android.content.SharedPreferences; @@ -37,9 +42,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageStatsObserver; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageManager; +import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -68,29 +73,30 @@ import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; -import android.os.Looper; -import android.os.RemoteException; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.Vibrator; import android.os.FileUtils.FileStatus; import android.telephony.TelephonyManager; import android.text.ClipboardManager; import android.util.AndroidRuntimeException; +import android.util.DisplayMetrics; import android.util.Log; import android.view.ContextThemeWrapper; +import android.view.Display; import android.view.LayoutInflater; import android.view.WindowManagerImpl; +import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; -import com.android.internal.policy.PolicyManager; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -100,16 +106,14 @@ import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import java.util.Set; -import java.util.HashSet; +import java.util.WeakHashMap; import java.util.Map.Entry; -import org.xmlpull.v1.XmlPullParserException; - class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { super(base); @@ -147,6 +151,7 @@ class ReceiverRestrictedContext extends ContextWrapper { */ class ApplicationContext extends Context { private final static String TAG = "ApplicationContext"; + private final static boolean DEBUG = false; private final static boolean DEBUG_ICONS = false; private static final Object sSync = new Object(); @@ -172,6 +177,7 @@ class ApplicationContext extends Context { private Resources.Theme mTheme = null; private PackageManager mPackageManager; private NotificationManager mNotificationManager = null; + private AccessibilityManager mAccessibilityManager = null; private ActivityManager mActivityManager = null; private Context mReceiverRestrictedContext = null; private SearchManager mSearchManager = null; @@ -181,6 +187,7 @@ class ApplicationContext extends Context { private StatusBarManager mStatusBarManager = null; private TelephonyManager mTelephonyManager = null; private ClipboardManager mClipboardManager = null; + private boolean mRestricted; private final Object mSync = new Object(); @@ -280,6 +287,14 @@ class ApplicationContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + if (mPackageInfo != null) { + return mPackageInfo.getApplicationInfo(); + } + throw new RuntimeException("Not supported in system context"); + } + + @Override public String getPackageResourcePath() { if (mPackageInfo != null) { return mPackageInfo.getResDir(); @@ -299,10 +314,14 @@ class ApplicationContext extends Context { return new File(prefsFile.getPath() + ".bak"); } + public File getSharedPrefsFile(String name) { + return makeFilename(getPreferencesDir(), name + ".xml"); + } + @Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; - File f = makeFilename(getPreferencesDir(), name + ".xml"); + File f = getSharedPrefsFile(name); synchronized (sSharedPrefs) { sp = sSharedPrefs.get(f); if (sp != null && !sp.hasFileChanged()) { @@ -550,19 +569,6 @@ class ApplicationContext extends Context { } } - /** - * @hide - */ - @Override - public float getApplicationScale() { - if (mPackageInfo != null) { - return mPackageInfo.getApplicationScale(); - } else { - // same as system density - return 1.0f; - } - } - @Override public void setWallpaper(Bitmap bitmap) throws IOException { try { @@ -904,6 +910,8 @@ class ApplicationContext extends Context { return getNotificationManager(); } else if (KEYGUARD_SERVICE.equals(name)) { return new KeyguardManager(); + } else if (ACCESSIBILITY_SERVICE.equals(name)) { + return AccessibilityManager.getInstance(this); } else if (LOCATION_SERVICE.equals(name)) { return getLocationManager(); } else if (SEARCH_SERVICE.equals(name)) { @@ -1033,11 +1041,6 @@ class ApplicationContext extends Context { } private SearchManager getSearchManager() { - // This is only useable in Activity Contexts - if (getActivityToken() == null) { - throw new AndroidRuntimeException( - "Acquiring SearchManager objects only valid in Activity Contexts."); - } synchronized (mSync) { if (mSearchManager == null) { mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler()); @@ -1238,7 +1241,7 @@ class ApplicationContext extends Context { @Override public int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags) { - if (false) { + if (DEBUG) { Log.i("foo", "checkUriPermission: uri=" + uri + "readPermission=" + readPermission + " writePermission=" + writePermission + " pid=" + pid + " uid=" + uid + " mode" + modeFlags); @@ -1337,8 +1340,22 @@ class ApplicationContext extends Context { mMainThread.getPackageInfo(packageName, flags); if (pi != null) { ApplicationContext c = new ApplicationContext(); + c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; c.init(pi, null, mMainThread); if (c.mResources != null) { + Resources newRes = c.mResources; + if (mResources.getCompatibilityInfo().applicationScale != + newRes.getCompatibilityInfo().applicationScale) { + DisplayMetrics dm = mMainThread.getDisplayMetricsLocked(false); + c.mResources = new Resources(newRes.getAssets(), dm, + newRes.getConfiguration(), + mResources.getCompatibilityInfo().copy()); + if (DEBUG) { + Log.d(TAG, "loaded context has different scaling. Using container's" + + " compatiblity info:" + mResources.getDisplayMetrics()); + } + + } return c; } } @@ -1348,6 +1365,11 @@ class ApplicationContext extends Context { "Application package " + packageName + " not found"); } + @Override + public boolean isRestricted() { + return mRestricted; + } + private File getDataDirFile() { if (mPackageInfo != null) { return mPackageInfo.getDataDirFile(); @@ -1453,7 +1475,7 @@ class ApplicationContext extends Context { if ((mode&MODE_WORLD_WRITEABLE) != 0) { perms |= FileUtils.S_IWOTH; } - if (false) { + if (DEBUG) { Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode) + ", perms=0x" + Integer.toHexString(perms)); } @@ -1516,43 +1538,33 @@ class ApplicationContext extends Context { throw new NameNotFoundException(packageName); } - public Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException { + @Override + public Intent getLaunchIntentForPackage(String packageName) { // First see if the package has an INFO activity; the existence of // such an activity is implied to be the desired front-door for the // overall package (such as if it has multiple launcher entries). - Intent intent = getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_INFO); - if (intent != null) { - return intent; - } - + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_INFO); + intentToResolve.setPackage(packageName); + ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0); + // Otherwise, try to find a main launcher activity. - return getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_LAUNCHER); - } - - // XXX This should be implemented as a call to the package manager, - // to reduce the work needed. - static Intent getLaunchIntentForPackageCategory(PackageManager pm, - String packageName, String category) { + if (resolveInfo == null) { + // reuse the intent instance + intentToResolve.removeCategory(Intent.CATEGORY_INFO); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + intentToResolve.setPackage(packageName); + resolveInfo = resolveActivity(intentToResolve, 0); + } + if (resolveInfo == null) { + return null; + } Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(packageName, resolveInfo.activityInfo.name); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null); - intentToResolve.addCategory(category); - final List<ResolveInfo> apps = - pm.queryIntentActivities(intentToResolve, 0); - // I wish there were a way to directly get the "main" activity of a - // package but ... - for (ResolveInfo app : apps) { - if (app.activityInfo.packageName.equals(packageName)) { - intent.setClassName(packageName, app.activityInfo.name); - return intent; - } - } - return null; + return intent; } - + @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { @@ -2024,8 +2036,7 @@ class ApplicationContext extends Context { ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app); Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir - : app.publicSourceDir, - pi.getApplicationScale()); + : app.publicSourceDir, pi); if (r != null) { return r; } @@ -2363,11 +2374,11 @@ class ApplicationContext extends Context { // Should never happen! } } - + @Override - public void freeStorage(long idealStorageSize, PendingIntent opFinishedIntent) { + public void freeStorage(long freeStorageSize, IntentSender pi) { try { - mPM.freeStorage(idealStorageSize, opFinishedIntent); + mPM.freeStorage(freeStorageSize, pi); } catch (RemoteException e) { // Should never happen! } @@ -2421,6 +2432,16 @@ class ApplicationContext extends Context { } @Override + public void replacePreferredActivity(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity) { + try { + mPM.replacePreferredActivity(filter, match, set, activity); + } catch (RemoteException e) { + // Should never happen! + } + } + + @Override public void clearPackagePreferredActivities(String packageName) { try { mPM.clearPackagePreferredActivities(packageName); diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java new file mode 100644 index 000000000000..6b172363296e --- /dev/null +++ b/core/java/android/app/ApplicationErrorReport.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +/** + * Describes an application error. + * + * A report has a type, which is one of + * <ul> + * <li> {@link #TYPE_CRASH} application crash. Information about the crash + * is stored in {@link #crashInfo}. + * <li> {@link #TYPE_ANR} application not responding. Information about the + * ANR is stored in {@link #anrInfo}. + * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}. + * </ul> + * + * @hide + */ + +public class ApplicationErrorReport implements Parcelable { + /** + * Uninitialized error report. + */ + public static final int TYPE_NONE = 0; + + /** + * An error report about an application crash. + */ + public static final int TYPE_CRASH = 1; + + /** + * An error report about an application that's not responding. + */ + public static final int TYPE_ANR = 2; + + /** + * Type of this report. Can be one of {@link #TYPE_NONE}, + * {@link #TYPE_CRASH} or {@link #TYPE_ANR}. + */ + public int type; + + /** + * Package name of the application. + */ + public String packageName; + + /** + * Package name of the application which installed the application this + * report pertains to. + * This identifies which Market the application came from. + */ + public String installerPackageName; + + /** + * Process name of the application. + */ + public String processName; + + /** + * Time at which the error occurred. + */ + public long time; + + /** + * If this report is of type {@link #TYPE_CRASH}, contains an instance + * of CrashInfo describing the crash; otherwise null. + */ + public CrashInfo crashInfo; + + /** + * If this report is of type {@link #TYPE_ANR}, contains an instance + * of AnrInfo describing the ANR; otherwise null. + */ + public AnrInfo anrInfo; + + /** + * Create an uninitialized instance of {@link ApplicationErrorReport}. + */ + public ApplicationErrorReport() { + } + + /** + * Create an instance of {@link ApplicationErrorReport} initialized from + * a parcel. + */ + ApplicationErrorReport(Parcel in) { + readFromParcel(in); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(type); + dest.writeString(packageName); + dest.writeString(installerPackageName); + dest.writeString(processName); + dest.writeLong(time); + + switch (type) { + case TYPE_CRASH: + crashInfo.writeToParcel(dest, flags); + break; + case TYPE_ANR: + anrInfo.writeToParcel(dest, flags); + break; + } + } + + public void readFromParcel(Parcel in) { + type = in.readInt(); + packageName = in.readString(); + installerPackageName = in.readString(); + processName = in.readString(); + time = in.readLong(); + + switch (type) { + case TYPE_CRASH: + crashInfo = new CrashInfo(in); + anrInfo = null; + break; + case TYPE_ANR: + anrInfo = new AnrInfo(in); + crashInfo = null; + break; + } + } + + /** + * Describes an application crash. + */ + public static class CrashInfo { + /** + * Class name of the exception that caused the crash. + */ + public String exceptionClassName; + + /** + * Message stored in the exception. + */ + public String exceptionMessage; + + /** + * File which the exception was thrown from. + */ + public String throwFileName; + + /** + * Class which the exception was thrown from. + */ + public String throwClassName; + + /** + * Method which the exception was thrown from. + */ + public String throwMethodName; + + /** + * Stack trace. + */ + public String stackTrace; + + /** + * Create an uninitialized instance of CrashInfo. + */ + public CrashInfo() { + } + + /** + * Create an instance of CrashInfo initialized from a Parcel. + */ + public CrashInfo(Parcel in) { + exceptionClassName = in.readString(); + exceptionMessage = in.readString(); + throwFileName = in.readString(); + throwClassName = in.readString(); + throwMethodName = in.readString(); + stackTrace = in.readString(); + } + + /** + * Save a CrashInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(exceptionClassName); + dest.writeString(exceptionMessage); + dest.writeString(throwFileName); + dest.writeString(throwClassName); + dest.writeString(throwMethodName); + dest.writeString(stackTrace); + } + + /** + * Dump a CrashInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "exceptionClassName: " + exceptionClassName); + pw.println(prefix + "exceptionMessage: " + exceptionMessage); + pw.println(prefix + "throwFileName: " + throwFileName); + pw.println(prefix + "throwClassName: " + throwClassName); + pw.println(prefix + "throwMethodName: " + throwMethodName); + pw.println(prefix + "stackTrace: " + stackTrace); + } + } + + /** + * Describes an application not responding error. + */ + public static class AnrInfo { + /** + * Activity name. + */ + public String activity; + + /** + * Description of the operation that timed out. + */ + public String cause; + + /** + * Additional info, including CPU stats. + */ + public String info; + + /** + * Create an uninitialized instance of AnrInfo. + */ + public AnrInfo() { + } + + /** + * Create an instance of AnrInfo initialized from a Parcel. + */ + public AnrInfo(Parcel in) { + activity = in.readString(); + cause = in.readString(); + info = in.readString(); + } + + /** + * Save an AnrInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(activity); + dest.writeString(cause); + dest.writeString(info); + } + + /** + * Dump an AnrInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "activity: " + activity); + pw.println(prefix + "cause: " + cause); + pw.println(prefix + "info: " + info); + } + } + + public static final Parcelable.Creator<ApplicationErrorReport> CREATOR + = new Parcelable.Creator<ApplicationErrorReport>() { + public ApplicationErrorReport createFromParcel(Parcel source) { + return new ApplicationErrorReport(source); + } + + public ApplicationErrorReport[] newArray(int size) { + return new ApplicationErrorReport[size]; + } + }; + + public int describeContents() { + return 0; + } + + /** + * Dump the report to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "type: " + type); + pw.println(prefix + "packageName: " + packageName); + pw.println(prefix + "installerPackageName: " + installerPackageName); + pw.println(prefix + "processName: " + processName); + pw.println(prefix + "time: " + time); + + switch (type) { + case TYPE_CRASH: + crashInfo.dump(pw, prefix); + break; + case TYPE_ANR: + anrInfo.dump(pw, prefix); + break; + } + } +} diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index bcc930208206..b052c99e75fc 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -18,6 +18,7 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; @@ -25,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; @@ -230,11 +232,13 @@ public abstract class ApplicationThreadNative extends Binder IBinder binder = data.readStrongBinder(); IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder); int testMode = data.readInt(); + boolean restrictedBackupMode = (data.readInt() != 0); Configuration config = Configuration.CREATOR.createFromParcel(data); HashMap<String, IBinder> services = data.readHashMap(null); bindApplication(packageName, info, providers, testName, profileName, - testArgs, testWatcher, testMode, config, services); + testArgs, testWatcher, testMode, restrictedBackupMode, + config, services); return true; } @@ -328,7 +332,34 @@ 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; + } + + case SET_SCHEDULING_GROUP_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + int group = data.readInt(); + setSchedulingGroup(group); + return true; + } + + case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data); + int backupMode = data.readInt(); + scheduleCreateBackupAgent(appInfo, backupMode); + return true; + } + + case SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data); + scheduleDestroyBackupAgent(appInfo); return true; } } @@ -484,6 +515,24 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) + throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + app.writeToParcel(data, 0); + data.writeInt(backupMode); + mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0); + data.recycle(); + } + + public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + app.writeToParcel(data, 0); + mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0); + data.recycle(); + } + public final void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException { Parcel data = Parcel.obtain(); @@ -543,7 +592,8 @@ class ApplicationThreadProxy implements IApplicationThread { public final void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode, - Configuration config, Map<String, IBinder> services) throws RemoteException { + boolean restrictedBackupMode, Configuration config, + Map<String, IBinder> services) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeString(packageName); @@ -559,6 +609,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeBundle(testArgs); data.writeStrongInterface(testWatcher); data.writeInt(debugMode); + data.writeInt(restrictedBackupMode ? 1 : 0); config.writeToParcel(data, 0); data.writeMap(services); mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null, @@ -663,14 +714,30 @@ 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(); } + + public void setSchedulingGroup(int group) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeInt(group); + mRemote.transact(SET_SCHEDULING_GROUP_TRANSACTION, data, null, + IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/backup/BackupService.java b/core/java/android/app/BackupAgent.java index 50a5921c0421..0ac8a1e4cfdc 100644 --- a/core/java/android/backup/BackupService.java +++ b/core/java/android/app/BackupAgent.java @@ -14,47 +14,38 @@ * limitations under the License. */ -package android.backup; +package android.app; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.app.Service; -import android.backup.IBackupService; -import android.content.Intent; +import android.app.IBackupAgent; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.content.Context; +import android.content.ContextWrapper; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.IOException; + /** * This is the central interface between an application and Android's * settings backup mechanism. * - * In order to use the backup service, your application must implement a - * subclass of BackupService, and declare an intent filter - * in the application manifest specifying that your BackupService subclass - * handles the {@link BackupService#SERVICE_ACTION} intent action. For example: - * - * <pre class="prettyprint"> - * <!-- Use the class "MyBackupService" to perform backups for my app --> - * <service android:name=".MyBackupService"> - * <intent-filter> - * <action android:name="android.backup.BackupService.SERVICE" /> - * </intent-filter> - * </service></pre> - * * @hide pending API solidification */ +public abstract class BackupAgent extends ContextWrapper { + private static final String TAG = "BackupAgent"; -public abstract class BackupService extends Service { - /** - * Service Action: Participate in the backup infrastructure. Applications - * that wish to use the Android backup mechanism must provide an exported - * subclass of BackupService and give it an {@link android.content.IntentFilter - * IntentFilter} that accepts this action. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_ACTION = "android.backup.BackupService.SERVICE"; + public BackupAgent() { + super(null); + } + + public void onCreate() { + } + + public void onDestroy() { + } /** * The application is being asked to write any data changed since the @@ -76,7 +67,7 @@ public abstract class BackupService extends Service { * 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 @@ -87,11 +78,17 @@ public abstract class BackupService extends Service { * * @param data An open, read-only ParcelFileDescriptor pointing to a full snapshot * of the application's data. + * @param appVersionCode The android:versionCode value of the application that backed + * up this particular data set. This makes it easier for an application's + * agent to distinguish among several possible older data versions when + * asked to perform the restore operation. * @param newState An open, read/write ParcelFileDescriptor pointing to an empty * file. The application should record the final backup state * here after restoring its data from dataFd. */ - public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data, ParcelFileDescriptor newState); + public abstract void onRestore(BackupDataInput data, int appVersionCode, + ParcelFileDescriptor newState) + throws IOException; // ----- Core implementation ----- @@ -100,38 +97,52 @@ public abstract class BackupService extends Service { * Returns the private interface called by the backup system. Applications will * not typically override this. */ - public IBinder onBind(Intent intent) { - if (intent.getAction().equals(SERVICE_ACTION)) { - return mBinder; - } - return null; + public IBinder onBind() { + return mBinder; } private final IBinder mBinder = new BackupServiceBinder().asBinder(); + /** @hide */ + public void attach(Context context) { + attachBaseContext(context); + } + // ----- IBackupService binder interface ----- - private class BackupServiceBinder extends IBackupService.Stub { + private class BackupServiceBinder extends IBackupAgent.Stub { + private static final String TAG = "BackupServiceBinder"; + public void doBackup(ParcelFileDescriptor oldState, ParcelFileDescriptor data, ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly - Log.v("BackupServiceBinder", "doBackup() invoked"); - BackupDataOutput output = new BackupDataOutput(BackupService.this, - data.getFileDescriptor()); + Log.v(TAG, "doBackup() invoked"); + BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); try { - BackupService.this.onBackup(oldState, output, newState); + 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("BackupService", "onBackup (" - + BackupService.this.getClass().getName() + ") threw", ex); + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; } } - public void doRestore(ParcelFileDescriptor data, + public void doRestore(ParcelFileDescriptor data, int appVersionCode, ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly - Log.v("BackupServiceBinder", "doRestore() invoked"); - BackupService.this.onRestore(data, newState); + Log.v(TAG, "doRestore() invoked"); + BackupDataInput input = new BackupDataInput(data.getFileDescriptor()); + try { + BackupAgent.this.onRestore(input, appVersionCode, newState); + } catch (IOException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); + } catch (RuntimeException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw ex; + } } } } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 863cbcc4d113..78bbb4f42c19 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -46,7 +46,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, private final DatePicker mDatePicker; private final OnDateSetListener mCallBack; private final Calendar mCalendar; - private final java.text.DateFormat mDateFormat; private final java.text.DateFormat mTitleDateFormat; private final String[] mWeekDays; @@ -108,7 +107,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, DateFormatSymbols symbols = new DateFormatSymbols(); mWeekDays = symbols.getShortWeekdays(); - mDateFormat = DateFormat.getMediumDateFormat(context); mTitleDateFormat = java.text.DateFormat. getDateInstance(java.text.DateFormat.FULL); mCalendar = Calendar.getInstance(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index b09a57fb27a5..222fe75fb3a2 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -16,32 +16,34 @@ package android.app; +import com.android.internal.policy.PolicyManager; + import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.os.Bundle; import android.util.Config; import android.util.Log; import android.view.ContextMenu; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnCreateContextMenuListener; - -import com.android.internal.policy.PolicyManager; +import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; import java.lang.ref.WeakReference; @@ -81,6 +83,7 @@ public class Dialog implements DialogInterface, Window.Callback, * {@hide} */ protected boolean mCancelable = true; + private Message mCancelMessage; private Message mDismissMessage; @@ -209,7 +212,9 @@ public class Dialog implements DialogInterface, Window.Callback, if (mShowing) { if (Config.LOGV) Log.v(LOG_TAG, "[Dialog] start: already showing, ignore"); - if (mDecor != null) mDecor.setVisibility(View.VISIBLE); + if (mDecor != null) { + mDecor.setVisibility(View.VISIBLE); + } return; } @@ -236,7 +241,9 @@ public class Dialog implements DialogInterface, Window.Callback, * Hide the dialog, but do not dismiss it. */ public void hide() { - if (mDecor != null) mDecor.setVisibility(View.GONE); + if (mDecor != null) { + mDecor.setVisibility(View.GONE); + } } /** @@ -266,6 +273,7 @@ public class Dialog implements DialogInterface, Window.Callback, } mWindowManager.removeView(mDecor); + mDecor = null; mWindow.closeAllPanels(); onStop(); @@ -280,7 +288,7 @@ public class Dialog implements DialogInterface, Window.Callback, Message.obtain(mDismissMessage).sendToTarget(); } } - + // internal method to make sure mcreated is set properly without requiring // users to call through to super in onCreate void dispatchOnCreate(Bundle savedInstanceState) { @@ -608,6 +616,18 @@ public class Dialog implements DialogInterface, Window.Callback, return onTrackballEvent(ev); } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(mContext.getPackageName()); + + LayoutParams params = getWindow().getAttributes(); + boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && + (params.height == LayoutParams.FILL_PARENT); + event.setFullScreen(isFullScreen); + + return false; + } + /** * @see Activity#onCreatePanelView(int) */ diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java new file mode 100644 index 000000000000..d89db9667473 --- /dev/null +++ b/core/java/android/app/FullBackupAgent.java @@ -0,0 +1,58 @@ +package android.app; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.FileBackupHelper; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Backs up an application's entire /data/data/<package>/... file system. This + * class is used by the desktop full backup mechanism and is not intended for direct + * use by applications. + * + * {@hide} + */ + +public class FullBackupAgent extends BackupAgent { + // !!! TODO: turn off debugging + private static final String TAG = "FullBackupAgent"; + private static final boolean DEBUG = true; + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + LinkedList<File> dirsToScan = new LinkedList<File>(); + ArrayList<String> allFiles = new ArrayList<String>(); + + // build the list of files in the app's /data/data tree + dirsToScan.add(getFilesDir()); + if (DEBUG) Log.v(TAG, "Backing up dir tree @ " + getFilesDir().getAbsolutePath() + " :"); + while (dirsToScan.size() > 0) { + File dir = dirsToScan.removeFirst(); + File[] contents = dir.listFiles(); + if (contents != null) { + for (File f : contents) { + if (f.isDirectory()) { + dirsToScan.add(f); + } else if (f.isFile()) { + if (DEBUG) Log.v(TAG, " " + f.getAbsolutePath()); + allFiles.add(f.getAbsolutePath()); + } + } + } + } + + // That's the file set; now back it all up + FileBackupHelper helper = new FileBackupHelper(this, (String[])allFiles.toArray()); + helper.performBackup(oldState, data, newState); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { + } +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 56b29c1c941f..3ec7938b5a10 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -21,6 +21,9 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.ProviderInfo; @@ -44,9 +47,30 @@ import java.util.List; * {@hide} */ public interface IActivityManager extends IInterface { + /** + * Returned by startActivity() if the start request was canceled because + * app switches are temporarily canceled to ensure the user's last request + * (such as pressing home) is performed. + */ + public static final int START_SWITCHES_CANCELED = 4; + /** + * Returned by startActivity() if an activity wasn't really started, but + * the given Intent was given to the existing top activity. + */ public static final int START_DELIVERED_TO_TOP = 3; + /** + * Returned by startActivity() if an activity wasn't really started, but + * a task was simply brought to the foreground. + */ public static final int START_TASK_TO_FRONT = 2; + /** + * Returned by startActivity() if the caller asked that the Intent not + * be executed if it is the recipient, and that is indeed the case. + */ public static final int START_RETURN_INTENT_TO_CALLER = 1; + /** + * Activity was started successfully as normal. + */ public static final int START_SUCCESS = 0; public static final int START_INTENT_NOT_RESOLVED = -1; public static final int START_CLASS_NOT_FOUND = -2; @@ -128,6 +152,11 @@ public interface IActivityManager extends IInterface { public void serviceDoneExecuting(IBinder token) throws RemoteException; public IBinder peekService(Intent service, String resolvedType) throws RemoteException; + public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode) + throws RemoteException; + public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException; + public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException; @@ -221,10 +250,13 @@ 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; + public void stopAppSwitches() throws RemoteException; + public void resumeAppSwitches() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -371,4 +403,9 @@ public interface IActivityManager extends IInterface { int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85; int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86; + int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87; + int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88; + int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89; + int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90; + int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 9f3534b0924a..c0bc2a03ac91 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -18,12 +18,14 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; 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; @@ -59,6 +61,11 @@ public interface IApplicationThread extends IInterface { int configChanges) throws RemoteException; void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode, String data, Bundle extras, boolean sync) throws RemoteException; + static final int BACKUP_MODE_INCREMENTAL = 0; + static final int BACKUP_MODE_FULL = 1; + static final int BACKUP_MODE_RESTORE = 2; + void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException; + void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException; void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException; void scheduleBindService(IBinder token, Intent intent, boolean rebind) throws RemoteException; @@ -71,8 +78,8 @@ public interface IApplicationThread extends IInterface { static final int DEBUG_WAIT = 2; void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, String profileName, Bundle testArguments, - IInstrumentationWatcher testWatcher, int debugMode, Configuration config, Map<String, - IBinder> services) throws RemoteException; + IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode, + Configuration config, Map<String, IBinder> services) throws RemoteException; void scheduleExit() throws RemoteException; void requestThumbnail(IBinder token) throws RemoteException; void scheduleConfigurationChanged(Configuration config) throws RemoteException; @@ -86,8 +93,10 @@ 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"; int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; @@ -117,4 +126,7 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25; int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26; int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27; + int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28; + int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29; + int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30; } diff --git a/core/java/android/backup/IBackupService.aidl b/core/java/android/app/IBackupAgent.aidl index 1bde8eac9bdb..9b0550fce64b 100644 --- a/core/java/android/backup/IBackupService.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -14,18 +14,18 @@ * limitations under the License. */ -package android.backup; +package android.app; import android.os.ParcelFileDescriptor; /** * Interface presented by applications being asked to participate in the * backup & restore mechanism. End user code does not typically implement - * this interface; they subclass BackupService instead. + * this interface; they subclass BackupAgent instead. * * {@hide} */ -interface IBackupService { +interface IBackupAgent { /** * Request that the app perform an incremental backup. * @@ -51,9 +51,14 @@ interface IBackupService { * app's backup. This is to be a <i>replacement</i> of the app's * current data, not to be merged into it. * + * @param appVersionCode The android:versionCode attribute of the application + * that created this data set. This can help the agent distinguish among + * various historical backup content possibilities. + * * @param newState Read-write file, empty when onRestore() is called, * that is to be written with the state description that holds after * the restore has been completed. */ - void doRestore(in ParcelFileDescriptor data, in ParcelFileDescriptor newState); + void doRestore(in ParcelFileDescriptor data, int appVersionCode, + in ParcelFileDescriptor newState); } diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 39eb4f1ccbe8..e8bd60a4d86f 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -16,11 +16,28 @@ package android.app; +import android.app.ISearchManagerCallback; import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.Bundle; import android.server.search.SearchableInfo; /** @hide */ interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); List<SearchableInfo> getSearchablesInGlobalSearch(); + List<SearchableInfo> getSearchablesForWebSearch(); + SearchableInfo getDefaultSearchableForWebSearch(); + void setDefaultWebSearch(in ComponentName component); + void startSearch(in String initialQuery, + boolean selectInitialQuery, + in ComponentName launchActivity, + in Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback); + void stopSearch(); + boolean isVisible(); + Bundle onSaveInstanceState(); + void onRestoreInstanceState(in Bundle savedInstanceState); + void onConfigurationChanged(in Configuration newConfig); } diff --git a/core/java/android/app/ISearchManagerCallback.aidl b/core/java/android/app/ISearchManagerCallback.aidl new file mode 100644 index 000000000000..bdfb2bad7ab4 --- /dev/null +++ b/core/java/android/app/ISearchManagerCallback.aidl @@ -0,0 +1,23 @@ +/** + * 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.app; + +/** @hide */ +oneway interface ISearchManagerCallback { + void onDismiss(); + void onCancel(); +} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f6a28b23a3a8..e31f4f834404 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -446,13 +446,13 @@ public class Instrumentation { if (ai == null) { throw new RuntimeException("Unable to resolve activity for: " + intent); } - if (!ai.applicationInfo.processName.equals( - getTargetContext().getPackageName())) { + String myProc = mThread.getProcessName(); + if (!ai.processName.equals(myProc)) { // todo: if this intent is ambiguous, look here to see if // there is a single match that is in our package. - throw new RuntimeException("Intent resolved to different package " - + ai.applicationInfo.packageName + ": " - + intent); + throw new RuntimeException("Intent in process " + + myProc + " resolved to different process " + + ai.processName + ": " + intent); } intent.setComponent(new ComponentName( diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8d249da960bd..accdda9ba599 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/PendingIntent.java b/core/java/android/app/PendingIntent.java index cb660c7fde6f..f9c38f998c35 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -18,6 +18,9 @@ package android.app; import android.content.Context; import android.content.Intent; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IntentSender; import android.os.Bundle; import android.os.RemoteException; import android.os.Handler; @@ -105,7 +108,7 @@ public final class PendingIntent implements Parcelable { public CanceledException(Exception cause) { super(cause); } - }; + } /** * Callback interface for discovering when a send operation has @@ -270,6 +273,21 @@ public final class PendingIntent implements Parcelable { return null; } + private class IntentSenderWrapper extends IntentSender { + protected IntentSenderWrapper(IIntentSender target) { + super(target); + } + } + /** + * Retrieve a IntentSender object that wraps the existing sender of the PendingIntent + * + * @return Returns a IntentSender object that wraps the sender of PendingIntent + * + */ + public IntentSender getIntentSender() { + return new IntentSenderWrapper(mTarget); + } + /** * Cancel a currently active PendingIntent. Only the original application * owning an PendingIntent can cancel it. diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 343380cc766e..fdb619ae2da6 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -24,6 +24,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -32,6 +34,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Animatable; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -41,8 +44,10 @@ import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.util.Regex; import android.util.AttributeSet; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; @@ -51,6 +56,7 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; @@ -67,8 +73,8 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; /** - * In-application-process implementation of Search Bar. This is still controlled by the - * SearchManager, but it runs in the current activity's process to keep things lighter weight. + * System search dialog. This is controlled by the + * SearchManagerService and runs in the system process. * * @hide */ @@ -82,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; @@ -103,6 +107,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Button mGoButton; private ImageButton mVoiceButton; private View mSearchPlate; + private Drawable mWorkingSpinner; // interaction with searchable application private SearchableInfo mSearchable; @@ -129,9 +134,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private SuggestionsAdapter mSuggestionsAdapter; // Whether to rewrite queries when selecting suggestions - // TODO: This is disabled because of problems with persistent selections - // causing non-user-initiated rewrites. - private static final boolean REWRITE_QUERIES = false; + private static final boolean REWRITE_QUERIES = true; // The query entered by the user. This is not changed when selecting a suggestion // that modifies the contents of the text field. But if the user then edits @@ -142,14 +145,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // more than once. private final WeakHashMap<String, Drawable> mOutsideDrawablesCache = new WeakHashMap<String, Drawable>(); - + + // Last known IME options value for the search edit text. + private int mSearchAutoCompleteImeOptions; + /** * Constructor - fires it up and makes it look like the search UI. * * @param context Application Context we can use for system acess */ public SearchDialog(Context context) { - super(context, com.android.internal.R.style.Theme_SearchBar); + super(context, com.android.internal.R.style.Theme_GlobalSearchBar); } /** @@ -160,17 +166,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Window theWindow = getWindow(); - theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL); - setContentView(com.android.internal.R.layout.search_bar); - theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT, - // taking up the whole window (even when transparent) is less than ideal, - // but necessary to show the popup window until the window manager supports - // having windows anchored by their parent but not clipped by them. - ViewGroup.LayoutParams.FILL_PARENT); + Window theWindow = getWindow(); WindowManager.LayoutParams lp = theWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR; + lp.width = ViewGroup.LayoutParams.FILL_PARENT; + // taking up the whole window (even when transparent) is less than ideal, + // but necessary to show the popup window until the window manager supports + // having windows anchored by their parent but not clipped by them. + lp.height = ViewGroup.LayoutParams.FILL_PARENT; + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; theWindow.setAttributes(lp); @@ -182,6 +188,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); + mWorkingSpinner = getContext().getResources(). + getDrawable(com.android.internal.R.drawable.search_spinner); // attach listeners mSearchAutoComplete.addTextChangedListener(mTextWatcher); @@ -213,10 +221,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Save voice intent for later queries/launching mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions(); } /** @@ -226,20 +238,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ public boolean show(String initialQuery, boolean selectInitialQuery, ComponentName componentName, Bundle appSearchData, boolean globalSearch) { - if (isShowing()) { - // race condition - already showing but not handling events yet. - // in this case, just discard the "show" request - return true; - } - + // Reset any stored values from last time dialog was shown. mStoredComponentName = null; mStoredAppSearchData = null; - - return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch); + + boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData, + globalSearch); + if (success) { + // Display the drop down as soon as possible instead of waiting for the rest of the + // pending UI stuff to get done, so that things appear faster to the user. + mSearchAutoComplete.showDropDownAfterLayout(); + } + return success; } - /** * Called in response to a press of the hard search button in * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app @@ -309,15 +322,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS + appSearchData + ", " + globalSearch + ")"); } + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); // Try to get the searchable info for the provided component (or for global search, // if globalSearch == true). - mSearchable = SearchManager.getSearchableInfo(componentName, globalSearch); + mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); // If we got back nothing, and it wasn't a request for global search, then try again // for global search, as we'll try to launch that in lieu of any component-specific search. if (!globalSearch && mSearchable == null) { globalSearch = true; - mSearchable = SearchManager.getSearchableInfo(componentName, globalSearch); + mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); // If we still get back null (i.e., there's not even a searchable info available // for global search), then really give up. @@ -332,7 +347,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mAppSearchData = appSearchData; // Using globalSearch here is just an optimization, just calling // isDefaultSearchable() should always give the same result. - mGlobalSearchMode = globalSearch || SearchManager.isDefaultSearchable(mSearchable); + mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable); mActivityContext = mSearchable.getActivityContext(getContext()); // show the dialog. this will call onStart(). @@ -345,6 +360,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInputUnchecked(0, null); } + + // The Dialog uses a ContextThemeWrapper for the context; use this to change the + // theme out from underneath us, between the global search theme and the in-app + // search theme. They are identical except that the global search theme does not + // dim the background of the window (because global search is full screen so it's + // not needed and this should save a little bit of time on global search invocation). + Object context = getContext(); + if (context instanceof ContextThemeWrapper) { + ContextThemeWrapper wrapper = (ContextThemeWrapper) context; + if (globalSearch) { + wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar); + } else { + wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar); + } + } show(); } @@ -372,11 +402,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS public void onStop() { super.onStop(); - // TODO: Removing the listeners means that they never get called, since - // Dialog.dismissDialog() calls onStop() before sendDismissMessage(). - setOnCancelListener(null); - setOnDismissListener(null); - // stop receiving broadcasts (throws exception if none registered) try { getContext().unregisterReceiver(mBroadcastReceiver); @@ -394,6 +419,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mUserQuery = null; mPreviousComponents = null; } + + /** + * Sets the search dialog to the 'working' state, which shows a working spinner in the + * right hand size of the text field. + * + * @param working true to show spinner, false to hide spinner + */ + public void setWorking(boolean working) { + if (working) { + mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( + null, null, mWorkingSpinner, null); + ((Animatable) mWorkingSpinner).start(); + } else { + mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, null); + ((Animatable) mWorkingSpinner).stop(); + } + } /** * Closes and gets rid of the suggestions adapter. @@ -412,8 +455,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 @@ -424,20 +465,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; } @@ -451,45 +483,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; - } } /** @@ -534,7 +548,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } mSearchAutoComplete.setInputType(inputType); - mSearchAutoComplete.setImeOptions(mSearchable.getImeOptions()); + mSearchAutoCompleteImeOptions = mSearchable.getImeOptions(); + mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions); } } @@ -547,24 +562,20 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold()); + // we dismiss the entire dialog instead + mSearchAutoComplete.setDropDownDismissedOnCompletion(false); if (mGlobalSearchMode) { mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in - mSearchAutoComplete.setDropDownDismissedOnCompletion(false); - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background); } else { mSearchAutoComplete.setDropDownAlwaysVisible(false); - mSearchAutoComplete.setDropDownDismissedOnCompletion(true); - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background_apps); } // attach the suggestions adapter, if suggestions are available // The existence of a suggestions authority is the proxy for "suggestions available here" if (mSearchable.getSuggestAuthority() != null) { - mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable, - mOutsideDrawablesCache); + mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable, + mOutsideDrawablesCache, mGlobalSearchMode); mSearchAutoComplete.setAdapter(mSuggestionsAdapter); } } @@ -597,7 +608,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchPlate.getPaddingBottom()); } else { PackageManager pm = getContext().getPackageManager(); - Drawable icon = null; + Drawable icon; try { ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); icon = pm.getApplicationIcon(info.applicationInfo); @@ -765,7 +776,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } - public void afterTextChanged(Editable s) { } + public void afterTextChanged(Editable s) { + if (!mSearchAutoComplete.isPerformingCompletion()) { + // The user changed the query, check if it is a URL and if so change the search + // button in the soft keyboard to the 'Go' button. + int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)); + if (Regex.WEB_URL_PATTERN.matcher(mUserQuery).matches()) { + options = options | EditorInfo.IME_ACTION_GO; + } else { + options = options | EditorInfo.IME_ACTION_SEARCH; + } + if (options != mSearchAutoCompleteImeOptions) { + mSearchAutoCompleteImeOptions = options; + mSearchAutoComplete.setImeOptions(options); + // This call is required to update the soft keyboard UI with latest IME flags. + mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType()); + } + } + } }; /** @@ -903,6 +931,32 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** + * Corrects http/https typo errors in the given url string, and if the protocol specifier was + * not present defaults to http. + * + * @param inUrl URL to check and fix + * @return fixed URL string. + */ + private String fixUrl(String inUrl) { + if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) + return inUrl; + + if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) { + if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { + inUrl = inUrl.replaceFirst("/", "//"); + } else { + inUrl = inUrl.replaceFirst(":", "://"); + } + } + + if (inUrl.indexOf("://") == -1) { + inUrl = "http://" + inUrl; + } + + return inUrl; + } + + /** * React to the user typing "enter" or other hardwired keys while typing in the search box. * This handles these special keys while the edit box has focus. */ @@ -932,7 +986,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { v.cancelLongPress(); - launchQuerySearch(); + + // If this is a url entered by the user and we displayed the 'Go' button which + // the user clicked, launch the url instead of using it as a search query. + if ((mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION) + == EditorInfo.IME_ACTION_GO) { + Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString())); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent(intent); + } else { + // Launch as a regular search. + launchQuerySearch(); + } return true; } if (event.getAction() == KeyEvent.ACTION_DOWN) { @@ -1069,7 +1135,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ protected void launchQuerySearch(int actionKey, String actionMsg) { String query = mSearchAutoComplete.getText().toString(); - Intent intent = createIntent(Intent.ACTION_SEARCH, null, query, null, + Intent intent = createIntent(Intent.ACTION_SEARCH, null, null, query, null, actionKey, actionMsg); launchIntent(intent); } @@ -1097,15 +1163,121 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { Cursor c = mSuggestionsAdapter.getCursor(); if ((c != null) && c.moveToPosition(position)) { + Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); + + // report back about the click + if (mGlobalSearchMode) { + // in global search mode, do it via cursor + mSuggestionsAdapter.callCursorOnClick(c, position); + } else if (intent != null + && mPreviousComponents != null + && !mPreviousComponents.isEmpty()) { + // in-app search (and we have pivoted in as told by mPreviousComponents, + // which is used for keeping track of what we pop back to when we are pivoting into + // in app search.) + reportInAppClickToGlobalSearch(c, intent); + } + + // launch the intent launchIntent(intent); + return true; } return false; } - + + /** + * Report a click from an in app search result back to global search for shortcutting porpoises. + * + * @param c The cursor that is pointing to the clicked position. + * @param intent The intent that will be launched for the click. + */ + private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { + // for in app search, still tell global search via content provider + Uri uri = getClickReportingUri(); + final ContentValues cv = new ContentValues(); + cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); + final ComponentName source = mSearchable.getSearchActivity(); + cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); + + // grab the intent columns from the intent we created since it has additional + // logic for falling back on the searchable default + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, + intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY)); + + // ensure the icons will work for global search + cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, + wrapIconForPackage( + source, + getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); + cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, + wrapIconForPackage( + source, + getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); + + // the rest can be passed through directly + cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, + getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); + cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, + getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); + cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, + getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); + cv.put(SearchManager.SUGGEST_COLUMN_QUERY, + getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); + cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, + getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); + // note: deliberately omitting background color since it is only for global search + // "more results" entries + mContext.getContentResolver().insert(uri, cv); + } + /** - * Launches an intent. Also dismisses the search dialog if not in global search mode. + * @return A URI appropriate for reporting a click. + */ + private Uri getClickReportingUri() { + Uri.Builder uriBuilder = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); + + uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); + + return uriBuilder + .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() + .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() + .build(); + } + + /** + * Wraps an icon for a particular package. If the icon is a resource id, it is converted into + * an android.resource:// URI. + * + * @param source The source of the icon + * @param icon The icon retrieved from a suggestion column + * @return An icon string appropriate for the package. + */ + private String wrapIconForPackage(ComponentName source, String icon) { + if (icon == null || icon.length() == 0 || "0".equals(icon)) { + // SearchManager specifies that null or zero can be returned to indicate + // no icon. We also allow empty string. + return null; + } else if (!Character.isDigit(icon.charAt(0))){ + return icon; + } else { + String packageName = source.getPackageName(); + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(packageName) + .encodedPath(icon) + .toString(); + } + } + + /** + * Launches an intent and dismisses the search dialog (unless the intent + * is one of the special intents that modifies the state of the search dialog). */ private void launchIntent(Intent intent) { if (intent == null) { @@ -1114,9 +1286,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (handleSpecialIntent(intent)){ return; } - if (!mGlobalSearchMode) { - dismiss(); - } + dismiss(); getContext().startActivity(intent); } @@ -1130,15 +1300,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) { handleChangeSourceIntent(intent); return true; - } else if (SearchManager.INTENT_ACTION_CURSOR_RESPOND.equals(action)) { - handleCursorRespondIntent(intent); - return true; } return false; } /** - * Handles SearchManager#INTENT_ACTION_CHANGE_SOURCE. + * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. */ private void handleChangeSourceIntent(Intent intent) { Uri dataUri = intent.getData(); @@ -1162,18 +1329,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = intent.getStringExtra(SearchManager.QUERY); setUserQuery(query); + mSearchAutoComplete.showDropDown(); } - + /** - * Handles {@link SearchManager#INTENT_ACTION_CURSOR_RESPOND}. + * Sets the list item selection in the AutoCompleteTextView's ListView. */ - private void handleCursorRespondIntent(Intent intent) { - Cursor c = mSuggestionsAdapter.getCursor(); - if (c != null) { - c.respond(intent.getExtras()); - } + public void setListSelection(int index) { + mSearchAutoComplete.setListSelection(index); } - + /** * Saves the previous component that was searched, so that we can go * back to it. @@ -1243,6 +1408,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS try { // use specific action if supplied, or default action if supplied, or fixed default String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); + + // some items are display only, or have effect via the cursor respond click reporting. + if (SearchManager.INTENT_ACTION_NONE.equals(action)) { + return null; + } + if (action == null) { action = mSearchable.getSuggestIntentAction(); } @@ -1264,11 +1435,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } Uri dataUri = (data == null) ? null : Uri.parse(data); - String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - + String componentName = getColumnString( + c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME); + String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); + String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - return createIntent(action, dataUri, query, extraData, actionKey, actionMsg); + return createIntent(action, dataUri, extraData, query, componentName, actionKey, + actionMsg); } catch (RuntimeException e ) { int rowNum; try { // be really paranoid now @@ -1287,27 +1461,33 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * * @param action Intent action. * @param data Intent data, or <code>null</code>. - * @param query Intent query, or <code>null</code>. * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. + * @param query Intent query, or <code>null</code>. + * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>. * @param actionKey The key code of the action key that was pressed, * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. * @param actionMsg The message for the action key that was pressed, * or <code>null</code> if none. * @return The intent. */ - private Intent createIntent(String action, Uri data, String query, String extraData, - int actionKey, String actionMsg) { + private Intent createIntent(String action, Uri data, String extraData, String query, + String componentName, int actionKey, String actionMsg) { // Now build the Intent Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (data != null) { intent.setData(data); } + intent.putExtra(SearchManager.USER_QUERY, mUserQuery); if (query != null) { intent.putExtra(SearchManager.QUERY, query); } if (extraData != null) { intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); } + if (componentName != null) { + intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName); + } if (mAppSearchData != null) { intent.putExtra(SearchManager.APP_DATA, mAppSearchData); } @@ -1383,20 +1563,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private boolean isEmpty() { return TextUtils.getTrimmedLength(getText()) == 0; } - + /** - * Clears the entered text. + * We override this method to avoid replacing the query box text + * when a suggestion is clicked. */ - private void clear() { - setText(""); + @Override + protected void replaceText(CharSequence text) { } /** - * We override this method to avoid replacing the query box text - * when a suggestion is clicked. + * We override this method to avoid an extra onItemClick being called on the + * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} + * when an item is clicked with the trackball. */ @Override - protected void replaceText(CharSequence text) { + public void performCompletion() { } /** diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 3bf37c3b2b01..e5ba6a4a03c7 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.server.search.SearchableInfo; +import android.util.Log; import android.view.KeyEvent; import java.util.List; @@ -1108,6 +1109,10 @@ import java.util.List; public class SearchManager implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener { + + private static final boolean DBG = false; + private static final String TAG = "SearchManager"; + /** * This is a shortcut definition for the default menu key to use for invoking search. * @@ -1131,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()} @@ -1148,7 +1167,7 @@ public class SearchManager * @hide */ public final static String SOURCE = "source"; - + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} @@ -1160,12 +1179,66 @@ public class SearchManager public final static String ACTION_KEY = "action_key"; /** + * Intent component name key: This key will be used for the extra populated by the + * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column. + * + * {@hide} + */ + public final static String COMPONENT_NAME_KEY = "intent_component_name_key"; + + /** * Intent extra data key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column. + * * {@hide} */ public final static String EXTRA_DATA_KEY = "intent_extra_data_key"; - + + /** + * Defines the constants used in the communication between {@link android.app.SearchDialog} and + * the global search provider via {@link Cursor#respond(android.os.Bundle)}. + * + * @hide + */ + public static class DialogCursorProtocol { + + /** + * The sent bundle will contain this integer key, with a value set to one of the events + * below. + */ + public final static String METHOD = "DialogCursorProtocol.method"; + + /** + * After data has been refreshed. + */ + public final static int POST_REFRESH = 0; + public final static String POST_REFRESH_RECEIVE_ISPENDING + = "DialogCursorProtocol.POST_REFRESH.isPending"; + public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY + = "DialogCursorProtocol.POST_REFRESH.displayNotify"; + + /** + * Just before closing the cursor. + */ + public final static int PRE_CLOSE = 1; + public final static String PRE_CLOSE_SEND_MAX_DISPLAY_POS + = "DialogCursorProtocol.PRE_CLOSE.sendDisplayPosition"; + + /** + * When a position has been clicked. + */ + public final static int CLICK = 2; + public final static String CLICK_SEND_POSITION + = "DialogCursorProtocol.CLICK.sendPosition"; + public final static String CLICK_RECEIVE_SELECTED_POS + = "DialogCursorProtocol.CLICK.receiveSelectedPosition"; + + /** + * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed. + */ + public final static int THRESH_HIT = 3; + } + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()} @@ -1210,6 +1283,41 @@ public class SearchManager */ public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; + + + /** + * The authority of the provider to report clicks to when a click is detected after pivoting + * into a specific app's search from global search. + * + * In addition to the columns below, the suggestion columns are used to pass along the full + * suggestion so it can be shortcutted. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_AUTHORITY = + "com.android.globalsearch.stats"; + + /** + * The path the write goes to. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; + + /** + * The column storing the query for the click. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; + + /** + * The column storing the component name of the application that was pivoted into. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; + /** * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i> */ @@ -1258,28 +1366,6 @@ public class SearchManager */ public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2"; /** - * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, - * then all suggestions will be provided in a format that includes space for two small icons, - * one at the left and one at the right of each suggestion. The data in the column must - * be a blob that contains a bitmap. - * - * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_1} column. - * - * @hide - */ - public final static String SUGGEST_COLUMN_ICON_1_BITMAP = "suggest_icon_1_bitmap"; - /** - * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, - * then all suggestions will be provided in a format that includes space for two small icons, - * one at the left and one at the right of each suggestion. The data in the column must - * be a blob that contains a bitmap. - * - * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_2} column. - * - * @hide - */ - public final static String SUGGEST_COLUMN_ICON_2_BITMAP = "suggest_icon_2_bitmap"; - /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, this is the action that will be used when * forming the suggestion's intent. If the element is not provided, the action will be taken @@ -1300,13 +1386,24 @@ public class SearchManager */ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; /** + * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> + * this element exists at the given row, this is the data that will be used when + * forming the suggestion's intent. If not provided, the Intent's extra data field will be null. + * This column allows suggestions to provide additional arbitrary data which will be included as + * an extra under the key EXTRA_DATA_KEY. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + /** * Column name for suggestions cursor. <i>Optional.</i> This column allows suggestions * to provide additional arbitrary data which will be included as an extra under the key - * {@link #EXTRA_DATA_KEY}. - * - * @hide pending API council approval + * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers + * attempt to use this column, the value will be overwritten by global search. + * + * @hide */ - public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component"; /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, then "/" and this value will be appended to the data @@ -1335,6 +1432,25 @@ public class SearchManager public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id"; /** + * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify the + * cursor item's background color if it needs a non-default background color. A non-zero value + * indicates a valid background color to override the default. + * + * @hide For internal use, not part of the public API. + */ + public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify + * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion + * is being refreshed. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = + "suggest_spinner_while_refreshing"; + + /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion * should not be stored as a shortcut in global search. * @@ -1362,21 +1478,7 @@ public class SearchManager */ public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE = "android.search.action.CHANGE_SEARCH_SOURCE"; - - /** - * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, - * the search dialog will call {@link Cursor#respond(Bundle)} when the - * suggestion is clicked. - * - * The {@link Bundle} argument will be constructed - * in the same way as the "extra" bundle included in an Intent constructed - * from the suggestion. - * - * @hide Pending API council approval. - */ - public final static String INTENT_ACTION_CURSOR_RESPOND - = "android.search.action.CURSOR_RESPOND"; - + /** * Intent action for finding the global search activity. * The global search provider should handle this intent. @@ -1396,21 +1498,53 @@ public class SearchManager = "android.search.action.SEARCH_SETTINGS"; /** + * Intent action for starting a web search provider's settings activity. + * Web search providers should handle this intent if they have provider-specific + * settings to implement. + * + * @hide Pending API council approval. + */ + public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS + = "android.search.action.WEB_SEARCH_SETTINGS"; + + /** + * Intent action broadcasted to inform that the searchables list or default have changed. + * Components should handle this intent if they cache any searchable data and wish to stay + * up to date on changes. + * + * @hide Pending API council approval. + */ + public final static String INTENT_ACTION_SEARCHABLES_CHANGED + = "android.search.action.SEARCHABLES_CHANGED"; + + /** + * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, + * the search dialog will take no action. + * + * @hide + */ + public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH"; + + /** * Reference to the shared system search service. */ - private static ISearchManager sService = getSearchManagerService(); + private static ISearchManager mService; private final Context mContext; - private final Handler mHandler; - - private SearchDialog mSearchDialog; - - private OnDismissListener mDismissListener = null; - private OnCancelListener mCancelListener = null; + + // package private since they are used by the inner class SearchManagerCallback + /* package */ boolean mIsShowing = false; + /* package */ final Handler mHandler; + /* package */ OnDismissListener mDismissListener = null; + /* package */ OnCancelListener mCancelListener = null; + + private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback(); /*package*/ SearchManager(Context context, Handler handler) { mContext = context; mHandler = handler; + mService = ISearchManager.Stub.asInterface( + ServiceManager.getService(Context.SEARCH_SERVICE)); } /** @@ -1458,17 +1592,16 @@ public class SearchManager ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) { - - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); + if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing); + if (mIsShowing) return; + try { + mIsShowing = true; + // activate the search manager and start it up! + mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch, mSearchManagerCallback); + } catch (RemoteException ex) { + Log.e(TAG, "startSearch() failed: " + ex); } - - // activate the search manager and start it up! - mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, - globalSearch); - - mSearchDialog.setOnCancelListener(this); - mSearchDialog.setOnDismissListener(this); } /** @@ -1482,9 +1615,16 @@ public class SearchManager * * @see #startSearch */ - public void stopSearch() { - if (mSearchDialog != null) { - mSearchDialog.cancel(); + public void stopSearch() { + if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + mService.stopSearch(); + // onDismiss will also clear this, but we do it here too since onDismiss() is + // called asynchronously. + mIsShowing = false; + } catch (RemoteException ex) { + Log.e(TAG, "stopSearch() failed: " + ex); } } @@ -1497,33 +1637,33 @@ public class SearchManager * * @hide */ - public boolean isVisible() { - if (mSearchDialog != null) { - return mSearchDialog.isShowing(); - } - return false; + public boolean isVisible() { + if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing); + return mIsShowing; } - + /** - * See {@link #setOnDismissListener} for configuring your activity to monitor search UI state. + * See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor + * search UI state. */ public interface OnDismissListener { /** - * This method will be called when the search UI is dismissed. To make use if it, you must - * implement this method in your activity, and call {@link #setOnDismissListener} to - * register it. + * This method will be called when the search UI is dismissed. To make use of it, you must + * implement this method in your activity, and call + * {@link SearchManager#setOnDismissListener} to register it. */ public void onDismiss(); } /** - * See {@link #setOnCancelListener} for configuring your activity to monitor search UI state. + * See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor + * search UI state. */ public interface OnCancelListener { /** * This method will be called when the search UI is canceled. To make use if it, you must - * implement this method in your activity, and call {@link #setOnCancelListener} to - * register it. + * implement this method in your activity, and call + * {@link SearchManager#setOnCancelListener} to register it. */ public void onCancel(); } @@ -1536,84 +1676,112 @@ public class SearchManager public void setOnDismissListener(final OnDismissListener listener) { mDismissListener = listener; } - - /** - * The callback from the search dialog when dismissed - * @hide - */ - public void onDismiss(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mDismissListener != null) { - mDismissListener.onDismiss(); - } - } - } /** * Set or clear the callback that will be invoked whenever the search UI is canceled. * * @param listener The {@link OnCancelListener} to use, or null. */ - public void setOnCancelListener(final OnCancelListener listener) { + public void setOnCancelListener(OnCancelListener listener) { mCancelListener = listener; } - - - /** - * The callback from the search dialog when canceled - * @hide - */ - public void onCancel(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mCancelListener != null) { - mCancelListener.onCancel(); + + private class SearchManagerCallback extends ISearchManagerCallback.Stub { + + private final Runnable mFireOnDismiss = new Runnable() { + public void run() { + if (DBG) debug("mFireOnDismiss"); + mIsShowing = false; + if (mDismissListener != null) { + mDismissListener.onDismiss(); + } + } + }; + + private final Runnable mFireOnCancel = new Runnable() { + public void run() { + if (DBG) debug("mFireOnCancel"); + // doesn't need to clear mIsShowing since onDismiss() always gets called too + if (mCancelListener != null) { + mCancelListener.onCancel(); + } } + }; + + public void onDismiss() { + if (DBG) debug("onDismiss()"); + mHandler.post(mFireOnDismiss); + } + + public void onCancel() { + if (DBG) debug("onCancel()"); + mHandler.post(mFireOnCancel); } + + } + + // TODO: remove the DialogInterface interfaces from SearchManager. + // This changes the public API, so I'll do it in a separate change. + public void onCancel(DialogInterface dialog) { + throw new UnsupportedOperationException(); + } + public void onDismiss(DialogInterface dialog) { + throw new UnsupportedOperationException(); } /** - * Save instance state so we can recreate after a rotation. - * + * Saves the state of the search UI. + * + * @return A Bundle containing the state of the search dialog, or {@code null} + * if the search UI is not visible. + * * @hide */ - void saveSearchDialog(Bundle outState, String key) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - Bundle searchDialogState = mSearchDialog.onSaveInstanceState(); - outState.putBundle(key, searchDialogState); + public Bundle saveSearchDialog() { + if (DBG) debug("saveSearchDialog(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return null; + try { + return mService.onSaveInstanceState(); + } catch (RemoteException ex) { + Log.e(TAG, "onSaveInstanceState() failed: " + ex); + return null; } } /** - * Restore instance state after a rotation. - * + * Restores the state of the search dialog. + * + * @param searchDialogState Bundle to read the state from. + * * @hide */ - void restoreSearchDialog(Bundle inState, String key) { - Bundle searchDialogState = inState.getBundle(key); - if (searchDialogState != null) { - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); - } - mSearchDialog.onRestoreInstanceState(searchDialogState); + public void restoreSearchDialog(Bundle searchDialogState) { + if (DBG) debug("restoreSearchDialog(" + searchDialogState + ")"); + if (searchDialogState == null) return; + try { + mService.onRestoreInstanceState(searchDialogState); + } catch (RemoteException ex) { + Log.e(TAG, "onRestoreInstanceState() failed: " + ex); } } - + /** - * Hook for updating layout on a rotation - * + * Update the search dialog after a configuration change. + * + * @param newConfig The new configuration. + * * @hide */ - void onConfigurationChanged(Configuration newConfig) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - mSearchDialog.onConfigurationChanged(newConfig); + public void onConfigurationChanged(Configuration newConfig) { + if (DBG) debug("onConfigurationChanged(" + newConfig + "), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + mService.onConfigurationChanged(newConfig); + } catch (RemoteException ex) { + Log.e(TAG, "onConfigurationChanged() failed:" + ex); } } - - private static ISearchManager getSearchManagerService() { - return ISearchManager.Stub.asInterface( - ServiceManager.getService(Context.SEARCH_SERVICE)); - } - + /** * Gets information about a searchable activity. This method is static so that it can * be used from non-Activity contexts. @@ -1625,11 +1793,12 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static SearchableInfo getSearchableInfo(ComponentName componentName, + public SearchableInfo getSearchableInfo(ComponentName componentName, boolean globalSearch) { try { - return sService.getSearchableInfo(componentName, globalSearch); - } catch (RemoteException e) { + return mService.getSearchableInfo(componentName, globalSearch); + } catch (RemoteException ex) { + Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; } } @@ -1639,23 +1808,22 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static boolean isDefaultSearchable(SearchableInfo searchable) { - SearchableInfo defaultSearchable = SearchManager.getSearchableInfo(null, true); + public boolean isDefaultSearchable(SearchableInfo searchable) { + SearchableInfo defaultSearchable = getSearchableInfo(null, true); return defaultSearchable != null && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity()); } - + /** - * Gets a cursor with search suggestions. This method is static so that it can - * be used from non-Activity context. + * Gets a cursor with search suggestions. * * @param searchable Information about how to get the suggestions. * @param query The search text entered (so far). - * @return a cursor with suggestions, or <code>null</null> the suggestion query failed. - * + * @return a cursor with suggestions, or <code>null</null> the suggestion query failed. + * * @hide because SearchableInfo is not part of the API. */ - public static Cursor getSuggestions(Context context, SearchableInfo searchable, String query) { + public Cursor getSuggestions(SearchableInfo searchable, String query) { if (searchable == null) { return null; } @@ -1694,7 +1862,7 @@ public class SearchManager .build(); // finally, make the query - return context.getContentResolver().query(uri, null, selection, selArgs, null); + return mContext.getContentResolver().query(uri, null, selection, selArgs, null); } /** @@ -1706,11 +1874,65 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static List<SearchableInfo> getSearchablesInGlobalSearch() { + public List<SearchableInfo> getSearchablesInGlobalSearch() { try { - return sService.getSearchablesInGlobalSearch(); + return mService.getSearchablesInGlobalSearch(); } catch (RemoteException e) { + Log.e(TAG, "getSearchablesInGlobalSearch() failed: " + e); return null; } } + + /** + * Returns a list of the searchable activities that handle web searches. + * + * @return a list of all searchable activities that handle + * {@link android.content.Intent#ACTION_WEB_SEARCH}. + * + * @hide because SearchableInfo is not part of the API. + */ + public List<SearchableInfo> getSearchablesForWebSearch() { + try { + return mService.getSearchablesForWebSearch(); + } catch (RemoteException e) { + Log.e(TAG, "getSearchablesForWebSearch() failed: " + e); + return null; + } + } + + /** + * Returns the default searchable activity for web searches. + * + * @return searchable information for the activity handling web searches by default. + * + * @hide because SearchableInfo is not part of the API. + */ + public SearchableInfo getDefaultSearchableForWebSearch() { + try { + return mService.getDefaultSearchableForWebSearch(); + } catch (RemoteException e) { + Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e); + return null; + } + } + + /** + * Sets the default searchable activity for web searches. + * + * @param component Name of the component to set as default activity for web searches. + * + * @hide + */ + public void setDefaultWebSearch(ComponentName component) { + try { + mService.setDefaultWebSearch(component); + } catch (RemoteException e) { + Log.e(TAG, "setDefaultWebSearch() failed: " + e); + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } } diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 6a02fc930a1c..49c94d12f8d0 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -20,62 +20,103 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources.NotFoundException; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.server.search.SearchableInfo; import android.text.Html; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; -import android.widget.CursorAdapter; +import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ResourceCursorAdapter; import android.widget.TextView; +import static android.app.SearchManager.DialogCursorProtocol; + import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.WeakHashMap; /** * Provides the contents for the suggestion drop-down list.in {@link SearchDialog}. - * + * * @hide */ class SuggestionsAdapter extends ResourceCursorAdapter { + private static final boolean DBG = false; private static final String LOG_TAG = "SuggestionsAdapter"; - + + private SearchManager mSearchManager; + private SearchDialog mSearchDialog; private SearchableInfo mSearchable; private Context mProviderContext; private WeakHashMap<String, Drawable> mOutsideDrawablesCache; + private boolean mGlobalSearchMode; - // Cached column indexes, updated when the cursor changes. + // Cached column indexes, updated when the cursor changes. private int mFormatCol; private int mText1Col; private int mText2Col; private int mIconName1Col; private int mIconName2Col; - private int mIconBitmap1Col; - private int mIconBitmap2Col; - - public SuggestionsAdapter(Context context, SearchableInfo searchable, - WeakHashMap<String, Drawable> outsideDrawablesCache) { + private int mBackgroundColorCol; + + // This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether + // a particular list item should be selected upon the next call to notifyDataSetChanged. + // This is used to indicate the index of the "More results..." list item so that when + // the data set changes after a click of "More results...", we can correctly tell the + // ListView to scroll to the right line item. It gets reset to NONE every time it + // is consumed. + private int mListItemToSelect = NONE; + static final int NONE = -1; + + // holds the maximum position that has been displayed to the user + int mMaxDisplayed = NONE; + + // holds the position that, when displayed, should result in notifying the cursor + int mDisplayNotifyPos = NONE; + + private final Runnable mStartSpinnerRunnable; + private final Runnable mStopSpinnerRunnable; + + public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable, + WeakHashMap<String, Drawable> outsideDrawablesCache, boolean globalSearchMode) { super(context, com.android.internal.R.layout.search_dropdown_item_icons_2line, null, // no initial cursor true); // auto-requery + mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); + mSearchDialog = searchDialog; mSearchable = searchable; - + // set up provider resources (gives us icons, etc.) Context activityContext = mSearchable.getActivityContext(mContext); mProviderContext = mSearchable.getProviderContext(mContext, activityContext); - + mOutsideDrawablesCache = outsideDrawablesCache; + mGlobalSearchMode = globalSearchMode; + + mStartSpinnerRunnable = new Runnable() { + public void run() { + mSearchDialog.setWorking(true); + } + }; + + mStopSpinnerRunnable = new Runnable() { + public void run() { + mSearchDialog.setWorking(false); + } + }; } - + /** * Overridden to always return <code>false</code>, since we cannot be sure that * suggestion sources return stable IDs. @@ -94,20 +135,41 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public Cursor runQueryOnBackgroundThread(CharSequence constraint) { if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")"); String query = (constraint == null) ? "" : constraint.toString(); + if (!mGlobalSearchMode) { + /** + * for in app search we show the progress spinner until the cursor is returned with + * the results. for global search we manage the progress bar using + * {@link DialogCursorProtocol#POST_REFRESH_RECEIVE_ISPENDING}. + */ + mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); + } try { - return SearchManager.getSuggestions(mContext, mSearchable, query); + 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 != null) cursor.getCount(); + return cursor; } catch (RuntimeException e) { Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); return null; + } finally { + if (!mGlobalSearchMode) { + mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); + } } } - + /** * Cache columns. */ @Override public void changeCursor(Cursor c) { if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")"); + + if (mCursor != null) { + callCursorPreClose(mCursor); + } + super.changeCursor(c); if (c != null) { mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT); @@ -115,21 +177,86 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); - mIconBitmap1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1_BITMAP); - mIconBitmap2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2_BITMAP); + mBackgroundColorCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR); + } + } + + /** + * Handle sending and receiving information associated with + * {@link DialogCursorProtocol#PRE_CLOSE}. + * + * @param cursor The cursor to call. + */ + private void callCursorPreClose(Cursor cursor) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.PRE_CLOSE); + request.putInt(DialogCursorProtocol.PRE_CLOSE_SEND_MAX_DISPLAY_POS, mMaxDisplayed); + final Bundle response = cursor.respond(request); + + mMaxDisplayed = -1; + } + + @Override + public void notifyDataSetChanged() { + if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged"); + super.notifyDataSetChanged(); + + callCursorPostRefresh(mCursor); + + // look out for the pending item we are supposed to scroll to + if (mListItemToSelect != NONE) { + mSearchDialog.setListSelection(mListItemToSelect); + mListItemToSelect = NONE; } } - + + /** + * Handle sending and receiving information associated with + * {@link DialogCursorProtocol#POST_REFRESH}. + * + * @param cursor The cursor to call. + */ + private void callCursorPostRefresh(Cursor cursor) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH); + final Bundle response = cursor.respond(request); + + mSearchDialog.setWorking( + response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false)); + + mDisplayNotifyPos = + response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1); + } + + /** + * Tell the cursor which position was clicked, handling sending and receiving information + * associated with {@link DialogCursorProtocol#CLICK}. + * + * @param cursor The cursor + * @param position The position that was clicked. + */ + void callCursorOnClick(Cursor cursor, int position) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(1); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK); + request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position); + final Bundle response = cursor.respond(request); + mListItemToSelect = response.getInt( + DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE); + } + /** * Tags the view with cached child view look-ups. */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View v = super.newView(context, cursor, parent); + View v = new SuggestionItemView(context, cursor); v.setTag(new ChildViewCache(v)); return v; } - + /** * Cache of the child views of drop-drown list items, to avoid looking up the children * each time the contents of a list item are changed. @@ -139,7 +266,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public final TextView mText2; public final ImageView mIcon1; public final ImageView mIcon2; - + public ChildViewCache(View v) { mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1); mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2); @@ -147,21 +274,38 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2); } } - + @Override public void bindView(View view, Context context, Cursor cursor) { ChildViewCache views = (ChildViewCache) view.getTag(); - boolean isHtml = false; - if (mFormatCol >= 0) { - String format = cursor.getString(mFormatCol); - isHtml = "html".equals(format); + final int pos = cursor.getPosition(); + + // update the maximum position displayed since last refresh + if (pos > mMaxDisplayed) { + mMaxDisplayed = pos; } + + // if the cursor wishes to be notified about this position, send it + if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) { + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT); + mCursor.respond(request); + mDisplayNotifyPos = NONE; // only notify the first time + } + + int backgroundColor = 0; + if (mBackgroundColorCol != -1) { + backgroundColor = cursor.getInt(mBackgroundColorCol); + } + ((SuggestionItemView)view).setColor(backgroundColor); + + final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol)); setViewText(cursor, views.mText1, mText1Col, isHtml); setViewText(cursor, views.mText2, mText2Col, isHtml); - setViewIcon(cursor, views.mIcon1, mIconBitmap1Col, mIconName1Col); - setViewIcon(cursor, views.mIcon2, mIconBitmap2Col, mIconName2Col); + setViewIcon(cursor, views.mIcon1, mIconName1Col); + setViewIcon(cursor, views.mIcon2, mIconName2Col); } - + private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) { if (v == null) { return; @@ -173,49 +317,46 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } // Set the text even if it's null, since we need to clear any previous text. v.setText(text); - + if (TextUtils.isEmpty(text)) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); } } - - private void setViewIcon(Cursor cursor, ImageView v, int iconBitmapCol, int iconNameCol) { + + private void setViewIcon(Cursor cursor, ImageView v, int iconNameCol) { if (v == null) { return; } - Drawable drawable = null; - // First try the bitmap column - if (iconBitmapCol >= 0) { - byte[] data = cursor.getBlob(iconBitmapCol); - if (data != null) { - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap != null) { - drawable = new BitmapDrawable(bitmap); - } - } - } - // If there was no bitmap, try the icon resource column. - if (drawable == null && iconNameCol >= 0) { - String value = cursor.getString(iconNameCol); - drawable = getDrawableFromResourceValue(value); + if (iconNameCol < 0) { + return; } + String value = cursor.getString(iconNameCol); + Drawable drawable = getDrawableFromResourceValue(value); // Set the icon even if the drawable is null, since we need to clear any // previous icon. v.setImageDrawable(drawable); - + if (drawable == null) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); + + // This is a hack to get any animated drawables (like a 'working' spinner) + // to animate. You have to setVisible true on an AnimationDrawable to get + // it to start animating, but it must first have been false or else the + // call to setVisible will be ineffective. We need to clear up the story + // about animated drawables in the future, see http://b/1878430. + drawable.setVisible(false, false); + drawable.setVisible(true, false); } } - + /** * Gets the text to show in the query field when a suggestion is selected. - * - * @param cursor The Cursor to read the suggestion data from. The Cursor should already + * + * @param cursor The Cursor to read the suggestion data from. The Cursor should already * be moved to the suggestion that is to be read from. * @return The text to show, or <code>null</code> if the query should not be * changed when selecting this suggestion. @@ -225,36 +366,36 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (cursor == null) { return null; } - + String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY); if (query != null) { return query; } - + if (mSearchable.shouldRewriteQueryFromData()) { String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA); if (data != null) { return data; } } - + if (mSearchable.shouldRewriteQueryFromText()) { String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1); if (text1 != null) { return text1; } } - + return null; } - + /** * This method is overridden purely to provide a bit of protection against * flaky content providers. - * + * * @see android.widget.ListAdapter#getView(int, View, ViewGroup) */ - @Override + @Override public View getView(int position, View convertView, ViewGroup parent) { try { return super.getView(position, convertView, parent); @@ -263,28 +404,28 @@ class SuggestionsAdapter extends ResourceCursorAdapter { // Put exception string in item title View v = newView(mContext, mCursor, parent); if (v != null) { - ChildViewCache views = (ChildViewCache) v.getTag(); + ChildViewCache views = (ChildViewCache) v.getTag(); TextView tv = views.mText1; tv.setText(e.toString()); } return v; } } - + /** * Gets a drawable given a value provided by a suggestion provider. - * + * * This value could be just the string value of a resource id * (e.g., "2130837524"), in which case we will try to retrieve a drawable from * the provider's resources. If the value is not an integer, it is - * treated as a Uri and opened with + * treated as a Uri and opened with * {@link ContentResolver#openOutputStream(android.net.Uri, String)}. * * All resources and URIs are read using the suggestion provider's context. * * If the string is not formatted as expected, or no drawable can be found for * the provided value, this method returns null. - * + * * @param drawableId a string like "2130837524", * "android.resource://com.android.alarmclock/2130837524", * or "content://contacts/photos/253". @@ -294,43 +435,58 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) { return null; } - + // First, check the cache. Drawable drawable = mOutsideDrawablesCache.get(drawableId); - if (drawable != null) return drawable; + if (drawable != null) { + if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId); + return drawable; + } try { // Not cached, try using it as a plain resource ID in the provider's context. int resourceId = Integer.parseInt(drawableId); drawable = mProviderContext.getResources().getDrawable(resourceId); + if (DBG) Log.d(LOG_TAG, "Found icon by resource ID: " + drawableId); } catch (NumberFormatException nfe) { // The id was not an integer resource id. // Let the ContentResolver handle content, android.resource and file URIs. try { Uri uri = Uri.parse(drawableId); - drawable = Drawable.createFromStream( - mProviderContext.getContentResolver().openInputStream(uri), - null); + InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); + if (stream != null) { + try { + drawable = Drawable.createFromStream(stream, null); + } finally { + try { + stream.close(); + } catch (IOException ex) { + Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); + } + } + } + if (DBG) Log.d(LOG_TAG, "Opened icon input stream: " + drawableId); } catch (FileNotFoundException fnfe) { + if (DBG) Log.d(LOG_TAG, "Icon stream not found: " + drawableId); // drawable = null; } - + // If we got a drawable for this resource id, then stick it in the // map so we don't do this lookup again. if (drawable != null) { mOutsideDrawablesCache.put(drawableId, drawable); } } catch (NotFoundException nfe) { - // Resource could not be found + if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId); // drawable = null; } - + return drawable; } - + /** * Gets the value of a string column by name. - * + * * @param cursor Cursor to read the value from. * @param columnName The name of the column to read. * @return The value of the given column, or <code>null</null> @@ -338,10 +494,75 @@ class SuggestionsAdapter extends ResourceCursorAdapter { */ public static String getColumnString(Cursor cursor, String columnName) { int col = cursor.getColumnIndex(columnName); - if (col == -1) { + if (col == NONE) { return null; } return cursor.getString(col); } + /** + * A parent viewgroup class which holds the actual suggestion item as a child. + * + * The sole purpose of this class is to draw the given background color when the item is in + * normal state and not draw the background color when it is pressed, so that when pressed the + * list view's selection highlight will be displayed properly (if we draw our background it + * draws on top of the list view selection highlight). + */ + private class SuggestionItemView extends ViewGroup { + private int mBackgroundColor; // the background color to draw in normal state. + private View mView; // the suggestion item's view. + + protected SuggestionItemView(Context context, Cursor cursor) { + // Initialize ourselves + super(context); + mBackgroundColor = 0; // transparent by default. + + // For our layout use the default list item height from the current theme. + TypedValue lineHeight = new TypedValue(); + context.getTheme().resolveAttribute( + com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + AbsListView.LayoutParams layout = new AbsListView.LayoutParams( + AbsListView.LayoutParams.FILL_PARENT, + (int)lineHeight.getDimension(metrics)); + + setLayoutParams(layout); + + // Initialize the child view + mView = SuggestionsAdapter.super.newView(context, cursor, this); + if (mView != null) { + addView(mView, layout.width, layout.height); + mView.setVisibility(View.VISIBLE); + } + } + + public void setColor(int backgroundColor) { + mBackgroundColor = backgroundColor; + } + + @Override + public void dispatchDraw(Canvas canvas) { + if (mBackgroundColor != 0 && !isPressed() && !isSelected()) { + canvas.drawColor(mBackgroundColor); + } + super.dispatchDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mView != null) { + mView.measure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mView != null) { + mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); + } + } + } + } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 10c2b02d4c35..03e8623a63de 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -26,7 +26,6 @@ import android.widget.RemoteViews; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; @@ -40,7 +39,7 @@ public class AppWidgetHost { static final int HANDLE_UPDATE = 1; static final int HANDLE_PROVIDER_CHANGED = 2; - static Object sServiceLock = new Object(); + final static Object sServiceLock = new Object(); static IAppWidgetService sService; Context mContext; @@ -85,7 +84,7 @@ public class AppWidgetHost { int mHostId; Callbacks mCallbacks = new Callbacks(); - HashMap<Integer,AppWidgetHostView> mViews = new HashMap(); + final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); public AppWidgetHost(Context context, int hostId) { mContext = context; @@ -104,8 +103,8 @@ public class AppWidgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { - int[] updatedIds = null; - ArrayList<RemoteViews> updatedViews = new ArrayList(); + int[] updatedIds; + ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>(); try { if (mPackageName == null) { @@ -209,7 +208,7 @@ public class AppWidgetHost { synchronized (mViews) { mViews.put(appWidgetId, view); } - RemoteViews views = null; + RemoteViews views; try { views = sService.getAppWidgetViews(appWidgetId); } catch (RemoteException e) { @@ -231,6 +230,7 @@ public class AppWidgetHost { /** * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. */ + @SuppressWarnings({"UnusedDeclaration"}) protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index be0f96e8ab6a..62d92674d864 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -22,16 +22,12 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.os.Handler; -import android.os.Message; import android.os.SystemClock; -import android.util.Config; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; @@ -86,6 +82,7 @@ public class AppWidgetHostView extends FrameLayout { * @param animationIn Resource ID of in animation to use * @param animationOut Resource ID of out animation to use */ + @SuppressWarnings({"UnusedDeclaration"}) public AppWidgetHostView(Context context, int animationIn, int animationOut) { super(context); mContext = context; @@ -272,7 +269,7 @@ public class AppWidgetHostView extends FrameLayout { try { if (mInfo != null) { Context theirContext = mContext.createPackageContext( - mInfo.provider.getPackageName(), 0 /* no flags */); + mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED); LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(theirContext); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index eca04b311330..3660001b1968 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -21,7 +21,9 @@ import android.content.Context; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetService; @@ -187,6 +189,8 @@ public class AppWidgetManager { Context mContext; + private DisplayMetrics mDisplayMetrics; + /** * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context * Context} object. @@ -213,6 +217,7 @@ public class AppWidgetManager { private AppWidgetManager(Context context) { mContext = context; + mDisplayMetrics = context.getResources().getDisplayMetrics(); } /** @@ -292,7 +297,15 @@ public class AppWidgetManager { */ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { try { - return sService.getAppWidgetInfo(appWidgetId); + AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId); + if (info != null) { + // Converting complex to dp. + info.minWidth = + TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics); + info.minHeight = + TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics); + } + return info; } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java new file mode 100644 index 000000000000..ab246754b5af --- /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/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java new file mode 100644 index 000000000000..69c206ce1c81 --- /dev/null +++ b/core/java/android/backup/BackupDataInput.java @@ -0,0 +1,117 @@ +/* + * 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 java.io.FileDescriptor; +import java.io.IOException; + +/** @hide */ +public class BackupDataInput { + int mBackupReader; + + private EntityHeader mHeader = new EntityHeader(); + private boolean mHeaderReady; + + private static class EntityHeader { + String key; + int dataSize; + } + + public BackupDataInput(FileDescriptor fd) { + if (fd == null) throw new NullPointerException(); + mBackupReader = ctor(fd); + if (mBackupReader == 0) { + throw new RuntimeException("Native initialization failed with fd=" + fd); + } + } + + protected void finalize() throws Throwable { + try { + dtor(mBackupReader); + } finally { + super.finalize(); + } + } + + public boolean readNextHeader() throws IOException { + int result = readNextHeader_native(mBackupReader, mHeader); + if (result == 0) { + // read successfully + mHeaderReady = true; + return true; + } else if (result > 0) { + // done + mHeaderReady = false; + return false; + } else { + // error + mHeaderReady = false; + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public String getKey() { + if (mHeaderReady) { + return mHeader.key; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int getDataSize() { + if (mHeaderReady) { + return mHeader.dataSize; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int readEntityData(byte[] data, int offset, int size) throws IOException { + if (mHeaderReady) { + int result = readEntityData_native(mBackupReader, data, offset, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public void skipEntityData() throws IOException { + if (mHeaderReady) { + int result = skipEntityData_native(mBackupReader); + if (result >= 0) { + return; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + private native static int ctor(FileDescriptor fd); + private native static void dtor(int mBackupReader); + + private native int readNextHeader_native(int mBackupReader, EntityHeader entity); + private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); + private native int skipEntityData_native(int mBackupReader); +} diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java new file mode 100644 index 000000000000..b705c4c36022 --- /dev/null +++ b/core/java/android/backup/BackupDataInputStream.java @@ -0,0 +1,63 @@ +/* + * 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.util.Log; + +import java.io.InputStream; +import java.io.IOException; + +/** @hide */ +public class BackupDataInputStream extends InputStream { + + String key; + int dataSize; + + BackupDataInput mData; + byte[] mOneByte; + + BackupDataInputStream(BackupDataInput data) { + mData = data; + } + + public int read() throws IOException { + byte[] one = mOneByte; + if (mOneByte == null) { + one = mOneByte = new byte[1]; + } + mData.readEntityData(one, 0, 1); + return one[0]; + } + + public int read(byte[] b, int offset, int size) throws IOException { + return mData.readEntityData(b, offset, size); + } + + public int read(byte[] b) throws IOException { + return mData.readEntityData(b, 0, b.length); + } + + public String getKey() { + return this.key; + } + + public int size() { + return this.dataSize; + } +} + + diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 555494e59524..d29c5ba02f14 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -19,27 +19,59 @@ package android.backup; import android.content.Context; import java.io.FileDescriptor; +import java.io.IOException; /** @hide */ public class BackupDataOutput { - /* package */ FileDescriptor fd; + int mBackupWriter; public static final int OP_UPDATE = 1; public static final int OP_DELETE = 2; - public BackupDataOutput(Context context, FileDescriptor fd) { - this.fd = fd; + public BackupDataOutput(FileDescriptor fd) { + if (fd == null) throw new NullPointerException(); + mBackupWriter = ctor(fd); + if (mBackupWriter == 0) { + throw new RuntimeException("Native initialization failed with fd=" + fd); + } } - public void close() { - // do we close the fd? + // A dataSize of -1 indicates that the record under this key should be deleted + public int writeEntityHeader(String key, int dataSize) throws IOException { + int result = writeEntityHeader_native(mBackupWriter, key, dataSize); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } } - public native void flush(); - public native void write(byte[] buffer); - public native void write(int oneByte); - public native void write(byte[] buffer, int offset, int count); - public native void writeOperation(int op); - public native void writeKey(String key); + public int writeEntityData(byte[] data, int size) throws IOException { + int result = writeEntityData_native(mBackupWriter, data, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public void setKeyPrefix(String keyPrefix) { + setKeyPrefix_native(mBackupWriter, keyPrefix); + } + + protected void finalize() throws Throwable { + try { + dtor(mBackupWriter); + } finally { + super.finalize(); + } + } + + private native static int ctor(FileDescriptor fd); + private native static void dtor(int mBackupWriter); + + private native static int writeEntityHeader_native(int mBackupWriter, String key, int dataSize); + private native static int writeEntityData_native(int mBackupWriter, byte[] data, int size); + private native static void setKeyPrefix_native(int mBackupWriter, String keyPrefix); } diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java new file mode 100644 index 000000000000..3983e28cce39 --- /dev/null +++ b/core/java/android/backup/BackupHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.backup; + +import android.os.ParcelFileDescriptor; + +import java.io.InputStream; + +/** @hide */ +public interface BackupHelper { + /** + * 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); + + /** + * Called by BackupHelperDispatcher to dispatch one entity of data. + * <p class=note> + * Do not close the <code>data</code> stream. Do not read more than + * <code>dataSize</code> bytes from <code>data</code>. + */ + public void restoreEntity(BackupDataInputStream data); + + /** + * + */ + public void writeRestoreSnapshot(ParcelFileDescriptor fd); +} + diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java new file mode 100644 index 000000000000..5d0c4a2514db --- /dev/null +++ b/core/java/android/backup/BackupHelperAgent.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package android.backup; + +import android.app.BackupAgent; +import android.backup.BackupHelper; +import android.backup.BackupHelperDispatcher; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.IOException; + +/** @hide */ +public class BackupHelperAgent extends BackupAgent { + static final String TAG = "BackupHelperAgent"; + + BackupHelperDispatcher mDispatcher = new BackupHelperDispatcher(); + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + mDispatcher.performBackup(oldState, data, newState); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + mDispatcher.performRestore(data, appVersionCode, newState); + } + + public BackupHelperDispatcher getDispatcher() { + return mDispatcher; + } + + public void addHelper(String keyPrefix, BackupHelper helper) { + mDispatcher.addHelper(keyPrefix, helper); + } +} + + diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java new file mode 100644 index 000000000000..6ccb83effd45 --- /dev/null +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -0,0 +1,151 @@ +/* + * 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.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; + +/** @hide */ +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() { + } + + public void addHelper(String keyPrefix, BackupHelper helper) { + mHelpers.put(keyPrefix, helper); + } + + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + 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 + ")"); + } + } + + public void performRestore(BackupDataInput input, int appVersionCode, + ParcelFileDescriptor newState) + throws IOException { + boolean alreadyComplained = false; + + BackupDataInputStream stream = new BackupDataInputStream(input); + while (input.readNextHeader()) { + + String rawKey = input.getKey(); + int pos = rawKey.indexOf(':'); + if (pos > 0) { + String prefix = rawKey.substring(0, pos); + BackupHelper helper = mHelpers.get(prefix); + if (helper != null) { + stream.dataSize = input.getDataSize(); + stream.key = rawKey.substring(pos+1); + helper.restoreEntity(stream); + } else { + if (!alreadyComplained) { + Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'"); + alreadyComplained = true; + } + } + } else { + if (!alreadyComplained) { + Log.w(TAG, "Entity with no prefix: '" + rawKey + "'"); + alreadyComplained = true; + } + } + input.skipEntityData(); // In case they didn't consume the data. + } + + // Write out the state files -- mHelpers is a TreeMap, so the order is well defined. + for (BackupHelper helper: mHelpers.values()) { + 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 6f0b2eef603f..34a1a0c8f2f4 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -19,6 +19,7 @@ package android.backup; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Log; /** * BackupManager is the interface to the system's backup service. @@ -32,14 +33,24 @@ import android.os.ServiceManager; * until the backup operation actually occurs. * * <p>The backup operation itself begins with the system launching the - * {@link BackupService} subclass declared in your manifest. See the documentation - * for {@link BackupService} for a detailed description of how the backup then proceeds. + * {@link android.app.BackupAgent} subclass declared in your manifest. See the + * documentation for {@link android.app.BackupAgent} for a detailed description + * of how the backup then proceeds. * * @hide pending API solidification */ public class BackupManager { + private static final String TAG = "BackupManager"; + private Context mContext; - private IBackupManager mService; + private static IBackupManager sService; + + private static void checkServiceBinder() { + if (sService == null) { + sService = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + } + } /** * Constructs a BackupManager object through which the application can @@ -51,19 +62,60 @@ public class BackupManager { */ public BackupManager(Context context) { mContext = context; - mService = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); } /** * Notifies the Android backup system that your application wishes to back up * new changes to its data. A backup operation using your application's - * {@link BackupService} subclass will be scheduled when you call this method. + * {@link android.app.BackupAgent} subclass will be scheduled when you call this method. */ public void dataChanged() { - try { - mService.dataChanged(mContext.getPackageName()); - } catch (RemoteException e) { + checkServiceBinder(); + if (sService != null) { + try { + sService.dataChanged(mContext.getPackageName()); + } catch (RemoteException e) { + Log.d(TAG, "dataChanged() couldn't connect"); + } + } + } + + /** + * Convenience method for callers who need to indicate that some other package + * needs a backup pass. This can be relevant in the case of groups of packages + * that share a uid, for example. + * + * This method requires that the application hold the "android.permission.BACKUP" + * permission if the package named in the argument is not the caller's own. + */ + public static void dataChanged(String packageName) { + checkServiceBinder(); + if (sService != null) { + try { + sService.dataChanged(packageName); + } catch (RemoteException e) { + Log.d(TAG, "dataChanged(pkg) couldn't connect"); + } + } + } + + /** + * Begin the process of restoring system data from backup. This method requires + * that the application hold the "android.permission.BACKUP" permission, and is + * not public. + * + * {@hide} + */ + public IRestoreSession beginRestoreSession(String transport) { + IRestoreSession binder = null; + checkServiceBinder(); + if (sService != null) { + try { + binder = sService.beginRestoreSession(transport); + } catch (RemoteException e) { + Log.d(TAG, "beginRestoreSession() couldn't connect"); + } } + return binder; } } diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index 05159dc3bdf2..405849705ff7 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -20,54 +20,53 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.File; import java.io.FileDescriptor; /** @hide */ -public class FileBackupHelper { +public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "FileBackupHelper"; + Context mContext; + File mFilesDir; + String[] mFiles; + + public FileBackupHelper(Context context, String... files) { + super(context); + + mContext = context; + mFilesDir = context.getFilesDir(); + 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 static void performBackup(Context context, - ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState, String[] files) { - String basePath = context.getFilesDir().getAbsolutePath(); - performBackup_checked(basePath, oldState, data, newState, files); - } - - /** - * Check the parameters so the native code doens't have to throw all the exceptions - * since it's easier to do that from java. - */ - static void performBackup_checked(String basePath, - ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState, String[] files) { - if (files.length == 0) { - return; - } - if (basePath == null) { - throw new NullPointerException(); - } - // oldStateFd can be null - FileDescriptor oldStateFd = oldState != null ? oldState.getFileDescriptor() : null; - if (data.fd == null) { - throw new NullPointerException(); - } - FileDescriptor newStateFd = newState.getFileDescriptor(); - if (newStateFd == null) { - throw new NullPointerException(); + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + // file names + String[] files = mFiles; + File base = mContext.getFilesDir(); + final int N = files.length; + String[] fullPaths = new String[N]; + for (int i=0; i<N; i++) { + fullPaths[i] = (new File(base, files[i])).getAbsolutePath(); } - int err = performBackup_native(basePath, oldStateFd, data.fd, newStateFd, files); + // go + performBackup_checked(oldState, data, newState, fullPaths, files); + } - if (err != 0) { - throw new RuntimeException("Backup failed"); // TODO: more here + 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(mFilesDir, key); + writeFile(f, data); } } - - native private static int performBackup_native(String basePath, FileDescriptor oldState, - FileDescriptor data, FileDescriptor newState, String[] files); } + diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/backup/FileBackupHelperBase.java new file mode 100644 index 000000000000..03ae4763fbd0 --- /dev/null +++ b/core/java/android/backup/FileBackupHelperBase.java @@ -0,0 +1,130 @@ +/* + * 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.InputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; + +class FileBackupHelperBase { + private static final String TAG = "RestoreHelperBase"; + + int mPtr; + Context mContext; + boolean mExceptionLogged; + + FileBackupHelperBase(Context context) { + mPtr = ctor(); + mContext = context; + } + + protected void finalize() throws Throwable { + try { + dtor(mPtr); + } finally { + super.finalize(); + } + } + + /** + * Check the parameters so the native code doens't have to throw all the exceptions + * since it's easier to do that from java. + */ + static void performBackup_checked(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState, String[] files, String[] keys) { + if (files.length == 0) { + return; + } + // files must be all absolute paths + for (String f: files) { + if (f.charAt(0) != '/') { + throw new RuntimeException("files must have all absolute paths: " + f); + } + } + // the length of files and keys must be the same + if (files.length != keys.length) { + throw new RuntimeException("files.length=" + files.length + + " keys.length=" + keys.length); + } + // oldStateFd can be null + FileDescriptor oldStateFd = oldState != null ? oldState.getFileDescriptor() : null; + FileDescriptor newStateFd = newState.getFileDescriptor(); + if (newStateFd == null) { + throw new NullPointerException(); + } + + int err = performBackup_native(oldStateFd, data.mBackupWriter, newStateFd, files, keys); + + if (err != 0) { + // TODO: more here + throw new RuntimeException("Backup failed 0x" + Integer.toHexString(err)); + } + } + + void writeFile(File f, InputStream in) { + if (!(in instanceof BackupDataInputStream)) { + throw new IllegalStateException("input stream must be a BackupDataInputStream"); + } + int result = -1; + + // Create the enclosing directory. + File parent = f.getParentFile(); + parent.mkdirs(); + + result = writeFile_native(mPtr, f.getAbsolutePath(), + ((BackupDataInputStream)in).mData.mBackupReader); + if (result != 0) { + // Bail on this entity. Only log one failure per helper object. + if (!mExceptionLogged) { + Log.e(TAG, "Failed restoring file '" + f + "' for app '" + + mContext.getPackageName() + "\' result=0x" + + Integer.toHexString(result)); + mExceptionLogged = true; + } + } + } + + public void writeRestoreSnapshot(ParcelFileDescriptor fd) { + int result = writeSnapshot_native(mPtr, fd.getFileDescriptor()); + // TODO: Do something with the error. + } + + boolean isKeyInList(String key, String[] list) { + for (String s: list) { + if (s.equals(key)) { + return true; + } + } + return false; + } + + private static native int ctor(); + private static native void dtor(int ptr); + + native private static int performBackup_native(FileDescriptor oldState, + int data, FileDescriptor newState, String[] files, String[] keys); + + private static native int writeFile_native(int ptr, String filename, int backupReader); + private static native int writeSnapshot_native(int ptr, FileDescriptor fd); +} + + diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl index cf22798507a8..9d181bec49df 100644 --- a/core/java/android/backup/IBackupManager.aidl +++ b/core/java/android/backup/IBackupManager.aidl @@ -16,6 +16,8 @@ package android.backup; +import android.backup.IRestoreSession; + /** * Direct interface to the Backup Manager Service that applications invoke on. The only * operation currently needed is a simple notification that the app has made changes to @@ -30,12 +32,102 @@ interface IBackupManager { /** * Tell the system service that the caller has made changes to its * data, and therefore needs to undergo an incremental backup pass. + * + * Any application can invoke this method for its own package, but + * only callers who hold the android.permission.BACKUP permission + * may invoke it for arbitrary packages. + */ + void dataChanged(String packageName); + + /** + * Erase all backed-up data for the given package from the storage + * destination. + * + * Any application can invoke this method for its own package, but + * only callers who hold the android.permission.BACKUP permission + * may invoke it for arbitrary packages. + */ + void clearBackupData(String packageName); + + /** + * Notifies the Backup Manager Service that an agent has become available. This + * method is only invoked by the Activity Manager. + */ + void agentConnected(String packageName, IBinder agent); + + /** + * Notify the Backup Manager Service that an agent has unexpectedly gone away. + * This method is only invoked by the Activity Manager. + */ + void agentDisconnected(String packageName); + + /** + * Enable/disable the backup service entirely. When disabled, no backup + * or restore operations will take place. Data-changed notifications will + * still be observed and collected, however, so that changes made while the + * mechanism was disabled will still be backed up properly if it is enabled + * at some point in the future. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupEnabled(boolean isEnabled); + + /** + * Indicate that any necessary one-time provisioning has occurred. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupProvisioned(boolean isProvisioned); + + /** + * Report whether the backup mechanism is currently enabled. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + boolean isBackupEnabled(); + + /** + * Schedule an immediate backup attempt for all pending updates. This is + * primarily intended for transports to use when they detect a suitable + * opportunity for doing a backup pass. If there are no pending updates to + * be sent, no action will be taken. Even if some updates are pending, the + * transport will still be asked to confirm via the usual requestBackupTime() + * method. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + void backupNow(); + + /** + * Identify the currently selected transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + */ + String getCurrentTransport(); + + /** + * Request a list of all available backup transports' names. Callers must + * hold the android.permission.BACKUP permission to use this method. + */ + String[] listAllTransports(); + + /** + * Specify the current backup transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + * + * @param transport The name of the transport to select. This should be one + * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}. + * @return The name of the previously selected transport. If the given transport + * name is not one of the currently available transports, no change is made to + * the current transport setting and the method returns null. */ - oneway void dataChanged(String packageName); + String selectBackupTransport(String transport); /** - * Schedule a full backup of the given package. - * !!! TODO: protect with a signature-or-system permission? + * Begin a restore session with the given transport (which may differ from the + * currently-active backup transport). + * + * @param transport The name of the transport to use for the restore operation. + * @return An interface to the restore session, or null on error. */ - oneway void scheduleFullBackup(String packageName); + IRestoreSession beginRestoreSession(String transportID); } diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl new file mode 100644 index 000000000000..59e59fc1f70a --- /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 new file mode 100644 index 000000000000..2a1fbc179933 --- /dev/null +++ b/core/java/android/backup/IRestoreSession.aidl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.backup; + +import android.backup.RestoreSet; +import android.backup.IRestoreObserver; + +/** + * Binder interface used by clients who wish to manage a restore operation. Every + * method in this interface requires the android.permission.BACKUP permission. + * + * {@hide} + */ +interface IRestoreSession { + /** + * Ask the current transport what the available restore sets are. + * + * @return A bundle containing two elements: an int array under the key + * "tokens" whose entries are a transport-private identifier for each backup set; + * and a String array under the key "names" whose entries are the user-meaningful + * text corresponding to the backup sets at each index in the tokens array. + */ + RestoreSet[] getAvailableRestoreSets(); + + /** + * Restore the given set onto the device, replacing the current data of any app + * contained in the restore set with the data previously backed up. + * + * @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(long token, IRestoreObserver observer); + + /** + * End this restore session. After this method is called, the IRestoreSession binder + * is no longer valid. + */ + void endRestoreSession(); +} diff --git a/core/java/android/backup/RestoreSet.aidl b/core/java/android/backup/RestoreSet.aidl new file mode 100644 index 000000000000..42e77bfe5990 --- /dev/null +++ b/core/java/android/backup/RestoreSet.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable RestoreSet;
\ No newline at end of file diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java new file mode 100644 index 000000000000..eeca148667f3 --- /dev/null +++ b/core/java/android/backup/RestoreSet.java @@ -0,0 +1,87 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +/** + * Descriptive information about a set of backed-up app data available for restore. + * Used by IRestoreSession clients. + * + * @hide + */ +public class RestoreSet implements Parcelable { + /** + * Name of this restore set. May be user generated, may simply be the name + * of the handset model, e.g. "T-Mobile G1". + */ + public String name; + + /** + * Identifier of the device whose data this is. This will be as unique as + * is practically possible; for example, it might be an IMEI. + */ + public String device; + + /** + * Token that identifies this backup set unambiguously to the backup/restore + * transport. This is guaranteed to be valid for the duration of a restore + * session, but is meaningless once the session has ended. + */ + public long token; + + + public RestoreSet() { + // Leave everything zero / null + } + + public RestoreSet(String _name, String _dev, long _token) { + name = _name; + device = _dev; + token = _token; + } + + + // Parcelable implementation + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(name); + out.writeString(device); + out.writeLong(token); + } + + public static final Parcelable.Creator<RestoreSet> CREATOR + = new Parcelable.Creator<RestoreSet>() { + public RestoreSet createFromParcel(Parcel in) { + return new RestoreSet(in); + } + + public RestoreSet[] newArray(int size) { + return new RestoreSet[size]; + } + }; + + private RestoreSet(Parcel in) { + name = in.readString(); + device = in.readString(); + token = in.readLong(); + } +} diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index 8627f08cf57b..4a7b399a5bde 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -18,24 +18,51 @@ package android.backup; import android.content.Context; import android.os.ParcelFileDescriptor; +import android.util.Log; +import java.io.File; import java.io.FileDescriptor; /** @hide */ -public class SharedPreferencesBackupHelper { - public static void performBackup(Context context, - ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot, - BackupDataOutput data, String[] prefGroups) { - String basePath = "/xxx"; //context.getPreferencesDir(); +public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper { + private static final String TAG = "SharedPreferencesBackupHelper"; + private Context mContext; + private String[] mPrefGroups; + + public SharedPreferencesBackupHelper(Context context, String... prefGroups) { + super(context); + + mContext = context; + mPrefGroups = prefGroups; + } + + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + Context context = mContext; + // make filenames for the prefGroups + String[] prefGroups = mPrefGroups; final int N = prefGroups.length; String[] files = new String[N]; for (int i=0; i<N; i++) { - files[i] = prefGroups[i] + ".xml"; + files[i] = context.getSharedPrefsFile(prefGroups[i]).getAbsolutePath(); } - FileBackupHelper.performBackup_checked(basePath, oldSnapshot, data, newSnapshot, files); + // go + performBackup_checked(oldState, data, newState, files, prefGroups); + } + + public void restoreEntity(BackupDataInputStream data) { + Context context = mContext; + + // TODO: turn this off before ship + Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); + String key = data.getKey(); + if (isKeyInList(key, mPrefGroups)) { + File f = context.getSharedPrefsFile(key).getAbsoluteFile(); + writeFile(f, data); + } } } diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index e1984353e88c..fe1e09af984a 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -332,6 +332,31 @@ public class BluetoothHeadset { } /** + * Get battery usage hint for Bluetooth Headset service. + * This is a monotonically increasing integer. Wraps to 0 at + * Integer.MAX_INT, and at boot. + * Current implementation returns the number of AT commands handled since + * boot. This is a good indicator for spammy headset/handsfree units that + * can keep the device awake by polling for cellular status updates. As a + * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms + * @return monotonically increasing battery usage hint, or a negative error + * code on error + * @hide + */ + public int getBatteryUsageHint() { + if (DBG) log("getBatteryUsageHint()"); + if (mService != null) { + try { + return mService.getBatteryUsageHint(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return -1; + } + + /** * Check class bits for possible HSP or HFP support. * This is a simple heuristic that tries to guess if a device with the * given class bits might support HSP or HFP. It is not accurate for all diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java index f31e7a2c65ec..f987ffdd440a 100644 --- a/core/java/android/bluetooth/HeadsetBase.java +++ b/core/java/android/bluetooth/HeadsetBase.java @@ -40,6 +40,8 @@ public class HeadsetBase { public static final int DIRECTION_INCOMING = 1; public static final int DIRECTION_OUTGOING = 2; + private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ + private final BluetoothDevice mBluetooth; private final String mAddress; private final int mRfcommChannel; @@ -109,6 +111,14 @@ public class HeadsetBase { acquireWakeLock(); long timestamp; + synchronized(HeadsetBase.class) { + if (sAtInputCount == Integer.MAX_VALUE) { + sAtInputCount = 0; + } else { + sAtInputCount++; + } + } + if (DBG) timestamp = System.currentTimeMillis(); AtCommandResult result = mAtParser.process(input); if (DBG) Log.d(TAG, "Processing " + input + " took " + @@ -279,7 +289,11 @@ public class HeadsetBase { } } - private void log(String msg) { + public static int getAtInputCount() { + return sAtInputCount; + } + + private static void log(String msg) { Log.d(TAG, msg); } } diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 582d4e340ee1..5f42fd6521b7 100644 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -31,4 +31,5 @@ interface IBluetoothHeadset { boolean stopVoiceRecognition(); boolean setPriority(in String address, int priority); int getPriority(in String address); + int getBatteryUsageHint(); } diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java index ce6501c9f147..249d9babd8a7 100644 --- a/core/java/android/content/AbstractSyncableContentProvider.java +++ b/core/java/android/content/AbstractSyncableContentProvider.java @@ -147,7 +147,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro @Override public boolean onCreate() { if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider"); - mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName); + mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), + mDatabaseName); mSyncState = new SyncStateContentProviderHelper(mOpenHelper); AccountMonitorListener listener = new AccountMonitorListener() { @@ -235,76 +236,147 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro return Collections.emptyList(); } - @Override - public final int update(final Uri url, final ContentValues values, - final String selection, final String[] selectionArgs) { + /** + * <p> + * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction(). + * {@link #endTransaction} MUST be called after calling this method. + * Those methods should be used like this: + * </p> + * + * <pre class="prettyprint"> + * boolean successful = false; + * beginTransaction(); + * try { + * // Do something related to mDb + * successful = true; + * return ret; + * } finally { + * endTransaction(successful); + * } + * </pre> + * + * @hide This method is dangerous from the view of database manipulation, though using + * this makes batch insertion/update/delete much faster. + */ + public final void beginTransaction() { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); + } + + /** + * <p> + * Call mDb.endTransaction(). If successful is true, try to call + * mDb.setTransactionSuccessful() before calling mDb.endTransaction(). + * This method MUST be used with {@link #beginTransaction()}. + * </p> + * + * @hide This method is dangerous from the view of database manipulation, though using + * this makes batch insertion/update/delete much faster. + */ + public final void endTransaction(boolean successful) { try { - if (isTemporary() && mSyncState.matches(url)) { - int numRows = mSyncState.asContentProvider().update( - url, values, selection, selectionArgs); + if (successful) { + // setTransactionSuccessful() must be called just once during opening the + // transaction. mDb.setTransactionSuccessful(); - return numRows; } + } finally { + mDb.endTransaction(); + } + } - int result = updateInternal(url, values, selection, selectionArgs); - mDb.setTransactionSuccessful(); + @Override + public final int update(final Uri uri, final ContentValues values, + final String selection, final String[] selectionArgs) { + boolean successful = false; + beginTransaction(); + try { + int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs); + successful = true; + return ret; + } finally { + endTransaction(successful); + } + } - if (!isTemporary() && result > 0) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } + /** + * @hide + */ + public final int nonTransactionalUpdate(final Uri uri, final ContentValues values, + final String selection, final String[] selectionArgs) { + if (isTemporary() && mSyncState.matches(uri)) { + int numRows = mSyncState.asContentProvider().update( + uri, values, selection, selectionArgs); + return numRows; + } - return result; - } finally { - mDb.endTransaction(); + int result = updateInternal(uri, values, selection, selectionArgs); + if (!isTemporary() && result > 0) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); } + + return result; } @Override - public final int delete(final Uri url, final String selection, + public final int delete(final Uri uri, final String selection, final String[] selectionArgs) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); + boolean successful = false; + beginTransaction(); try { - if (isTemporary() && mSyncState.matches(url)) { - int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs); - mDb.setTransactionSuccessful(); - return numRows; - } - int result = deleteInternal(url, selection, selectionArgs); - mDb.setTransactionSuccessful(); - if (!isTemporary() && result > 0) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } - return result; + int ret = nonTransactionalDelete(uri, selection, selectionArgs); + successful = true; + return ret; } finally { - mDb.endTransaction(); + endTransaction(successful); } } + /** + * @hide + */ + public final int nonTransactionalDelete(final Uri uri, final String selection, + final String[] selectionArgs) { + if (isTemporary() && mSyncState.matches(uri)) { + int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs); + return numRows; + } + int result = deleteInternal(uri, selection, selectionArgs); + if (!isTemporary() && result > 0) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); + } + return result; + } + @Override - public final Uri insert(final Uri url, final ContentValues values) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); + public final Uri insert(final Uri uri, final ContentValues values) { + boolean successful = false; + beginTransaction(); try { - if (isTemporary() && mSyncState.matches(url)) { - Uri result = mSyncState.asContentProvider().insert(url, values); - mDb.setTransactionSuccessful(); - return result; - } - Uri result = insertInternal(url, values); - mDb.setTransactionSuccessful(); - if (!isTemporary() && result != null) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } - return result; + Uri ret = nonTransactionalInsert(uri, values); + successful = true; + return ret; } finally { - mDb.endTransaction(); + endTransaction(successful); + } + } + + /** + * @hide + */ + public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) { + if (isTemporary() && mSyncState.matches(uri)) { + Uri result = mSyncState.asContentProvider().insert(uri, values); + return result; + } + Uri result = insertInternal(uri, values); + if (!isTemporary() && result != null) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); } + return result; } @Override diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java index 700f1d88241c..9c760d94ad63 100644 --- a/core/java/android/content/AbstractTableMerger.java +++ b/core/java/android/content/AbstractTableMerger.java @@ -61,8 +61,10 @@ public abstract class AbstractTableMerger _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=?"; private static final String SELECT_BY_ID = BaseColumns._ID +"=?"; - private static final String SELECT_UNSYNCED = "" - + _SYNC_DIRTY + " > 0 and (" + _SYNC_ACCOUNT + "=? or " + _SYNC_ACCOUNT + " is null)"; + private static final String SELECT_UNSYNCED = + "(" + _SYNC_ACCOUNT + " IS NULL OR " + _SYNC_ACCOUNT + "=?) AND " + + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 AND " + + _SYNC_VERSION + " IS NOT NULL))"; public AbstractTableMerger(SQLiteDatabase database, String table, Uri tableURL, String deletedTable, @@ -365,26 +367,32 @@ public abstract class AbstractTableMerger if (!TextUtils.isEmpty(localSyncID)) { // An existing server item has changed - boolean recordChanged = (localSyncVersion == null) || - !serverSyncVersion.equals(localSyncVersion); - if (recordChanged) { - if (localSyncDirty) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "remote record " + serverSyncId - + " conflicts with local _sync_id " + localSyncID - + ", local _id " + localRowId); + // If serverSyncVersion is null, there is no edit URL; + // server won't let this change be written. + // Just hold onto it, I guess, in case the server permissions + // change later. + if (serverSyncVersion != null) { + boolean recordChanged = (localSyncVersion == null) || + !serverSyncVersion.equals(localSyncVersion); + if (recordChanged) { + if (localSyncDirty) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "remote record " + serverSyncId + + " conflicts with local _sync_id " + localSyncID + + ", local _id " + localRowId); + } + conflict = true; + } else { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, + "remote record " + + serverSyncId + + " updates local _sync_id " + + localSyncID + ", local _id " + + localRowId); + } + update = true; } - conflict = true; - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, - "remote record " + - serverSyncId + - " updates local _sync_id " + - localSyncID + ", local _id " + - localRowId); - } - update = true; } } } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f2ad2485d0e1..9e37ae448731 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -233,6 +234,9 @@ public abstract class Context { /** Return the name of this application's package. */ public abstract String getPackageName(); + /** Return the full application info for this context's package. */ + public abstract ApplicationInfo getApplicationInfo(); + /** * {@hide} * Return the full path to this context's resource files. This is the ZIP files @@ -254,12 +258,20 @@ public abstract class Context { * <p>Note: this is not generally useful for applications, since they should * not be directly accessing the file system. * - * * @return String Path to the code and assets. */ public abstract String getPackageCodePath(); /** + * {@hide} + * Return the full path to the shared prefs file for the given prefs group name. + * + * <p>Note: this is not generally useful for applications, since they should + * not be directly accessing the file system. + */ + public abstract File getSharedPrefsFile(String name); + + /** * Retrieve and hold the contents of the preferences file 'name', returning * a SharedPreferences through which you can retrieve and modify its * values. Only one instance of the SharedPreferences object is returned @@ -527,16 +539,6 @@ public abstract class Context { public abstract int getWallpaperDesiredMinimumHeight(); /** - * Returns the scale in which the application will be drawn on the - * screen. This is usually 1.0f if the application supports the device's - * resolution/density. This will be 1.5f, for example, if the application - * that supports only 160 density runs on 240 density screen. - * - * @hide - */ - public abstract float getApplicationScale(); - - /** * Change the current system wallpaper to a bitmap. The given bitmap is * converted to a PNG and stored as the wallpaper. On success, the intent * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. @@ -1135,6 +1137,15 @@ public abstract class Context { public static final String NOTIFICATION_SERVICE = "notification"; /** * Use with {@link #getSystemService} to retrieve a + * {@link android.view.accessibility.AccessibilityManager} for giving the user + * feedback for UI events through the registered event listeners. + * + * @see #getSystemService + * @see android.view.accessibility.AccessibilityManager + */ + public static final String ACCESSIBILITY_SERVICE = "accessibility"; + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.app.NotificationManager} for controlling keyguard. * * @see #getSystemService @@ -1643,6 +1654,13 @@ public abstract class Context { * with extreme care! */ public static final int CONTEXT_IGNORE_SECURITY = 0x00000002; + + /** + * Flag for use with {@link #createPackageContext}: a restricted context may + * disable specific features. For instance, a View associated with a restricted + * context would ignore particular XML attributes. + */ + public static final int CONTEXT_RESTRICTED = 0x00000004; /** * Return a new Context object for the given application name. This @@ -1671,4 +1689,15 @@ public abstract class Context { */ public abstract Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException; + + /** + * Indicates whether this Context is restricted. + * + * @return True if this Context is restricted, false otherwise. + * + * @see #CONTEXT_RESTRICTED + */ + public boolean isRestricted() { + return false; + } } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 25b2caeb7279..45a082a9520e 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -120,6 +121,11 @@ public class ContextWrapper extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + return mBase.getApplicationInfo(); + } + + @Override public String getPackageResourcePath() { return mBase.getPackageResourcePath(); } @@ -130,6 +136,11 @@ public class ContextWrapper extends Context { } @Override + public File getSharedPrefsFile(String name) { + return mBase.getSharedPrefsFile(name); + } + + @Override public SharedPreferences getSharedPreferences(String name, int mode) { return mBase.getSharedPreferences(name, mode); } @@ -420,11 +431,8 @@ public class ContextWrapper extends Context { return mBase.createPackageContext(packageName, flags); } - /** - * @hide - */ @Override - public float getApplicationScale() { - return mBase.getApplicationScale(); + public boolean isRestricted() { + return mBase.isRestricted(); } } diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl new file mode 100755 index 000000000000..443db2d06d0f --- /dev/null +++ b/core/java/android/content/IIntentReceiver.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.content; + +import android.content.Intent; +import android.os.Bundle; + +/** + * System private API for dispatching intent broadcasts. This is given to the + * activity manager as part of registering for an intent broadcasts, and is + * called when it receives intents. + * + * {@hide} + */ +oneway interface IIntentReceiver { + void performReceive(in Intent intent, int resultCode, + String data, in Bundle extras, boolean ordered); +} + diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl new file mode 100644 index 000000000000..b7da47219ce4 --- /dev/null +++ b/core/java/android/content/IIntentSender.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.content; + +import android.content.IIntentReceiver; +import android.content.Intent; + +/** @hide */ +interface IIntentSender { + int send(int code, in Intent intent, String resolvedType, + IIntentReceiver finishedReceiver); +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 24262f51197d..263f9279e69a 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -240,35 +240,35 @@ import java.util.Set; * * <activity class=".NotesList" android:label="@string/title_notes_list"> * <intent-filter> - * <action android:value="android.intent.action.MAIN" /> - * <category android:value="android.intent.category.LAUNCHER" /> + * <action android:name="android.intent.action.MAIN" /> + * <category android:name="android.intent.category.LAUNCHER" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <action android:value="android.intent.action.PICK" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <action android:name="android.intent.action.PICK" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.GET_CONTENT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.GET_CONTENT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * </activity> * * <activity class=".NoteEditor" android:label="@string/title_note"> * <intent-filter android:label="@string/resolve_edit"> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * * <intent-filter> - * <action android:value="android.intent.action.INSERT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.INSERT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter> * * </activity> @@ -276,11 +276,11 @@ import java.util.Set; * <activity class=".TitleEditor" android:label="@string/title_edit_title" * android:theme="@android:style/Theme.Dialog"> * <intent-filter android:label="@string/resolve_title"> - * <action android:value="<i>com.android.notepad.action.EDIT_TITLE</i>" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <category android:value="android.intent.category.ALTERNATIVE" /> - * <category android:value="android.intent.category.SELECTED_ALTERNATIVE" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <category android:name="android.intent.category.ALTERNATIVE" /> + * <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * </activity> * @@ -294,8 +294,8 @@ import java.util.Set; * <ol> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_MAIN android.intent.action.MAIN}" /> - * <category android:value="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" /> + * <action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" /> + * <category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" /> * </intent-filter></pre> * <p>This provides a top-level entry into the NotePad application: the standard * MAIN action is a main entry point (not requiring any other information in @@ -303,11 +303,11 @@ import java.util.Set; * listed in the application launcher.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" /> - * <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" /> - * <action android:value="{@link #ACTION_PICK android.intent.action.PICK}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /> + * <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /> + * <action android:name="{@link #ACTION_PICK android.intent.action.PICK}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data mimeType:name="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>This declares the things that the activity can do on a directory of * notes. The type being supported is given with the <type> tag, where @@ -322,9 +322,9 @@ import java.util.Set; * activity when its component name is not explicitly specified.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>This filter describes the ability return to the caller a note selected by * the user without needing to know where it came from. The data type @@ -371,10 +371,10 @@ import java.util.Set; * <ol> * <li><pre> * <intent-filter android:label="@string/resolve_edit"> - * <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" /> - * <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /> + * <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>The first, primary, purpose of this activity is to let the user interact * with a single note, as decribed by the MIME type @@ -384,9 +384,9 @@ import java.util.Set; * specifying its component.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_INSERT android.intent.action.INSERT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>The secondary use of this activity is to insert a new note entry into * an existing directory of notes. This is used when the user creates a new @@ -422,11 +422,11 @@ import java.util.Set; * * <pre> * <intent-filter android:label="@string/resolve_title"> - * <action android:value="<i>com.android.notepad.action.EDIT_TITLE</i>" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <category android:value="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" /> - * <category android:value="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" /> + * <category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * * <p>In the single intent template here, we @@ -509,8 +509,8 @@ import java.util.Set; * <li> {@link #ACTION_UID_REMOVED} * <li> {@link #ACTION_BATTERY_CHANGED} * <li> {@link #ACTION_POWER_CONNECTED} - * <li> {@link #ACTION_POWER_DISCONNECTED} - * <li> {@link #ACTION_SHUTDOWN} + * <li> {@link #ACTION_POWER_DISCONNECTED} + * <li> {@link #ACTION_SHUTDOWN} * </ul> * * <h3>Standard Categories</h3> @@ -915,6 +915,23 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SEND = "android.intent.action.SEND"; /** + * Activity Action: Deliver multiple data to someone else. + * <p> + * Like ACTION_SEND, except the data is multiple. + * <p> + * Input: {@link #getType} is the MIME type of the data being sent. + * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link + * #EXTRA_STREAM} field, containing the data to be sent. + * <p> + * Optional standard extras, which may be interpreted by some recipients as + * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC}, + * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}. + * <p> + * Output: nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE"; + /** * Activity Action: Handle an incoming phone call. * <p>Input: nothing. * <p>Output: nothing. @@ -1059,6 +1076,36 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR"; + + /** + * Activity Action: Show power usage information to the user. + * <p>Input: Nothing. + * <p>Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; + + /** + * Activity Action: Setup wizard to launch after a platform update. This + * activity should have a string meta-data field associated with it, + * {@link #METADATA_SETUP_VERSION}, which defines the current version of + * the platform for setup. The activity will be launched only if + * {@link android.provider.Settings.Secure#LAST_SETUP_SHOWN} is not the + * same value. + * <p>Input: Nothing. + * <p>Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; + + /** + * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity + * describing the last run version of the platform that was setup. + * @hide + */ + public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). @@ -1264,6 +1311,13 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW"; /** + * Broadcast Action: Indicates the battery is now okay after being low. + * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has + * gone back up to an okay state. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY"; + /** * Broadcast Action: External power has been connected to the device. * This is intended for applications that wish to register specifically to this notification. * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to @@ -1277,10 +1331,10 @@ public class Intent implements Parcelable { * This is intended for applications that wish to register specifically to this notification. * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to * stay active to receive this notification. This action can be used to implement actions - * that wait until power is available to trigger. + * that wait until power is available to trigger. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; + public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; /** * Broadcast Action: Device is shutting down. * This is broadcast when the device is being shut down (completely turned @@ -1289,7 +1343,7 @@ public class Intent implements Parcelable { * to handle this, since the forground activity will be paused as well. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; + public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; /** * Broadcast Action: Indicates low memory condition on the device */ @@ -1552,6 +1606,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()). @@ -1791,23 +1855,23 @@ public class Intent implements Parcelable { * delivered. */ public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; - + /** * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing * the bug report. - * + * * @hide */ public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; /** - * Used as a string extra field when sending an intent to PackageInstaller to install a + * Used as a string extra field when sending an intent to PackageInstaller to install a * package. Specifies the installer package name; this package will receive the * {@link #ACTION_APP_ERROR} intent. - * + * * @hide */ - public static final String EXTRA_INSTALLER_PACKAGE_NAME + public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; // --------------------------------------------------------------------- @@ -2040,10 +2104,25 @@ public class Intent implements Parcelable { public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // toUri() and parseUri() options. + + /** + * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string + * always has the "intent:" scheme. This syntax can be used when you want + * to later disambiguate between URIs that are intended to describe an + * Intent vs. all others that should be treated as raw URIs. When used + * with {@link #parseUri}, any other scheme will result in a generic + * VIEW action for that raw URI. + */ + public static final int URI_INTENT_SCHEME = 1<<0; + + // --------------------------------------------------------------------- private String mAction; private Uri mData; private String mType; + private String mPackage; private ComponentName mComponent; private int mFlags; private HashSet<String> mCategories; @@ -2064,6 +2143,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mFlags = o.mFlags; if (o.mCategories != null) { @@ -2083,6 +2163,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; if (o.mCategories != null) { this.mCategories = new HashSet<String>(o.mCategories); @@ -2183,23 +2264,50 @@ public class Intent implements Parcelable { } /** + * Call {@link #parseUri} with 0 flags. + * @deprecated Use {@link #parseUri} instead. + */ + @Deprecated + public static Intent getIntent(String uri) throws URISyntaxException { + return parseUri(uri, 0); + } + + /** * Create an intent from a URI. This URI may encode the action, - * category, and other intent fields, if it was returned by toURI(). If - * the Intent was not generate by toURI(), its data will be the entire URI - * and its action will be ACTION_VIEW. + * category, and other intent fields, if it was returned by + * {@link #toUri}.. If the Intent was not generate by toUri(), its data + * will be the entire URI and its action will be ACTION_VIEW. * * <p>The URI given here must not be relative -- that is, it must include * the scheme and full path. * * @param uri The URI to turn into an Intent. + * @param flags Additional processing flags. Either 0 or * * @return Intent The newly created Intent object. * - * @see #toURI + * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax + * it bad (as parsed by the Uri class) or the Intent data within the + * URI is invalid. + * + * @see #toUri */ - public static Intent getIntent(String uri) throws URISyntaxException { + public static Intent parseUri(String uri, int flags) throws URISyntaxException { int i = 0; try { + // Validate intent scheme for if requested. + if ((flags&URI_INTENT_SCHEME) != 0) { + if (!uri.startsWith("intent:")) { + Intent intent = new Intent(ACTION_VIEW); + try { + intent.setData(Uri.parse(uri)); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + return intent; + } + } + // simple case i = uri.lastIndexOf("#"); if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri)); @@ -2211,16 +2319,15 @@ public class Intent implements Parcelable { Intent intent = new Intent(ACTION_VIEW); // fetch data part, if present - if (i > 0) { - intent.mData = Uri.parse(uri.substring(0, i)); - } + String data = i >= 0 ? uri.substring(0, i) : null; + String scheme = null; i += "#Intent;".length(); // loop over contents of Intent, all name=value; while (!uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); int semi = uri.indexOf(';', eq); - String value = uri.substring(eq + 1, semi); + String value = Uri.decode(uri.substring(eq + 1, semi)); // action if (uri.startsWith("action=", i)) { @@ -2242,15 +2349,24 @@ public class Intent implements Parcelable { intent.mFlags = Integer.decode(value).intValue(); } + // package + else if (uri.startsWith("package=", i)) { + intent.mPackage = value; + } + // component else if (uri.startsWith("component=", i)) { intent.mComponent = ComponentName.unflattenFromString(value); } + // scheme + else if (uri.startsWith("scheme=", i)) { + scheme = value; + } + // extra else { String key = Uri.decode(uri.substring(i + 2, eq)); - value = Uri.decode(value); // create Bundle if it doesn't already exist if (intent.mExtras == null) intent.mExtras = new Bundle(); Bundle b = intent.mExtras; @@ -2271,6 +2387,23 @@ public class Intent implements Parcelable { i = semi + 1; } + if (data != null) { + if (data.startsWith("intent:")) { + data = data.substring(7); + if (scheme != null) { + data = scheme + ':' + data; + } + } + + if (data.length() > 0) { + try { + intent.mData = Uri.parse(data); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + } + } + return intent; } catch (IndexOutOfBoundsException e) { @@ -3084,6 +3217,20 @@ public class Intent implements Parcelable { } /** + * Retrieve the application package name this Intent is limited to. When + * resolving an Intent, if non-null this limits the resolution to only + * components in the given application package. + * + * @return The name of the application package for the Intent. + * + * @see #resolveActivity + * @see #setPackage + */ + public String getPackage() { + return mPackage; + } + + /** * Retrieve the concrete component associated with the intent. When receiving * an intent, this is the component that was found to best handle it (that is, * yourself) and will always be non-null; in all other cases it will be @@ -3118,6 +3265,9 @@ public class Intent implements Parcelable { * <p>If {@link #addCategory} has added any categories, the activity must * handle ALL of the categories specified. * + * <p>If {@link #getPackage} is non-NULL, only activity components in + * that application package will be considered. + * * <p>If there are no activities that satisfy all of these conditions, a * null string is returned. * @@ -3239,7 +3389,7 @@ public class Intent implements Parcelable { * only specify a type and not data, for example to indicate the type of * data to return. This method automatically clears any data that was * previously set by {@link #setData}. - * + * * <p><em>Note: MIME type matching in the Android framework is * case-sensitive, unlike formal RFC MIME types. As a result, * you should always write your MIME types with lower case letters, @@ -4089,6 +4239,27 @@ public class Intent implements Parcelable { } /** + * (Usually optional) Set an explicit application package name that limits + * the components this Intent will resolve to. If left to the default + * value of null, all components in all applications will considered. + * If non-null, the Intent can only match the components in the given + * application package. + * + * @param packageName The name of the application package to handle the + * intent, or null to allow any application package. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #getPackage + * @see #resolveActivity + */ + public Intent setPackage(String packageName) { + mPackage = packageName; + return this; + } + + /** * (Usually optional) Explicitly set the component to handle the intent. * If left with the default value of null, the system will determine the * appropriate class to use based on the other fields (action, data, @@ -4200,6 +4371,12 @@ public class Intent implements Parcelable { public static final int FILL_IN_COMPONENT = 1<<3; /** + * Use with {@link #fillIn} to allow the current package value to be + * overwritten, even if it is already set. + */ + public static final int FILL_IN_PACKAGE = 1<<4; + + /** * Copy the contents of <var>other</var> in to this object, but only * where fields are not defined by this object. For purposes of a field * being defined, the following pieces of data in the Intent are @@ -4210,14 +4387,15 @@ public class Intent implements Parcelable { * <li> data URI and MIME type, as set by {@link #setData(Uri)}, * {@link #setType(String)}, or {@link #setDataAndType(Uri, String)}. * <li> categories, as set by {@link #addCategory}. + * <li> package, as set by {@link #setPackage}. * <li> component, as set by {@link #setComponent(ComponentName)} or * related methods. * <li> each top-level name in the associated extras. * </ul> * * <p>In addition, you can use the {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} to override the restriction where the + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} to override the restriction where the * corresponding field will not be replaced if it is already set. * * <p>For example, consider Intent A with {data="foo", categories="bar"} @@ -4233,32 +4411,39 @@ public class Intent implements Parcelable { * @param flags Options to control which fields can be filled in. * * @return Returns a bit mask of {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} indicating which fields were changed. + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} indicating which fields were changed. */ public int fillIn(Intent other, int flags) { int changes = 0; - if ((mAction == null && other.mAction == null) - || (flags&FILL_IN_ACTION) != 0) { + if (other.mAction != null + && (mAction == null || (flags&FILL_IN_ACTION) != 0)) { mAction = other.mAction; changes |= FILL_IN_ACTION; } - if ((mData == null && mType == null && - (other.mData != null || other.mType != null)) - || (flags&FILL_IN_DATA) != 0) { + if ((other.mData != null || other.mType != null) + && ((mData == null && mType == null) + || (flags&FILL_IN_DATA) != 0)) { mData = other.mData; mType = other.mType; changes |= FILL_IN_DATA; } - if ((mCategories == null && other.mCategories == null) - || (flags&FILL_IN_CATEGORIES) != 0) { + if (other.mCategories != null + && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) { if (other.mCategories != null) { mCategories = new HashSet<String>(other.mCategories); } changes |= FILL_IN_CATEGORIES; } - if ((mComponent == null && other.mComponent == null) - || (flags&FILL_IN_COMPONENT) != 0) { + if (other.mPackage != null + && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { + mPackage = other.mPackage; + changes |= FILL_IN_PACKAGE; + } + // Component is special: it can -only- be set if explicitly allowed, + // since otherwise the sender could force the intent somewhere the + // originator didn't intend. + if (other.mComponent != null && (flags&FILL_IN_COMPONENT) != 0) { mComponent = other.mComponent; changes |= FILL_IN_COMPONENT; } @@ -4373,6 +4558,17 @@ public class Intent implements Parcelable { } } } + if (mPackage != other.mPackage) { + if (mPackage != null) { + if (!mPackage.equals(other.mPackage)) { + return false; + } + } else { + if (!other.mPackage.equals(mPackage)) { + return false; + } + } + } if (mComponent != other.mComponent) { if (mComponent != null) { if (!mComponent.equals(other.mComponent)) { @@ -4418,6 +4614,9 @@ public class Intent implements Parcelable { if (mType != null) { code += mType.hashCode(); } + if (mPackage != null) { + code += mPackage.hashCode(); + } if (mComponent != null) { code += mComponent.hashCode(); } @@ -4444,7 +4643,7 @@ public class Intent implements Parcelable { toShortString(b, comp, extras); return b.toString(); } - + /** @hide */ public void toShortString(StringBuilder b, boolean comp, boolean extras) { boolean first = true; @@ -4488,6 +4687,13 @@ public class Intent implements Parcelable { first = false; b.append("flg=0x").append(Integer.toHexString(mFlags)); } + if (mPackage != null) { + if (!first) { + b.append(' '); + } + first = false; + b.append("pkg=").append(mPackage); + } if (comp && mComponent != null) { if (!first) { b.append(' '); @@ -4504,28 +4710,87 @@ public class Intent implements Parcelable { } } + /** + * Call {@link #toUri} with 0 flags. + * @deprecated Use {@link #toUri} instead. + */ + @Deprecated public String toURI() { + return toUri(0); + } + + /** + * Convert this Intent into a String holding a URI representation of it. + * The returned URI string has been properly URI encoded, so it can be + * used with {@link Uri#parse Uri.parse(String)}. The URI contains the + * Intent's data as the base URI, with an additional fragment describing + * the action, categories, type, flags, package, component, and extras. + * + * <p>You can convert the returned string back to an Intent with + * {@link #getIntent}. + * + * @param flags Additional operating flags. Either 0 or + * {@link #URI_INTENT_SCHEME}. + * + * @return Returns a URI encoding URI string describing the entire contents + * of the Intent. + */ + public String toUri(int flags) { StringBuilder uri = new StringBuilder(128); - if (mData != null) uri.append(mData.toString()); + String scheme = null; + if (mData != null) { + String data = mData.toString(); + if ((flags&URI_INTENT_SCHEME) != 0) { + final int N = data.length(); + for (int i=0; i<N; i++) { + char c = data.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || c == '.' || c == '-') { + continue; + } + if (c == ':' && i > 0) { + // Valid scheme. + scheme = data.substring(0, i); + uri.append("intent:"); + data = data.substring(i+1); + break; + } + + // No scheme. + break; + } + } + uri.append(data); + + } else if ((flags&URI_INTENT_SCHEME) != 0) { + uri.append("intent:"); + } uri.append("#Intent;"); + if (scheme != null) { + uri.append("scheme=").append(scheme).append(';'); + } if (mAction != null) { - uri.append("action=").append(mAction).append(';'); + uri.append("action=").append(Uri.encode(mAction)).append(';'); } if (mCategories != null) { for (String category : mCategories) { - uri.append("category=").append(category).append(';'); + uri.append("category=").append(Uri.encode(category)).append(';'); } } if (mType != null) { - uri.append("type=").append(mType).append(';'); + uri.append("type=").append(Uri.encode(mType, "/")).append(';'); } if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } + if (mPackage != null) { + uri.append("package=").append(Uri.encode(mPackage)).append(';'); + } if (mComponent != null) { - uri.append("component=").append(mComponent.flattenToShortString()).append(';'); + uri.append("component=").append(Uri.encode( + mComponent.flattenToShortString(), "/")).append(';'); } if (mExtras != null) { for (String key : mExtras.keySet()) { @@ -4567,6 +4832,7 @@ public class Intent implements Parcelable { Uri.writeToParcel(out, mData); out.writeString(mType); out.writeInt(mFlags); + out.writeString(mPackage); ComponentName.writeToParcel(mComponent, out); if (mCategories != null) { @@ -4600,6 +4866,7 @@ public class Intent implements Parcelable { mData = Uri.CREATOR.createFromParcel(in); mType = in.readString(); mFlags = in.readInt(); + mPackage = in.readString(); mComponent = ComponentName.readFromParcel(in); int N = in.readInt(); diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index e5c5dc8a5e0c..365f26983a33 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -366,6 +366,7 @@ public class IntentFilter implements Parcelable { throws MalformedMimeTypeException { mPriority = 0; mActions = new ArrayList<String>(); + addAction(action); addDataType(dataType); } diff --git a/core/java/android/content/IntentSender.aidl b/core/java/android/content/IntentSender.aidl new file mode 100644 index 000000000000..741bc8c953cb --- /dev/null +++ b/core/java/android/content/IntentSender.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +parcelable IntentSender; diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java new file mode 100644 index 000000000000..4da49d974fd5 --- /dev/null +++ b/core/java/android/content/IntentSender.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.content; + +import android.content.Context; +import android.content.Intent; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.Handler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AndroidException; + + +/** + * A description of an Intent and target action to perform with it. + * The returned object can be + * handed to other applications so that they can perform the action you + * described on your behalf at a later time. + * + * <p>By giving a IntentSender to another application, + * you are granting it the right to perform the operation you have specified + * as if the other application was yourself (with the same permissions and + * identity). As such, you should be careful about how you build the IntentSender: + * often, for example, the base Intent you supply will have the component + * name explicitly set to one of your own components, to ensure it is ultimately + * sent there and nowhere else. + * + * <p>A IntentSender itself is simply a reference to a token maintained by + * the system describing the original data used to retrieve it. This means + * that, even if its owning application's process is killed, the + * IntentSender itself will remain usable from other processes that + * have been given it. If the creating application later re-retrieves the + * same kind of IntentSender (same operation, same Intent action, data, + * categories, and components, and same flags), it will receive a IntentSender + * representing the same token if that is still valid. + * + */ +public class IntentSender implements Parcelable { + private final IIntentSender mTarget; + + /** + * Exception thrown when trying to send through a PendingIntent that + * has been canceled or is otherwise no longer able to execute the request. + */ + public static class SendIntentException extends AndroidException { + public SendIntentException() { + } + + public SendIntentException(String name) { + super(name); + } + + public SendIntentException(Exception cause) { + super(cause); + } + } + + /** + * Callback interface for discovering when a send operation has + * completed. Primarily for use with a IntentSender that is + * performing a broadcast, this provides the same information as + * calling {@link Context#sendOrderedBroadcast(Intent, String, + * android.content.BroadcastReceiver, Handler, int, String, Bundle) + * Context.sendBroadcast()} with a final BroadcastReceiver. + */ + public interface OnFinished { + /** + * Called when a send operation as completed. + * + * @param IntentSender The IntentSender this operation was sent through. + * @param intent The original Intent that was sent. + * @param resultCode The final result code determined by the send. + * @param resultData The final data collected by a broadcast. + * @param resultExtras The final extras collected by a broadcast. + */ + void onSendFinished(IntentSender IntentSender, Intent intent, + int resultCode, String resultData, Bundle resultExtras); + } + + private static class FinishedDispatcher extends IIntentReceiver.Stub + implements Runnable { + private final IntentSender mIntentSender; + private final OnFinished mWho; + private final Handler mHandler; + private Intent mIntent; + private int mResultCode; + private String mResultData; + private Bundle mResultExtras; + FinishedDispatcher(IntentSender pi, OnFinished who, Handler handler) { + mIntentSender = pi; + mWho = who; + mHandler = handler; + } + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean serialized) { + mIntent = intent; + mResultCode = resultCode; + mResultData = data; + mResultExtras = extras; + if (mHandler == null) { + run(); + } else { + mHandler.post(this); + } + } + public void run() { + mWho.onSendFinished(mIntentSender, mIntent, mResultCode, + mResultData, mResultExtras); + } + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler) throws SendIntentException { + try { + String resolvedType = intent != null ? + intent.resolveTypeIfNeeded(context.getContentResolver()) + : null; + int res = mTarget.send(code, intent, resolvedType, + onFinished != null + ? new FinishedDispatcher(this, onFinished, handler) + : null); + if (res < 0) { + throw new SendIntentException(); + } + } catch (RemoteException e) { + throw new SendIntentException(); + } + } + + /** + * Comparison operator on two IntentSender objects, such that true + * is returned then they both represent the same operation from the + * same package. + */ + @Override + public boolean equals(Object otherObj) { + if (otherObj instanceof IntentSender) { + return mTarget.asBinder().equals(((IntentSender)otherObj) + .mTarget.asBinder()); + } + return false; + } + + @Override + public int hashCode() { + return mTarget.asBinder().hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("IntentSender{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(": "); + sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append('}'); + return sb.toString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(mTarget.asBinder()); + } + + public static final Parcelable.Creator<IntentSender> CREATOR + = new Parcelable.Creator<IntentSender>() { + public IntentSender createFromParcel(Parcel in) { + IBinder target = in.readStrongBinder(); + return target != null ? new IntentSender(target) : null; + } + + public IntentSender[] newArray(int size) { + return new IntentSender[size]; + } + }; + + /** + * Convenience function for writing either a IntentSender or null pointer to + * a Parcel. You must use this with {@link #readIntentSenderOrNullFromParcel} + * for later reading it. + * + * @param sender The IntentSender to write, or null. + * @param out Where to write the IntentSender. + */ + public static void writeIntentSenderOrNullToParcel(IntentSender sender, + Parcel out) { + out.writeStrongBinder(sender != null ? sender.mTarget.asBinder() + : null); + } + + /** + * Convenience function for reading either a Messenger or null pointer from + * a Parcel. You must have previously written the Messenger with + * {@link #writeIntentSenderOrNullToParcel}. + * + * @param in The Parcel containing the written Messenger. + * + * @return Returns the Messenger read from the Parcel, or null if null had + * been written. + */ + public static IntentSender readIntentSenderOrNullFromParcel(Parcel in) { + IBinder b = in.readStrongBinder(); + return b != null ? new IntentSender(b) : null; + } + + protected IntentSender(IIntentSender target) { + mTarget = target; + } + + protected IntentSender(IBinder target) { + mTarget = IIntentSender.Stub.asInterface(target); + } +} diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 9c25e73b0cf3..f781e0d0e12d 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.backup.IBackupManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -35,6 +36,7 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.util.SparseArray; import android.util.Xml; @@ -351,8 +353,18 @@ public class SyncStorageEngine extends Handler { } } } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (RemoteException e) { + // Try again later + } + } } - + public boolean getSyncProviderAutomatically(String account, String providerName) { synchronized (mAuthorities) { if (account != null) { diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 85d877a07017..27783efea9df 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 88ac04c24e6d..bcf95b6f574e 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -58,11 +58,22 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Class implementing the Application's manage space * functionality. From the "manageSpaceActivity" * attribute. This is an optional attribute and will be null if - * application's dont specify it in their manifest + * applications don't specify it in their manifest */ public String manageSpaceActivityName; /** + * Class implementing the Application's backup functionality. From + * the "backupAgent" attribute. This is an optional attribute and + * will be null if the application does not specify it in its manifest. + * + * <p>If android:allowBackup is set to false, this attribute is ignored. + * + * {@hide} + */ + public String backupAgentName; + + /** * Value for {@link #flags}: if set, this application is installed in the * device's system image. */ @@ -93,7 +104,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_PERSISTENT = 1<<3; /** - * Value for {@link #flags}: set to true iif this application holds the + * Value for {@link #flags}: set to true if this application holds the * {@link android.Manifest.permission#FACTORY_TEST} permission and the * device is running in factory test mode. */ @@ -123,13 +134,46 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Value for {@link #flags}: this is set of the application has set * its android:targetSdkVersion to something >= the current SDK version. */ - public static final int FLAG_TARGETS_SDK = 1<<8; + public static final int FLAG_TEST_ONLY = 1<<8; /** - * Value for {@link #flags}: this is set of the application has set - * its android:targetSdkVersion to something >= the current SDK version. + * Value for {@link #flags}: true when the application's window can be + * reduced in size for smaller screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens + * android:smallScreens}. */ - public static final int FLAG_TEST_ONLY = 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 + * its android:allowBackup to false, true otherwise. + * + * {@hide} + */ + public static final int FLAG_ALLOW_BACKUP = 1<<12; + + /** + * Indicates that the application supports any densities; + * {@hide} + */ + public static final int ANY_DENSITY = -1; + private static final int[] ANY_DENSITIES_ARRAY = { ANY_DENSITY }; /** * Flags associated with the application. Any combination of @@ -137,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_TARGETS_SDK}. + * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS}, + * {@link #FLAG_SUPPORTS_NORMAL_SCREENS}, + * {@link #FLAG_SUPPORTS_LARGE_SCREENS}. */ public int flags = 0; @@ -173,7 +219,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int uid; - /** * The list of densities in DPI that application supprots. This * field is only set if the {@link PackageManager#GET_SUPPORTS_DENSITIES} flag was @@ -182,6 +227,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int[] supportsDensities; /** + * The minimum SDK version this application targets. It may run on earilier + * versions, but it knows how to work with any new behavior added at this + * version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT} + * if this is a development build and the app is targeting that. You should + * compare that this number is >= the SDK version number at which your + * behavior was introduced. + */ + public int targetSdkVersion; + + /** * When false, indicates that all components within this application are * considered disabled, regardless of their individually set enabled status. */ @@ -200,6 +255,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "publicSourceDir=" + publicSourceDir); pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles); pw.println(prefix + "dataDir=" + dataDir); + pw.println(prefix + "targetSdkVersion=" + targetSdkVersion); pw.println(prefix + "enabled=" + enabled); pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName); pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes)); @@ -246,6 +302,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sharedLibraryFiles = orig.sharedLibraryFiles; dataDir = orig.dataDir; uid = orig.uid; + targetSdkVersion = orig.targetSdkVersion; enabled = orig.enabled; manageSpaceActivityName = orig.manageSpaceActivityName; descriptionRes = orig.descriptionRes; @@ -276,8 +333,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeStringArray(sharedLibraryFiles); dest.writeString(dataDir); dest.writeInt(uid); + dest.writeInt(targetSdkVersion); dest.writeInt(enabled ? 1 : 0); dest.writeString(manageSpaceActivityName); + dest.writeString(backupAgentName); dest.writeInt(descriptionRes); dest.writeIntArray(supportsDensities); } @@ -305,8 +364,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sharedLibraryFiles = source.readStringArray(); dataDir = source.readString(); uid = source.readInt(); + targetSdkVersion = source.readInt(); enabled = source.readInt() != 0; manageSpaceActivityName = source.readString(); + backupAgentName = source.readString(); descriptionRes = source.readInt(); supportsDensities = source.createIntArray(); } @@ -331,4 +392,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } return null; } + + /** + * Disable compatibility mode + * + * @hide + */ + public void disableCompatibilityMode() { + flags |= FLAG_SUPPORTS_LARGE_SCREENS; + supportsDensities = ANY_DENSITIES_ARRAY; + } } diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index dcc746331b0b..fb7a47fbd1bd 100755 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * Information you can retrieve about hardware configuration preferences * declared by an application. This corresponds to information collected from the - * AndroidManifest.xml's <uses-configuration> tags. + * AndroidManifest.xml's <uses-configuration> and the <uses-feature>tags. */ public class ConfigurationInfo implements Parcelable { /** @@ -70,6 +70,16 @@ public class ConfigurationInfo implements Parcelable { */ public int reqInputFeatures = 0; + /** + * Default value for {@link #reqGlEsVersion}; + */ + public static final int GL_ES_VERSION_UNDEFINED = 0; + /** + * The GLES version used by an application. The upper order 16 bits represent the + * major version and the lower order 16 bits the minor version. + */ + public int reqGlEsVersion; + public ConfigurationInfo() { } @@ -78,6 +88,7 @@ public class ConfigurationInfo implements Parcelable { reqKeyboardType = orig.reqKeyboardType; reqNavigation = orig.reqNavigation; reqInputFeatures = orig.reqInputFeatures; + reqGlEsVersion = orig.reqGlEsVersion; } public String toString() { @@ -86,7 +97,8 @@ public class ConfigurationInfo implements Parcelable { + ", touchscreen = " + reqTouchScreen + "}" + ", inputMethod = " + reqKeyboardType + "}" + ", navigation = " + reqNavigation + "}" - + ", reqInputFeatures = " + reqInputFeatures + "}"; + + ", reqInputFeatures = " + reqInputFeatures + "}" + + ", reqGlEsVersion = " + reqGlEsVersion + "}"; } public int describeContents() { @@ -98,6 +110,7 @@ public class ConfigurationInfo implements Parcelable { dest.writeInt(reqKeyboardType); dest.writeInt(reqNavigation); dest.writeInt(reqInputFeatures); + dest.writeInt(reqGlEsVersion); } public static final Creator<ConfigurationInfo> CREATOR = @@ -115,5 +128,18 @@ public class ConfigurationInfo implements Parcelable { reqKeyboardType = source.readInt(); reqNavigation = source.readInt(); reqInputFeatures = source.readInt(); + reqGlEsVersion = source.readInt(); + } + + /** + * This method extracts the major and minor version of reqGLEsVersion attribute + * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned + * as 1.2 + * @return String representation of the reqGlEsVersion attribute + */ + public String getGlEsVersion() { + int major = ((reqGlEsVersion & 0xffff0000) >> 16); + int minor = reqGlEsVersion & 0x0000ffff; + return String.valueOf(major)+"."+String.valueOf(minor); } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c199619c57a9..bf2a8959c7f4 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -34,7 +34,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.Uri; -import android.app.PendingIntent; +import android.content.IntentSender; /** * See {@link PackageManager} for documentation on most of the APIs @@ -164,7 +164,12 @@ interface IPackageManager { void addPreferredActivity(in IntentFilter filter, int match, in ComponentName[] set, in ComponentName activity); + + void replacePreferredActivity(in IntentFilter filter, int match, + in ComponentName[] set, in ComponentName activity); + void clearPackagePreferredActivities(String packageName); + int getPreferredActivities(out List<IntentFilter> outFilters, out List<ComponentName> outActivities, String packageName); @@ -229,12 +234,12 @@ interface IPackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. */ void freeStorage(in long freeStorageSize, - in PendingIntent opFinishedIntent); + in IntentSender pi); /** * Delete all the cache files in an applications cache directory @@ -271,4 +276,11 @@ interface IPackageManager { boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); + + /** + * Ask the package manager to perform dex-opt (if needed) on the given + * package, if it already hasn't done mode. Only does this if running + * in the special development "no pre-dexopt" mode. + */ + boolean performDexOpt(String packageName); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 3a192f7eb749..941ca9e5bb74 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,12 +16,11 @@ package android.content.pm; - -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -398,6 +397,15 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_TEST_ONLY = -15; /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the package being installed contains native code, but none that is + * compatible with the the device's CPU_ABI. + * @hide + */ + public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16; + + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} * if the parser was given a path that is not a file, or does not end with the expected @@ -563,9 +571,8 @@ public abstract class PackageManager { * launch the main activity in the package, or null if the package does * not contain such an activity. */ - public abstract Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException; - + public abstract Intent getLaunchIntentForPackage(String packageName); + /** * Return an array of all of the secondary group-ids that have been * assigned to a package. @@ -1491,7 +1498,7 @@ public abstract class PackageManager { * @hide */ public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); - + /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage @@ -1509,13 +1516,13 @@ public abstract class PackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. * * @hide */ - public abstract void freeStorage(long freeStorageSize, PendingIntent opFinishedIntent); + public abstract void freeStorage(long freeStorageSize, IntentSender pi); /** * Retrieve the size information for a package. @@ -1605,6 +1612,26 @@ public abstract class PackageManager { ComponentName[] set, ComponentName activity); /** + * Replaces an existing preferred activity mapping to the system, and if that were not present + * adds a new preferred activity. This will be used + * to automatically select the given activity component when + * {@link Context#startActivity(Intent) Context.startActivity()} finds + * multiple matching activities and also matches the given filter. + * + * @param filter The set of intents under which this activity will be + * made preferred. + * @param match The IntentFilter match category that this preference + * applies to. + * @param set The set of activities that the user was picking from when + * this preference was made. + * @param activity The component name of the activity that is to be + * preferred. + * @hide + */ + public abstract void replacePreferredActivity(IntentFilter filter, int match, + ComponentName[] set, ComponentName activity); + + /** * Remove all preferred activity mappings, previously added with * {@link #addPreferredActivity}, from the * system whose activities are implemented in the given package name. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 88907c180117..558b0c3e369a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,6 +55,32 @@ import java.util.jar.JarFile; * {@hide} */ public class PackageParser { + /** @hide */ + public static class NewPermissionInfo { + public final String name; + public final int sdkVersion; + public final int fileVersion; + + public NewPermissionInfo(String name, int sdkVersion, int fileVersion) { + this.name = name; + this.sdkVersion = sdkVersion; + this.fileVersion = fileVersion; + } + } + + /** + * List of new permissions that have been added since 1.0. + * NOTE: These must be declared in SDK version order, with permissions + * added to older SDKs appearing before those added to newer SDKs. + * @hide + */ + public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = + new PackageParser.NewPermissionInfo[] { + new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, + android.os.Build.VERSION_CODES.DONUT, 0), + new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, + android.os.Build.VERSION_CODES.DONUT, 0) + }; private String mArchiveSourcePath; private String[] mSeparateProcesses; @@ -616,7 +642,6 @@ public class PackageParser { final Package pkg = new Package(pkgName); boolean foundApp = false; - boolean targetsSdk = false; TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifest); @@ -643,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)) { @@ -723,6 +753,18 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("uses-feature")) { + ConfigurationInfo cPref = new ConfigurationInfo(); + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestUsesFeature); + cPref.reqGlEsVersion = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion, + ConfigurationInfo.GL_ES_VERSION_UNDEFINED); + sa.recycle(); + pkg.configPreferences.add(cPref); + + XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("uses-sdk")) { if (mSdkVersion > 0) { sa = res.obtainAttributes(attrs, @@ -740,7 +782,7 @@ public class PackageParser { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. - minVers = val.data; + targetVers = minVers = val.data; } } @@ -761,6 +803,25 @@ public class PackageParser { sa.recycle(); + if (minCode != null) { + if (!minCode.equals(mSdkCodename)) { + if (mSdkCodename != null) { + outError[0] = "Requires development platform " + minCode + + " (current platform is " + mSdkCodename + ")"; + } else { + outError[0] = "Requires development platform " + minCode + + " but this is a release platform."; + } + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + } else if (minVers > mSdkVersion) { + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + mSdkVersion + ")"; + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + if (targetCode != null) { if (!targetCode.equals(mSdkCodename)) { if (mSdkCodename != null) { @@ -774,18 +835,10 @@ public class PackageParser { return null; } // If the code matches, it definitely targets this SDK. - targetsSdk = true; - } else if (targetVers >= mSdkVersion) { - // If they have explicitly targeted our current version - // or something after it, then note this. - targetsSdk = true; - } - - if (minVers > mSdkVersion) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + mSdkVersion + ")"; - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; + pkg.applicationInfo.targetSdkVersion + = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; + } else { + pkg.applicationInfo.targetSdkVersion = targetVers; } if (maxVers < mSdkVersion) { @@ -811,6 +864,42 @@ public class PackageParser { + parser.getName(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; + + + } else if (tagName.equals("supports-density")) { + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestSupportsDensity); + + int density = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1); + + sa.recycle(); + + if (density != -1 && !pkg.supportsDensityList.contains(density)) { + pkg.supportsDensityList.add(density); + } + + XmlUtils.skipCurrentTag(parser); + + } 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>: " + parser.getName()); @@ -824,15 +913,39 @@ public class PackageParser { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; } - if (targetsSdk) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_TARGETS_SDK; + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip<NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) { + break; + } + if (!pkg.requestedPermissions.contains(npi.name)) { + Log.i(TAG, "Impliciting adding " + npi.name + " to old pkg " + + pkg.packageName); + pkg.requestedPermissions.add(npi.name); + } } if (pkg.usesLibraries.size() > 0) { pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; pkg.usesLibraries.toArray(pkg.usesLibraryFiles); } - + + 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) { int densities[] = pkg.supportsDensities = new int[size]; @@ -1142,6 +1255,19 @@ public class PackageParser { outError); } + boolean allowBackup = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true); + if (allowBackup) { + ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; + String backupAgent = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestApplication_backupAgent); + if (backupAgent != null) { + ai.backupAgentName = buildClassName(pkgName, backupAgent, outError); + Log.v(TAG, "android:backupAgent = " + ai.backupAgentName + + " from " + pkgName + "+" + backupAgent); + } + } + TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestApplication_label); if (v != null && (ai.labelRes=v.resourceId) == 0) { @@ -1298,21 +1424,6 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals("supports-density")) { - sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AndroidManifestSupportsDensity); - - int density = sa.getInteger( - com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1); - - sa.recycle(); - - if (density != -1 && !owner.supportsDensityList.contains(density)) { - owner.supportsDensityList.add(density); - } - - XmlUtils.skipCurrentTag(parser); - } else { if (!RIGID_PARSER) { Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); @@ -2219,6 +2330,17 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; + // For use by package manager service to keep track of which apps + // have been installed with forward locking. + public boolean mForwardLocked; + + // For use by the package manager to keep track of the path to the + // file an app came from. + public String mScanPath; + + // For use by package manager to keep track of where it has done dexopt. + public boolean mDidDexOpt; + // Additional data supplied by callers. public Object mExtras; @@ -2368,7 +2490,7 @@ public class PackageParser { return true; } if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0 - && p.supportsDensities != null) { + && p.supportsDensities != null) { return true; } return false; diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 231e3e24a27c..a37e4e8cc3bf 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -16,6 +16,7 @@ package android.content.res; +import android.os.MemoryFile; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -24,6 +25,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.FileChannel; /** * File descriptor of an entry in the AssetManager. This provides your own @@ -124,6 +127,13 @@ public class AssetFileDescriptor implements Parcelable { } /** + * Checks whether this file descriptor is for a memory file. + */ + private boolean isMemoryFile() throws IOException { + return MemoryFile.isMemoryFile(mFd.getFileDescriptor()); + } + + /** * Create and return a new auto-close input stream for this asset. This * will either return a full asset {@link AutoCloseInputStream}, or * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream @@ -132,6 +142,12 @@ public class AssetFileDescriptor implements Parcelable { * should only call this once for a particular asset. */ public FileInputStream createInputStream() throws IOException { + if (isMemoryFile()) { + if (mLength > Integer.MAX_VALUE) { + throw new IOException("File length too large for a memory file: " + mLength); + } + return new AutoCloseMemoryFileInputStream(mFd, (int)mLength); + } if (mLength < 0) { return new ParcelFileDescriptor.AutoCloseInputStream(mFd); } @@ -262,6 +278,66 @@ public class AssetFileDescriptor implements Parcelable { } /** + * An input stream that reads from a MemoryFile and closes it when the stream is closed. + * This extends FileInputStream just because {@link #createInputStream} returns + * a FileInputStream. All the FileInputStream methods are + * overridden to use the MemoryFile instead. + */ + private static class AutoCloseMemoryFileInputStream extends FileInputStream { + private ParcelFileDescriptor mParcelFd; + private MemoryFile mFile; + private InputStream mStream; + + public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length) + throws IOException { + super(fd.getFileDescriptor()); + mParcelFd = fd; + mFile = new MemoryFile(fd.getFileDescriptor(), length, "r"); + mStream = mFile.getInputStream(); + } + + @Override + public int available() throws IOException { + return mStream.available(); + } + + @Override + public void close() throws IOException { + mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor, + // since it could be a subclass of ParcelFileDescriptor. + // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases + // a content provider + mFile.close(); // to unmap the memory file from the address space. + mStream.close(); // doesn't actually do anything + } + + @Override + public FileChannel getChannel() { + return null; + } + + @Override + public int read() throws IOException { + return mStream.read(); + } + + @Override + public int read(byte[] buffer, int offset, int count) throws IOException { + return mStream.read(buffer, offset, count); + } + + @Override + public int read(byte[] buffer) throws IOException { + return mStream.read(buffer); + } + + @Override + public long skip(long count) throws IOException { + return mStream.skip(count); + } + } + + /** * An OutputStream you can create on a ParcelFileDescriptor, which will * take care of calling {@link ParcelFileDescriptor#close * ParcelFileDescritor.close()} for you when the stream is closed. @@ -345,4 +421,16 @@ public class AssetFileDescriptor implements Parcelable { return new AssetFileDescriptor[size]; } }; + + /** + * Creates an AssetFileDescriptor from a memory file. + * + * @hide + */ + public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile) + throws IOException { + ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor(); + return new AssetFileDescriptor(fd, 0, memoryFile.length()); + } + } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 1c9173694d2b..5c7b01fa0ccb 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 new file mode 100644 index 000000000000..dfe304d1593d --- /dev/null +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.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 + * running under. + * + * {@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(); + + /** + * The default width of the screen in portrait mode. + */ + public static final int DEFAULT_PORTRAIT_WIDTH = 320; + + /** + * The default height of the screen in portrait mode. + */ + public static final int DEFAULT_PORTRAIT_HEIGHT = 480; + + /** + * The x-shift mode that controls the position of the content or the window under + * compatibility mode. + * {@see getTranslator} + * {@see Translator#mShiftMode} + */ + 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; + + + /** + * A compatibility flags + */ + private int mCompatibilityFlags; + + /** + * 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} + */ + 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; + + /** + * Application's scale. + */ + 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; + + /** + * 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. + */ + private int mXOffset; + + public CompatibilityInfo(ApplicationInfo appInfo) { + appFlags = appInfo.flags; + + if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; + } + + float packageDensityScale = -1.0f; + if (appInfo.supportsDensities != null) { + int minDiff = Integer.MAX_VALUE; + for (int density : appInfo.supportsDensities) { + if (density == ApplicationInfo.ANY_DENSITY) { + packageDensityScale = 1.0f; + break; + } + int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density); + if (tmpDiff == 0) { + packageDensityScale = 1.0f; + break; + } + // prefer higher density (appScale>1.0), unless that's only option. + if (tmpDiff < minDiff && packageDensityScale < 1.0f) { + packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density; + minDiff = tmpDiff; + } + } + } + if (packageDensityScale > 0.0f) { + applicationScale = packageDensityScale; + } else { + applicationScale = + DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY; + } + applicationInvertedScale = 1.0f / applicationScale; + if (applicationScale != 1.0f) { + mCompatibilityFlags |= SCALING_REQUIRED; + } + } + + private CompatibilityInfo(int appFlags, int compFlags, float scale, float invertedScale) { + this.appFlags = appFlags; + mCompatibilityFlags = compFlags; + applicationScale = scale; + applicationInvertedScale = invertedScale; + } + + private CompatibilityInfo() { + this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS, + EXPANDABLE | CONFIGURED_EXPANDABLE, + 1.0f, + 1.0f); + } + + /** + * Returns the copy of this instance. + */ + public CompatibilityInfo copy() { + CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags, + applicationScale, applicationInvertedScale); + info.setVisibleRect(mXOffset, mWidth, mHeight); + return info; + } + + /** + * 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=" + 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; + } + } +} diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index bb3486c20554..577aa600a19e 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 665e40ca362f..49ad656f4cc6 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -22,9 +22,9 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.pm.ApplicationInfo; import android.graphics.Movie; import android.graphics.drawable.Drawable; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.SystemProperties; @@ -33,6 +33,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; +import android.util.LongSparseArray; import java.io.IOException; import java.io.InputStream; @@ -57,19 +58,19 @@ public class Resources { // Information about preloaded resources. Note that they are not // protected by a lock, because while preloading in zygote we are all // single-threaded, and after that these are immutable. - private static final SparseArray<Drawable.ConstantState> sPreloadedDrawables - = new SparseArray<Drawable.ConstantState>(); + private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables + = new LongSparseArray<Drawable.ConstantState>(); private static final SparseArray<ColorStateList> mPreloadedColorStateLists = new SparseArray<ColorStateList>(); private static boolean mPreloaded; - private final SparseArray<Drawable.ConstantState> mPreloadedDrawables; + private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables; /*package*/ final TypedValue mTmpValue = new TypedValue(); // These are protected by the mTmpValue lock. - private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache - = new SparseArray<WeakReference<Drawable.ConstantState> >(); + private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache + = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache = new SparseArray<WeakReference<ColorStateList> >(); private boolean mPreloading; @@ -84,21 +85,23 @@ public class Resources { private final Configuration mConfiguration = new Configuration(); /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); PluralRules mPluralRule; + + private final CompatibilityInfo mCompatibilityInfo; - private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() { + private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() { @Override - public void put(int k, Object o) { + public void put(long k, Object o) { throw new UnsupportedOperationException(); } @Override - public void append(int k, Object o) { + public void append(long k, Object o) { throw new UnsupportedOperationException(); } }; @SuppressWarnings("unchecked") - private static <T> SparseArray<T> emptySparseArray() { - return (SparseArray<T>) EMPTY_ARRAY; + private static <T> LongSparseArray<T> emptySparseArray() { + return (LongSparseArray<T>) EMPTY_ARRAY; } /** @@ -126,26 +129,59 @@ public class Resources { */ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { - this(assets, metrics, config, true); + this(assets, metrics, config, (ApplicationInfo) null); } /** - * Create a resource with an additional flag for preloaded - * drawable cache. Used by {@link ActivityThread}. - * + * Creates a new Resources object with ApplicationInfo. + * + * @param assets Previously created AssetManager. + * @param metrics Current display metrics to consider when + * selecting/computing resource values. + * @param config Desired device configuration to consider when + * selecting/computing resource values (optional). + * @param appInfo this resource's application info. * @hide */ public Resources(AssetManager assets, DisplayMetrics metrics, - Configuration config, boolean usePreloadedCache) { + Configuration config, ApplicationInfo appInfo) { mAssets = assets; mConfiguration.setToDefaults(); mMetrics.setToDefaults(); + if (appInfo != null) { + mCompatibilityInfo = new CompatibilityInfo(appInfo); + if (DEBUG_CONFIG) { + Log.d(TAG, "compatibility for " + appInfo.packageName + " : " + mCompatibilityInfo); + } + } else { + mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + } updateConfiguration(config, metrics); assets.ensureStringBlocks(); - if (usePreloadedCache) { - mPreloadedDrawables = sPreloadedDrawables; + if (mCompatibilityInfo.isScalingRequired()) { + mPreloadedDrawables = emptySparseArray(); } else { + mPreloadedDrawables = sPreloadedDrawables; + } + } + + /** + * Creates a new resources that uses the given compatibility info. Used to create + * a context for widgets using the container's compatibility info. + * {@see ApplicationContext#createPackageCotnext}. + * @hide + */ + public Resources(AssetManager assets, DisplayMetrics metrics, + Configuration config, CompatibilityInfo info) { + mAssets = assets; + mMetrics.setToDefaults(); + mCompatibilityInfo = info; + updateConfiguration(config, metrics); + assets.ensureStringBlocks(); + if (mCompatibilityInfo.isScalingRequired()) { mPreloadedDrawables = emptySparseArray(); + } else { + mPreloadedDrawables = sPreloadedDrawables; } } @@ -1238,7 +1274,7 @@ public class Resources { return array; } - + /** * Store the newly updated configuration. */ @@ -1251,6 +1287,8 @@ public class Resources { } if (metrics != null) { mMetrics.setTo(metrics); + mMetrics.updateMetrics(mCompatibilityInfo, + mConfiguration.orientation, mConfiguration.screenLayout); } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; @@ -1282,7 +1320,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" @@ -1297,14 +1335,14 @@ public class Resources { configChanges, cs.getChangingConfigurations())) { if (DEBUG_CONFIG) { Log.d(TAG, "FLUSHING #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations())); } mDrawableCache.setValueAt(i, null); } else if (DEBUG_CONFIG) { Log.d(TAG, "(Keeping #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations()) + ")"); @@ -1356,6 +1394,17 @@ public class Resources { public Configuration getConfiguration() { return mConfiguration; } + + /** + * Return the compatibility mode information for the application. + * The returned object should be treated as read-only. + * + * @return compatibility info. null if the app does not require compatibility mode. + * @hide + */ + public CompatibilityInfo getCompatibilityInfo() { + return mCompatibilityInfo; + } /** * Return a resource identifier for the given resource name. A fully @@ -1624,7 +1673,7 @@ public class Resources { } } - final int key = (value.assetCookie << 24) | value.data; + final long key = (((long) value.assetCookie) << 32) | value.data; Drawable dr = getCachedDrawable(key); if (dr != null) { @@ -1704,7 +1753,7 @@ public class Resources { return dr; } - private Drawable getCachedDrawable(int key) { + private Drawable getCachedDrawable(long key) { synchronized (mTmpValue) { WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); if (wr != null) { // we have the key @@ -1920,5 +1969,6 @@ public class Resources { updateConfiguration(null, null); mAssets.ensureStringBlocks(); mPreloadedDrawables = sPreloadedDrawables; + mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; } } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index c26810a6248f..cf30dd9a9112 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -247,9 +247,11 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { return mBulkCursor.respond(extras); } catch (RemoteException e) { - // This should never happen because the system kills processes that are using remote - // cursors when the provider process is killed. - throw new RuntimeException(e); + // the system kills processes that are using remote cursors when the provider process + // is killed, but this can still happen if this is being called from the system process, + // so, better to log and return an empty bundle. + Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e); + return Bundle.EMPTY; } } } diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java new file mode 100644 index 000000000000..2800d86279b1 --- /dev/null +++ b/core/java/android/database/sqlite/SQLiteContentHelper.java @@ -0,0 +1,92 @@ +/* + * 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.database.sqlite; + +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.os.MemoryFile; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * Some helper functions for using SQLite database to implement content providers. + * + * @hide + */ +public class SQLiteContentHelper { + + /** + * Runs an SQLite query and returns an AssetFileDescriptor for the + * blob in column 0 of the first row. If the first column does + * not contain a blob, an unspecified exception is thrown. + * + * @param db Handle to a readable database. + * @param sql SQL query, possibly with query arguments. + * @param selectionArgs Query argument values, or {@code null} for no argument. + * @return If no exception is thrown, a non-null AssetFileDescriptor is returned. + * @throws FileNotFoundException If the query returns no results or the + * value of column 0 is NULL, or if there is an error creating the + * asset file descriptor. + */ + public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql, + String[] selectionArgs) throws FileNotFoundException { + try { + MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs); + if (file == null) { + throw new FileNotFoundException("No results."); + } + return AssetFileDescriptor.fromMemoryFile(file); + } catch (IOException ex) { + throw new FileNotFoundException(ex.toString()); + } + } + + /** + * Runs an SQLite query and returns a MemoryFile for the + * blob in column 0 of the first row. If the first column does + * not contain a blob, an unspecified exception is thrown. + * + * @return A memory file, or {@code null} if the query returns no results + * or the value column 0 is NULL. + * @throws IOException If there is an error creating the memory file. + */ + // TODO: make this native and use the SQLite blob API to reduce copying + private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql, + String[] selectionArgs) throws IOException { + Cursor cursor = db.rawQuery(sql, selectionArgs); + if (cursor == null) { + return null; + } + try { + if (!cursor.moveToFirst()) { + return null; + } + byte[] bytes = cursor.getBlob(0); + if (bytes == null) { + return null; + } + MemoryFile file = new MemoryFile(null, bytes.length); + file.writeBytes(bytes, 0, 0, bytes.length); + file.deactivate(); + return file; + } finally { + cursor.close(); + } + } + +} diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index ab7c827cc7c8..8a639196ee7a 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -18,16 +18,15 @@ package android.database.sqlite; import android.database.Cursor; import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; import android.provider.BaseColumns; import android.text.TextUtils; -import android.util.Config; import android.util.Log; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Map.Entry; +import java.util.regex.Pattern; /** * This is a convience class that helps build SQL queries to be sent to @@ -36,10 +35,12 @@ import java.util.Map.Entry; public class SQLiteQueryBuilder { private static final String TAG = "SQLiteQueryBuilder"; + private static final Pattern sLimitPattern = + Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private Map<String, String> mProjectionMap = null; private String mTables = ""; - private StringBuilder mWhereClause = new StringBuilder(64); + private final StringBuilder mWhereClause = new StringBuilder(64); private boolean mDistinct; private SQLiteDatabase.CursorFactory mFactory; @@ -169,6 +170,9 @@ public class SQLiteQueryBuilder throw new IllegalArgumentException( "HAVING clauses are only permitted when using a groupBy clause"); } + if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) { + throw new IllegalArgumentException("invalid LIMIT clauses:" + limit); + } StringBuilder query = new StringBuilder(120); @@ -187,7 +191,7 @@ public class SQLiteQueryBuilder appendClause(query, " GROUP BY ", groupBy); appendClause(query, " HAVING ", having); appendClause(query, " ORDER BY ", orderBy); - appendClauseEscapeClause(query, " LIMIT ", limit); + appendClause(query, " LIMIT ", limit); return query.toString(); } diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java new file mode 100755 index 000000000000..2262477a93c4 --- /dev/null +++ b/core/java/android/gesture/Gesture.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; + +/** + * A gesture can have a single or multiple strokes + */ + +public class Gesture implements Parcelable { + private static final long GESTURE_ID_BASE = System.currentTimeMillis(); + + private static final int BITMAP_RENDERING_WIDTH = 2; + + private static final boolean BITMAP_RENDERING_ANTIALIAS = true; + private static final boolean BITMAP_RENDERING_DITHER = true; + + private static int sGestureCount = 0; + + private final RectF mBoundingBox = new RectF(); + + // the same as its instance ID + private long mGestureID; + + private final ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>(); + + public Gesture() { + mGestureID = GESTURE_ID_BASE + sGestureCount++; + } + + void recycle() { + mStrokes.clear(); + mBoundingBox.setEmpty(); + } + + /** + * @return all the strokes of the gesture + */ + public ArrayList<GestureStroke> getStrokes() { + return mStrokes; + } + + /** + * @return the number of strokes included by this gesture + */ + public int getStrokesCount() { + return mStrokes.size(); + } + + /** + * Add a stroke to the gesture + * + * @param stroke + */ + public void addStroke(GestureStroke stroke) { + mStrokes.add(stroke); + mBoundingBox.union(stroke.boundingBox); + } + + /** + * Get the total length of the gesture. When there are multiple strokes in + * the gesture, this returns the sum of the lengths of all the strokes + * + * @return the length of the gesture + */ + public float getLength() { + int len = 0; + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + len += strokes.get(i).length; + } + + return len; + } + + /** + * @return the bounding box of the gesture + */ + public RectF getBoundingBox() { + return mBoundingBox; + } + + public Path toPath() { + return toPath(null); + } + + public Path toPath(Path path) { + if (path == null) path = new Path(); + + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + path.addPath(strokes.get(i).getPath()); + } + + return path; + } + + public Path toPath(int width, int height, int edge, int numSample) { + return toPath(null, width, height, edge, numSample); + } + + public Path toPath(Path path, int width, int height, int edge, int numSample) { + if (path == null) path = new Path(); + + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + path.addPath(strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample)); + } + + return path; + } + + /** + * Set the id of the gesture + * + * @param id + */ + void setID(long id) { + mGestureID = id; + } + + /** + * @return the id of the gesture + */ + public long getID() { + return mGestureID; + } + + /** + * draw the gesture + * + * @param canvas + */ + void draw(Canvas canvas, Paint paint) { + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + strokes.get(i).draw(canvas, paint); + } + } + + /** + * Create a bitmap of the gesture with a transparent background + * + * @param width width of the target bitmap + * @param height height of the target bitmap + * @param edge the edge + * @param numSample + * @param color + * @return the bitmap + */ + public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) { + final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + + canvas.translate(edge, edge); + + final Paint paint = new Paint(); + paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS); + paint.setDither(BITMAP_RENDERING_DITHER); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(BITMAP_RENDERING_WIDTH); + + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + Path path = strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample); + canvas.drawPath(path, paint); + } + + return bitmap; + } + + /** + * Create a bitmap of the gesture with a transparent background + * + * @param width + * @param height + * @param inset + * @param color + * @return the bitmap + */ + public Bitmap toBitmap(int width, int height, int inset, int color) { + final Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + + final Paint paint = new Paint(); + paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS); + paint.setDither(BITMAP_RENDERING_DITHER); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(BITMAP_RENDERING_WIDTH); + + final Path path = toPath(); + final RectF bounds = new RectF(); + path.computeBounds(bounds, true); + + final float sx = (width - 2 * inset) / bounds.width(); + final float sy = (height - 2 * inset) / bounds.height(); + final float scale = sx > sy ? sy : sx; + paint.setStrokeWidth(2.0f / scale); + + path.offset(-bounds.left + (width - bounds.width() * scale) / 2.0f, + -bounds.top + (height - bounds.height() * scale) / 2.0f); + + canvas.translate(inset, inset); + canvas.scale(scale, scale); + + canvas.drawPath(path, paint); + + return bitmap; + } + + void serialize(DataOutputStream out) throws IOException { + final ArrayList<GestureStroke> strokes = mStrokes; + final int count = strokes.size(); + + // Write gesture ID + out.writeLong(mGestureID); + // Write number of strokes + out.writeInt(count); + + for (int i = 0; i < count; i++) { + strokes.get(i).serialize(out); + } + } + + static Gesture deserialize(DataInputStream in) throws IOException { + final Gesture gesture = new Gesture(); + + // Gesture ID + gesture.mGestureID = in.readLong(); + // Number of strokes + final int count = in.readInt(); + + for (int i = 0; i < count; i++) { + gesture.addStroke(GestureStroke.deserialize(in)); + } + + return gesture; + } + + public static final Parcelable.Creator<Gesture> CREATOR = new Parcelable.Creator<Gesture>() { + public Gesture createFromParcel(Parcel in) { + Gesture gesture = null; + final long gestureID = in.readLong(); + + final DataInputStream inStream = new DataInputStream( + new ByteArrayInputStream(in.createByteArray())); + + try { + gesture = deserialize(inStream); + } catch (IOException e) { + Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e); + } finally { + GestureUtilities.closeStream(inStream); + } + + if (gesture != null) { + gesture.mGestureID = gestureID; + } + + return gesture; + } + + public Gesture[] newArray(int size) { + return new Gesture[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mGestureID); + + boolean result = false; + final ByteArrayOutputStream byteStream = + new ByteArrayOutputStream(GestureConstants.IO_BUFFER_SIZE); + final DataOutputStream outStream = new DataOutputStream(byteStream); + + try { + serialize(outStream); + result = true; + } catch (IOException e) { + Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e); + } finally { + GestureUtilities.closeStream(outStream); + GestureUtilities.closeStream(byteStream); + } + + if (result) { + out.writeByteArray(byteStream.toByteArray()); + } + } + + public int describeContents() { + return 0; + } +} + diff --git a/core/java/android/gesture/GestureConstants.java b/core/java/android/gesture/GestureConstants.java new file mode 100644 index 000000000000..230db0c00c50 --- /dev/null +++ b/core/java/android/gesture/GestureConstants.java @@ -0,0 +1,26 @@ +/* + * 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.gesture; + +interface GestureConstants { + static final int STROKE_STRING_BUFFER_SIZE = 1024; + static final int STROKE_POINT_BUFFER_SIZE = 100; // number of points + + static final int IO_BUFFER_SIZE = 32 * 1024; // 32K + + static final String LOG_TAG = "Gestures"; +} diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java new file mode 100644 index 000000000000..6d6c156def9a --- /dev/null +++ b/core/java/android/gesture/GestureLibraries.java @@ -0,0 +1,143 @@ +/* + * 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.gesture; + +import android.util.Log; +import static android.gesture.GestureConstants.*; +import android.content.Context; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.ref.WeakReference; + +public final class GestureLibraries { + private GestureLibraries() { + } + + public static GestureLibrary fromFile(String path) { + return fromFile(new File(path)); + } + + public static GestureLibrary fromFile(File path) { + return new FileGestureLibrary(path); + } + + public static GestureLibrary fromPrivateFile(Context context, String name) { + return fromFile(context.getFileStreamPath(name)); + } + + public static GestureLibrary fromRawResource(Context context, int resourceId) { + return new ResourceGestureLibrary(context, resourceId); + } + + private static class FileGestureLibrary extends GestureLibrary { + private final File mPath; + + public FileGestureLibrary(File path) { + mPath = path; + } + + @Override + public boolean isReadOnly() { + return !mPath.canWrite(); + } + + public boolean save() { + if (!mStore.hasChanged()) return true; + + final File file = mPath; + + final File parentFile = file.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + return false; + } + } + + boolean result = false; + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + mStore.save(new FileOutputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } + + return result; + } + + public boolean load() { + boolean result = false; + final File file = mPath; + if (file.exists() && file.canRead()) { + try { + mStore.load(new FileInputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } + } + + return result; + } + } + + private static class ResourceGestureLibrary extends GestureLibrary { + private final WeakReference<Context> mContext; + private final int mResourceId; + + public ResourceGestureLibrary(Context context, int resourceId) { + mContext = new WeakReference<Context>(context); + mResourceId = resourceId; + } + + @Override + public boolean isReadOnly() { + return true; + } + + public boolean save() { + return false; + } + + public boolean load() { + boolean result = false; + final Context context = mContext.get(); + if (context != null) { + final InputStream in = context.getResources().openRawResource(mResourceId); + try { + mStore.load(in, true); + result = true; + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from raw resource " + + context.getResources().getResourceName(mResourceId), e); + } + } + + return result; + } + } +} diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java new file mode 100644 index 000000000000..a29c2c83cfbf --- /dev/null +++ b/core/java/android/gesture/GestureLibrary.java @@ -0,0 +1,81 @@ +/* + * 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.gesture; + +import java.util.Set; +import java.util.ArrayList; + +public abstract class GestureLibrary { + protected final GestureStore mStore; + + protected GestureLibrary() { + mStore = new GestureStore(); + } + + public abstract boolean save(); + + public abstract boolean load(); + + public boolean isReadOnly() { + return false; + } + + public Learner getLearner() { + return mStore.getLearner(); + } + + public void setOrientationStyle(int style) { + mStore.setOrientationStyle(style); + } + + public int getOrientationStyle() { + return mStore.getOrientationStyle(); + } + + public void setSequenceType(int type) { + mStore.setSequenceType(type); + } + + public int getSequenceType() { + return mStore.getSequenceType(); + } + + public Set<String> getGestureEntries() { + return mStore.getGestureEntries(); + } + + public ArrayList<Prediction> recognize(Gesture gesture) { + return mStore.recognize(gesture); + } + + public void addGesture(String entryName, Gesture gesture) { + mStore.addGesture(entryName, gesture); + } + + public void removeGesture(String entryName, Gesture gesture) { + mStore.removeGesture(entryName, gesture); + } + + public void removeEntry(String entryName) { + mStore.removeEntry(entryName); + } + + public ArrayList<Gesture> getGestures(String entryName) { + return mStore.getGestures(entryName); + } +} diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java new file mode 100755 index 000000000000..5bfdcc4a5578 --- /dev/null +++ b/core/java/android/gesture/GestureOverlayView.java @@ -0,0 +1,793 @@ +/* + * 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.gesture; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.animation.AnimationUtils; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.FrameLayout; +import android.os.SystemClock; +import com.android.internal.R; + +import java.util.ArrayList; + +/** + * A transparent overlay for gesture input that can be placed on top of other + * widgets or contain other widgets. + * + * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled + * @attr ref android.R.styleable#GestureOverlayView_fadeDuration + * @attr ref android.R.styleable#GestureOverlayView_fadeOffset + * @attr ref android.R.styleable#GestureOverlayView_fadeEnabled + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeType + * @attr ref android.R.styleable#GestureOverlayView_gestureColor + * @attr ref android.R.styleable#GestureOverlayView_orientation + * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor + */ +public class GestureOverlayView extends FrameLayout { + public static final int GESTURE_STROKE_TYPE_SINGLE = 0; + public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1; + + public static final int ORIENTATION_HORIZONTAL = 0; + public static final int ORIENTATION_VERTICAL = 1; + + private static final int FADE_ANIMATION_RATE = 16; + private static final boolean GESTURE_RENDERING_ANTIALIAS = true; + private static final boolean DITHER_FLAG = true; + + private final Paint mGesturePaint = new Paint(); + + private long mFadeDuration = 150; + private long mFadeOffset = 420; + private long mFadingStart; + private boolean mFadingHasStarted; + private boolean mFadeEnabled = true; + + private int mCurrentColor; + private int mCertainGestureColor = 0xFFFFFF00; + private int mUncertainGestureColor = 0x48FFFF00; + private float mGestureStrokeWidth = 12.0f; + private int mInvalidateExtraBorder = 10; + + private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE; + private float mGestureStrokeLengthThreshold = 50.0f; + private float mGestureStrokeSquarenessTreshold = 0.275f; + private float mGestureStrokeAngleThreshold = 40.0f; + + private int mOrientation = ORIENTATION_VERTICAL; + + private final Rect mInvalidRect = new Rect(); + private final Path mPath = new Path(); + private boolean mGestureVisible = true; + + private float mX; + private float mY; + + private float mCurveEndX; + private float mCurveEndY; + + private float mTotalLength; + private boolean mIsGesturing = false; + private boolean mPreviousWasGesturing = false; + private boolean mInterceptEvents = true; + private boolean mIsListeningForGestures; + private boolean mResetGesture; + + // current gesture + private Gesture mCurrentGesture; + private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); + + // TODO: Make this a list of WeakReferences + private final ArrayList<OnGestureListener> mOnGestureListeners = + new ArrayList<OnGestureListener>(); + // TODO: Make this a list of WeakReferences + private final ArrayList<OnGesturePerformedListener> mOnGesturePerformedListeners = + new ArrayList<OnGesturePerformedListener>(); + // TODO: Make this a list of WeakReferences + private final ArrayList<OnGesturingListener> mOnGesturingListeners = + new ArrayList<OnGesturingListener>(); + + private boolean mHandleGestureActions; + + // fading out effect + private boolean mIsFadingOut = false; + private float mFadingAlpha = 1.0f; + private final AccelerateDecelerateInterpolator mInterpolator = + new AccelerateDecelerateInterpolator(); + + private final FadeOutRunnable mFadingOut = new FadeOutRunnable(); + + public GestureOverlayView(Context context) { + super(context); + init(); + } + + public GestureOverlayView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle); + } + + public GestureOverlayView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.GestureOverlayView, defStyle, 0); + + mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth, + mGestureStrokeWidth); + mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1); + mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor, + mCertainGestureColor); + mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor, + mUncertainGestureColor); + mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration); + mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset); + mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType, + mGestureStrokeType); + mGestureStrokeLengthThreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeLengthThreshold, + mGestureStrokeLengthThreshold); + mGestureStrokeAngleThreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeAngleThreshold, + mGestureStrokeAngleThreshold); + mGestureStrokeSquarenessTreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold, + mGestureStrokeSquarenessTreshold); + mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled, + mInterceptEvents); + mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled, + mFadeEnabled); + mOrientation = a.getInt(R.styleable.GestureOverlayView_orientation, mOrientation); + + a.recycle(); + + init(); + } + + private void init() { + setWillNotDraw(false); + + final Paint gesturePaint = mGesturePaint; + gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS); + gesturePaint.setColor(mCertainGestureColor); + gesturePaint.setStyle(Paint.Style.STROKE); + gesturePaint.setStrokeJoin(Paint.Join.ROUND); + gesturePaint.setStrokeCap(Paint.Cap.ROUND); + gesturePaint.setStrokeWidth(mGestureStrokeWidth); + gesturePaint.setDither(DITHER_FLAG); + + mCurrentColor = mCertainGestureColor; + setPaintAlpha(255); + } + + public ArrayList<GesturePoint> getCurrentStroke() { + return mStrokeBuffer; + } + + public int getOrientation() { + return mOrientation; + } + + public void setOrientation(int orientation) { + mOrientation = orientation; + } + + public void setGestureColor(int color) { + mCertainGestureColor = color; + } + + public void setUncertainGestureColor(int color) { + mUncertainGestureColor = color; + } + + public int getUncertainGestureColor() { + return mUncertainGestureColor; + } + + public int getGestureColor() { + return mCertainGestureColor; + } + + public float getGestureStrokeWidth() { + return mGestureStrokeWidth; + } + + public void setGestureStrokeWidth(float gestureStrokeWidth) { + mGestureStrokeWidth = gestureStrokeWidth; + mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1); + mGesturePaint.setStrokeWidth(gestureStrokeWidth); + } + + public int getGestureStrokeType() { + return mGestureStrokeType; + } + + public void setGestureStrokeType(int gestureStrokeType) { + mGestureStrokeType = gestureStrokeType; + } + + public float getGestureStrokeLengthThreshold() { + return mGestureStrokeLengthThreshold; + } + + public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) { + mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold; + } + + public float getGestureStrokeSquarenessTreshold() { + return mGestureStrokeSquarenessTreshold; + } + + public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) { + mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold; + } + + public float getGestureStrokeAngleThreshold() { + return mGestureStrokeAngleThreshold; + } + + public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) { + mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold; + } + + public boolean isEventsInterceptionEnabled() { + return mInterceptEvents; + } + + public void setEventsInterceptionEnabled(boolean enabled) { + mInterceptEvents = enabled; + } + + public boolean isFadeEnabled() { + return mFadeEnabled; + } + + public void setFadeEnabled(boolean fadeEnabled) { + mFadeEnabled = fadeEnabled; + } + + public Gesture getGesture() { + return mCurrentGesture; + } + + public void setGesture(Gesture gesture) { + if (mCurrentGesture != null) { + clear(false); + } + + setCurrentColor(mCertainGestureColor); + mCurrentGesture = gesture; + + final Path path = mCurrentGesture.toPath(); + final RectF bounds = new RectF(); + path.computeBounds(bounds, true); + + // TODO: The path should also be scaled to fit inside this view + mPath.rewind(); + mPath.addPath(path, -bounds.left + (getWidth() - bounds.width()) / 2.0f, + -bounds.top + (getHeight() - bounds.height()) / 2.0f); + + mResetGesture = true; + + invalidate(); + } + + public Path getGesturePath() { + return mPath; + } + + public Path getGesturePath(Path path) { + path.set(mPath); + return path; + } + + public boolean isGestureVisible() { + return mGestureVisible; + } + + public void setGestureVisible(boolean visible) { + mGestureVisible = visible; + } + + public long getFadeOffset() { + return mFadeOffset; + } + + public void setFadeOffset(long fadeOffset) { + mFadeOffset = fadeOffset; + } + + public void addOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.add(listener); + } + + public void removeOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.remove(listener); + } + + public void removeAllOnGestureListeners() { + mOnGestureListeners.clear(); + } + + public void addOnGesturePerformedListener(OnGesturePerformedListener listener) { + mOnGesturePerformedListeners.add(listener); + if (mOnGesturePerformedListeners.size() > 0) { + mHandleGestureActions = true; + } + } + + public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) { + mOnGesturePerformedListeners.remove(listener); + if (mOnGesturePerformedListeners.size() <= 0) { + mHandleGestureActions = false; + } + } + + public void removeAllOnGesturePerformedListeners() { + mOnGesturePerformedListeners.clear(); + mHandleGestureActions = false; + } + + public void addOnGesturingListener(OnGesturingListener listener) { + mOnGesturingListeners.add(listener); + } + + public void removeOnGesturingListener(OnGesturingListener listener) { + mOnGesturingListeners.remove(listener); + } + + public void removeAllOnGesturingListeners() { + mOnGesturingListeners.clear(); + } + + public boolean isGesturing() { + return mIsGesturing; + } + + private void setCurrentColor(int color) { + mCurrentColor = color; + if (mFadingHasStarted) { + setPaintAlpha((int) (255 * mFadingAlpha)); + } else { + setPaintAlpha(255); + } + invalidate(); + } + + /** + * @hide + */ + public Paint getGesturePaint() { + return mGesturePaint; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + if (mCurrentGesture != null && mGestureVisible) { + canvas.drawPath(mPath, mGesturePaint); + } + } + + private void setPaintAlpha(int alpha) { + alpha += alpha >> 7; + final int baseAlpha = mCurrentColor >>> 24; + final int useAlpha = baseAlpha * alpha >> 8; + mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24)); + } + + public void clear(boolean animated) { + clear(animated, false, true); + } + + private void clear(boolean animated, boolean fireActionPerformed, boolean immediate) { + setPaintAlpha(255); + removeCallbacks(mFadingOut); + mResetGesture = false; + mFadingOut.fireActionPerformed = fireActionPerformed; + mFadingOut.resetMultipleStrokes = false; + + if (animated && mCurrentGesture != null) { + mFadingAlpha = 1.0f; + mIsFadingOut = true; + mFadingHasStarted = false; + mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset; + + postDelayed(mFadingOut, mFadeOffset); + } else { + mFadingAlpha = 1.0f; + mIsFadingOut = false; + mFadingHasStarted = false; + + if (immediate) { + mCurrentGesture = null; + mPath.rewind(); + invalidate(); + } else if (fireActionPerformed) { + postDelayed(mFadingOut, mFadeOffset); + } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) { + mFadingOut.resetMultipleStrokes = true; + postDelayed(mFadingOut, mFadeOffset); + } else { + mCurrentGesture = null; + mPath.rewind(); + invalidate(); + } + } + } + + public void cancelClearAnimation() { + setPaintAlpha(255); + mIsFadingOut = false; + mFadingHasStarted = false; + removeCallbacks(mFadingOut); + mPath.rewind(); + mCurrentGesture = null; + } + + public void cancelGesture() { + mIsListeningForGestures = false; + + // add the stroke to the current gesture + mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); + + // pass the event to handlers + final long now = SystemClock.uptimeMillis(); + final MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + + final ArrayList<OnGestureListener> listeners = mOnGestureListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureCancelled(this, event); + } + + event.recycle(); + + clear(false); + mIsGesturing = false; + mPreviousWasGesturing = false; + mStrokeBuffer.clear(); + + final ArrayList<OnGesturingListener> otherListeners = mOnGesturingListeners; + count = otherListeners.size(); + for (int i = 0; i < count; i++) { + otherListeners.get(i).onGesturingEnded(this); + } + } + + @Override + protected void onDetachedFromWindow() { + cancelClearAnimation(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (isEnabled()) { + final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null && + mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) && + mInterceptEvents; + + processEvent(event); + + if (cancelDispatch) { + event.setAction(MotionEvent.ACTION_CANCEL); + } + + super.dispatchTouchEvent(event); + + return true; + } + + return super.dispatchTouchEvent(event); + } + + private boolean processEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchDown(event); + invalidate(); + return true; + case MotionEvent.ACTION_MOVE: + if (mIsListeningForGestures) { + Rect rect = touchMove(event); + if (rect != null) { + invalidate(rect); + } + return true; + } + break; + case MotionEvent.ACTION_UP: + if (mIsListeningForGestures) { + touchUp(event, false); + invalidate(); + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + if (mIsListeningForGestures) { + touchUp(event, true); + invalidate(); + return true; + } + } + + return false; + } + + private void touchDown(MotionEvent event) { + mIsListeningForGestures = true; + + float x = event.getX(); + float y = event.getY(); + + mX = x; + mY = y; + + mTotalLength = 0; + mIsGesturing = false; + + if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) { + if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor); + mResetGesture = false; + mCurrentGesture = null; + mPath.rewind(); + } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) { + if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor); + } + + // if there is fading out going on, stop it. + if (mFadingHasStarted) { + cancelClearAnimation(); + } else if (mIsFadingOut) { + setPaintAlpha(255); + mIsFadingOut = false; + mFadingHasStarted = false; + removeCallbacks(mFadingOut); + } + + if (mCurrentGesture == null) { + mCurrentGesture = new Gesture(); + } + + mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); + mPath.moveTo(x, y); + + final int border = mInvalidateExtraBorder; + mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border); + + mCurveEndX = x; + mCurveEndY = y; + + // pass the event to handlers + final ArrayList<OnGestureListener> listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureStarted(this, event); + } + } + + private Rect touchMove(MotionEvent event) { + Rect areaToRefresh = null; + + final float x = event.getX(); + final float y = event.getY(); + + final float previousX = mX; + final float previousY = mY; + + final float dx = Math.abs(x - previousX); + final float dy = Math.abs(y - previousY); + + if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) { + areaToRefresh = mInvalidRect; + + // start with the curve end + final int border = mInvalidateExtraBorder; + areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border, + (int) mCurveEndX + border, (int) mCurveEndY + border); + + float cX = mCurveEndX = (x + previousX) / 2; + float cY = mCurveEndY = (y + previousY) / 2; + + mPath.quadTo(previousX, previousY, cX, cY); + + // union with the control point of the new curve + areaToRefresh.union((int) previousX - border, (int) previousY - border, + (int) previousX + border, (int) previousY + border); + + // union with the end point of the new curve + areaToRefresh.union((int) cX - border, (int) cY - border, + (int) cX + border, (int) cY + border); + + mX = x; + mY = y; + + mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); + + if (mHandleGestureActions && !mIsGesturing) { + mTotalLength += (float) Math.sqrt(dx * dx + dy * dy); + + if (mTotalLength > mGestureStrokeLengthThreshold) { + final OrientedBoundingBox box = + GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer); + + float angle = Math.abs(box.orientation); + if (angle > 90) { + angle = 180 - angle; + } + + if (box.squareness > mGestureStrokeSquarenessTreshold || + (mOrientation == ORIENTATION_VERTICAL ? + angle < mGestureStrokeAngleThreshold : + angle > mGestureStrokeAngleThreshold)) { + + mIsGesturing = true; + setCurrentColor(mCertainGestureColor); + + final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesturingStarted(this); + } + } + } + } + + // pass the event to handlers + final ArrayList<OnGestureListener> listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesture(this, event); + } + } + + return areaToRefresh; + } + + private void touchUp(MotionEvent event, boolean cancel) { + mIsListeningForGestures = false; + + // A gesture wasn't started or was cancelled + if (mCurrentGesture != null) { + // add the stroke to the current gesture + mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); + + if (!cancel) { + // pass the event to handlers + final ArrayList<OnGestureListener> listeners = mOnGestureListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureEnded(this, event); + } + + clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing, + false); + } else { + cancelGesture(event); + + } + } else { + cancelGesture(event); + } + + mStrokeBuffer.clear(); + mPreviousWasGesturing = mIsGesturing; + mIsGesturing = false; + + final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesturingEnded(this); + } + } + + private void cancelGesture(MotionEvent event) { + // pass the event to handlers + final ArrayList<OnGestureListener> listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureCancelled(this, event); + } + + clear(false); + } + + private void fireOnGesturePerformed() { + final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners; + final int count = actionListeners.size(); + for (int i = 0; i < count; i++) { + actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture); + } + } + + private class FadeOutRunnable implements Runnable { + boolean fireActionPerformed; + boolean resetMultipleStrokes; + + public void run() { + if (mIsFadingOut) { + final long now = AnimationUtils.currentAnimationTimeMillis(); + final long duration = now - mFadingStart; + + if (duration > mFadeDuration) { + if (fireActionPerformed) { + fireOnGesturePerformed(); + } + + mPreviousWasGesturing = false; + mIsFadingOut = false; + mFadingHasStarted = false; + mPath.rewind(); + mCurrentGesture = null; + setPaintAlpha(255); + } else { + mFadingHasStarted = true; + float interpolatedTime = Math.max(0.0f, + Math.min(1.0f, duration / (float) mFadeDuration)); + mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime); + setPaintAlpha((int) (255 * mFadingAlpha)); + postDelayed(this, FADE_ANIMATION_RATE); + } + } else if (resetMultipleStrokes) { + mResetGesture = true; + } else { + fireOnGesturePerformed(); + + mFadingHasStarted = false; + mPath.rewind(); + mCurrentGesture = null; + mPreviousWasGesturing = false; + setPaintAlpha(255); + } + + invalidate(); + } + } + + public static interface OnGesturingListener { + void onGesturingStarted(GestureOverlayView overlay); + + void onGesturingEnded(GestureOverlayView overlay); + } + + public static interface OnGestureListener { + void onGestureStarted(GestureOverlayView overlay, MotionEvent event); + + void onGesture(GestureOverlayView overlay, MotionEvent event); + + void onGestureEnded(GestureOverlayView overlay, MotionEvent event); + + void onGestureCancelled(GestureOverlayView overlay, MotionEvent event); + } + + public static interface OnGesturePerformedListener { + void onGesturePerformed(GestureOverlayView overlay, Gesture gesture); + } +} diff --git a/core/java/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java new file mode 100644 index 000000000000..3698011fc7fc --- /dev/null +++ b/core/java/android/gesture/GesturePoint.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A timed point of a gesture stroke + */ + +public class GesturePoint { + public final float x; + public final float y; + + public final long timestamp; + + public GesturePoint(float x, float y, long t) { + this.x = x; + this.y = y; + timestamp = t; + } + + static GesturePoint deserialize(DataInputStream in) throws IOException { + // Read X and Y + final float x = in.readFloat(); + final float y = in.readFloat(); + // Read timestamp + final long timeStamp = in.readLong(); + return new GesturePoint(x, y, timeStamp); + } +} diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java new file mode 100644 index 000000000000..5f1a44503604 --- /dev/null +++ b/core/java/android/gesture/GestureStore.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.util.Log; +import android.os.SystemClock; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; +import java.util.Map; + +import static android.gesture.GestureConstants.LOG_TAG; + +/** + * GestureLibrary maintains gesture examples and makes predictions on a new + * gesture + */ +// +// File format for GestureStore: +// +// Nb. bytes Java type Description +// ----------------------------------- +// Header +// 2 bytes short File format version number +// 4 bytes int Number of entries +// Entry +// X bytes UTF String Entry name +// 4 bytes int Number of gestures +// Gesture +// 8 bytes long Gesture ID +// 4 bytes int Number of strokes +// Stroke +// 4 bytes int Number of points +// Point +// 4 bytes float X coordinate of the point +// 4 bytes float Y coordinate of the point +// 8 bytes long Time stamp +// +public class GestureStore { + public static final int SEQUENCE_INVARIANT = 1; + // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed + public static final int SEQUENCE_SENSITIVE = 2; + + // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures + public static final int ORIENTATION_INVARIANT = 1; + public static final int ORIENTATION_SENSITIVE = 2; + + private static final short FILE_FORMAT_VERSION = 1; + + private static final boolean PROFILE_LOADING_SAVING = false; + + private int mSequenceType = SEQUENCE_SENSITIVE; + private int mOrientationStyle = ORIENTATION_SENSITIVE; + + private final HashMap<String, ArrayList<Gesture>> mNamedGestures = + new HashMap<String, ArrayList<Gesture>>(); + + private Learner mClassifier; + + private boolean mChanged = false; + + public GestureStore() { + mClassifier = new InstanceLearner(); + } + + /** + * Specify how the gesture library will handle orientation. + * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE + * + * @param style + */ + public void setOrientationStyle(int style) { + mOrientationStyle = style; + } + + public int getOrientationStyle() { + return mOrientationStyle; + } + + /** + * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public void setSequenceType(int type) { + mSequenceType = type; + } + + /** + * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public int getSequenceType() { + return mSequenceType; + } + + /** + * Get all the gesture entry names in the library + * + * @return a set of strings + */ + public Set<String> getGestureEntries() { + return mNamedGestures.keySet(); + } + + /** + * Recognize a gesture + * + * @param gesture the query + * @return a list of predictions of possible entries for a given gesture + */ + public ArrayList<Prediction> recognize(Gesture gesture) { + Instance instance = Instance.createInstance(mSequenceType, + mOrientationStyle, gesture, null); + return mClassifier.classify(mSequenceType, instance.vector); + } + + /** + * Add a gesture for the entry + * + * @param entryName entry name + * @param gesture + */ + public void addGesture(String entryName, Gesture gesture) { + if (entryName == null || entryName.length() == 0) { + return; + } + ArrayList<Gesture> gestures = mNamedGestures.get(entryName); + if (gestures == null) { + gestures = new ArrayList<Gesture>(); + mNamedGestures.put(entryName, gestures); + } + gestures.add(gesture); + mClassifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName)); + mChanged = true; + } + + /** + * Remove a gesture from the library. If there are no more gestures for the + * given entry, the gesture entry will be removed. + * + * @param entryName entry name + * @param gesture + */ + public void removeGesture(String entryName, Gesture gesture) { + ArrayList<Gesture> gestures = mNamedGestures.get(entryName); + if (gestures == null) { + return; + } + + gestures.remove(gesture); + + // if there are no more samples, remove the entry automatically + if (gestures.isEmpty()) { + mNamedGestures.remove(entryName); + } + + mClassifier.removeInstance(gesture.getID()); + + mChanged = true; + } + + /** + * Remove a entry of gestures + * + * @param entryName the entry name + */ + public void removeEntry(String entryName) { + mNamedGestures.remove(entryName); + mClassifier.removeInstances(entryName); + mChanged = true; + } + + /** + * Get all the gestures of an entry + * + * @param entryName + * @return the list of gestures that is under this name + */ + public ArrayList<Gesture> getGestures(String entryName) { + ArrayList<Gesture> gestures = mNamedGestures.get(entryName); + if (gestures != null) { + return new ArrayList<Gesture>(gestures); + } else { + return null; + } + } + + public boolean hasChanged() { + return mChanged; + } + + /** + * Save the gesture library + */ + public void save(OutputStream stream) throws IOException { + save(stream, false); + } + + public void save(OutputStream stream, boolean closeStream) throws IOException { + DataOutputStream out = null; + + try { + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + final HashMap<String, ArrayList<Gesture>> maps = mNamedGestures; + + out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream : + new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE)); + // Write version number + out.writeShort(FILE_FORMAT_VERSION); + // Write number of entries + out.writeInt(maps.size()); + + for (Map.Entry<String, ArrayList<Gesture>> entry : maps.entrySet()) { + final String key = entry.getKey(); + final ArrayList<Gesture> examples = entry.getValue(); + final int count = examples.size(); + + // Write entry name + out.writeUTF(key); + // Write number of examples for this entry + out.writeInt(count); + + for (int i = 0; i < count; i++) { + examples.get(i).serialize(out); + } + } + + out.flush(); + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms"); + } + + mChanged = false; + } finally { + if (closeStream) GestureUtilities.closeStream(out); + } + } + + /** + * Load the gesture library + */ + public void load(InputStream stream) throws IOException { + load(stream, false); + } + + public void load(InputStream stream, boolean closeStream) throws IOException { + DataInputStream in = null; + try { + in = new DataInputStream((stream instanceof BufferedInputStream) ? stream : + new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE)); + + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + // Read file format version number + final short versionNumber = in.readShort(); + switch (versionNumber) { + case 1: + readFormatV1(in); + break; + } + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms"); + } + } finally { + if (closeStream) GestureUtilities.closeStream(in); + } + } + + private void readFormatV1(DataInputStream in) throws IOException { + final Learner classifier = mClassifier; + final HashMap<String, ArrayList<Gesture>> namedGestures = mNamedGestures; + namedGestures.clear(); + + // Number of entries in the library + final int entriesCount = in.readInt(); + + for (int i = 0; i < entriesCount; i++) { + // Entry name + final String name = in.readUTF(); + // Number of gestures + final int gestureCount = in.readInt(); + + final ArrayList<Gesture> gestures = new ArrayList<Gesture>(gestureCount); + for (int j = 0; j < gestureCount; j++) { + final Gesture gesture = Gesture.deserialize(in); + gestures.add(gesture); + classifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); + } + + namedGestures.put(name, gestures); + } + } + + Learner getLearner() { + return mClassifier; + } +} diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java new file mode 100644 index 000000000000..598eb8534ffc --- /dev/null +++ b/core/java/android/gesture/GestureStroke.java @@ -0,0 +1,229 @@ +/* + * 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.gesture; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; + +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.util.ArrayList; + +/** + * A gesture stroke started on a touch down and ended on a touch up. + */ +public class GestureStroke { + static final float TOUCH_TOLERANCE = 8; + + public final RectF boundingBox; + + public final float length; + public final float[] points; + + private final long[] timestamps; + private Path mCachedPath; + + /** + * Construct a gesture stroke from a list of gesture points + * + * @param points + */ + public GestureStroke(ArrayList<GesturePoint> points) { + final int count = points.size(); + final float[] tmpPoints = new float[count * 2]; + final long[] times = new long[count]; + + RectF bx = null; + float len = 0; + int index = 0; + + for (int i = 0; i < count; i++) { + final GesturePoint p = points.get(i); + tmpPoints[i * 2] = p.x; + tmpPoints[i * 2 + 1] = p.y; + times[index] = p.timestamp; + + if (bx == null) { + bx = new RectF(); + bx.top = p.y; + bx.left = p.x; + bx.right = p.x; + bx.bottom = p.y; + len = 0; + } else { + len += Math.sqrt(Math.pow(p.x - tmpPoints[(i - 1) * 2], 2) + + Math.pow(p.y - tmpPoints[(i -1 ) * 2 + 1], 2)); + bx.union(p.x, p.y); + } + index++; + } + + timestamps = times; + this.points = tmpPoints; + boundingBox = bx; + length = len; + } + + /** + * Draw the gesture with a given canvas and paint + * + * @param canvas + */ + void draw(Canvas canvas, Paint paint) { + if (mCachedPath == null) { + makePath(); + } + + canvas.drawPath(mCachedPath, paint); + } + + public Path getPath() { + if (mCachedPath == null) { + makePath(); + } + + return mCachedPath; + } + + private void makePath() { + final float[] localPoints = points; + final int count = localPoints.length; + + Path path = null; + + float mX = 0; + float mY = 0; + + for (int i = 0; i < count; i += 2) { + float x = localPoints[i]; + float y = localPoints[i + 1]; + if (path == null) { + path = new Path(); + path.moveTo(x, y); + mX = x; + mY = y; + } else { + float dx = Math.abs(x - mX); + float dy = Math.abs(y - mY); + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); + mX = x; + mY = y; + } + } + } + + mCachedPath = path; + } + + /** + * Convert the stroke to a Path based on the number of points + * + * @param width the width of the bounding box of the target path + * @param height the height of the bounding box of the target path + * @param numSample the number of points needed + * + * @return the path + */ + public Path toPath(float width, float height, int numSample) { + final float[] pts = GestureUtilities.temporalSampling(this, numSample); + final RectF rect = boundingBox; + + GestureUtilities.translate(pts, -rect.left, -rect.top); + + float sx = width / rect.width(); + float sy = height / rect.height(); + float scale = sx > sy ? sy : sx; + GestureUtilities.scale(pts, scale, scale); + + float mX = 0; + float mY = 0; + + Path path = null; + + final int count = pts.length; + + for (int i = 0; i < count; i += 2) { + float x = pts[i]; + float y = pts[i + 1]; + if (path == null) { + path = new Path(); + path.moveTo(x, y); + mX = x; + mY = y; + } else { + float dx = Math.abs(x - mX); + float dy = Math.abs(y - mY); + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); + mX = x; + mY = y; + } + } + } + + return path; + } + + void serialize(DataOutputStream out) throws IOException { + final float[] pts = points; + final long[] times = timestamps; + final int count = points.length; + + // Write number of points + out.writeInt(count / 2); + + for (int i = 0; i < count; i += 2) { + // Write X + out.writeFloat(pts[i]); + // Write Y + out.writeFloat(pts[i + 1]); + // Write timestamp + out.writeLong(times[i / 2]); + } + } + + static GestureStroke deserialize(DataInputStream in) throws IOException { + // Number of points + final int count = in.readInt(); + + final ArrayList<GesturePoint> points = new ArrayList<GesturePoint>(count); + for (int i = 0; i < count; i++) { + points.add(GesturePoint.deserialize(in)); + } + + return new GestureStroke(points); + } + + /** + * Invalidate the cached path that is used to render the stroke + */ + public void clearPath() { + if (mCachedPath != null) mCachedPath.rewind(); + } + + /** + * Compute an oriented bounding box of the stroke + * @return OrientedBoundingBox + */ + public OrientedBoundingBox computeOrientedBoundingBox() { + return GestureUtilities.computeOrientedBoundingBox(points); + } +} diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java new file mode 100755 index 000000000000..40d70295fc9b --- /dev/null +++ b/core/java/android/gesture/GestureUtilities.java @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.RectF; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.io.Closeable; +import java.io.IOException; + +import static android.gesture.GestureConstants.*; + +final class GestureUtilities { + private static final int TEMPORAL_SAMPLING_RATE = 16; + + private GestureUtilities() { + } + + /** + * Closes the specified stream. + * + * @param stream The stream to close. + */ + static void closeStream(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not close stream", e); + } + } + } + + static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) { + final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive + float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension]; + Arrays.fill(sample, 0); + + RectF rect = gesture.getBoundingBox(); + float sx = targetPatchSize / rect.width(); + float sy = targetPatchSize / rect.height(); + float scale = sx < sy ? sx : sy; + + float preDx = -rect.centerX(); + float preDy = -rect.centerY(); + float postDx = targetPatchSize / 2; + float postDy = targetPatchSize / 2; + + final ArrayList<GestureStroke> strokes = gesture.getStrokes(); + final int count = strokes.size(); + + int size; + float xpos; + float ypos; + + for (int index = 0; index < count; index++) { + final GestureStroke stroke = strokes.get(index); + float[] strokepoints = stroke.points; + size = strokepoints.length; + + final float[] pts = new float[size]; + + for (int i = 0; i < size; i += 2) { + pts[i] = (strokepoints[i] + preDx) * scale + postDx; + pts[i + 1] = (strokepoints[i + 1] + preDy) * scale + postDy; + } + + float segmentEndX = -1; + float segmentEndY = -1; + + for (int i = 0; i < size; i += 2) { + + float segmentStartX = pts[i] < 0 ? 0 : pts[i]; + float segmentStartY = pts[i + 1] < 0 ? 0 : pts[i + 1]; + + if (segmentStartX > targetPatchSize) { + segmentStartX = targetPatchSize; + } + + if (segmentStartY > targetPatchSize) { + segmentStartY = targetPatchSize; + } + + plot(segmentStartX, segmentStartY, sample, sampleMatrixDimension); + + if (segmentEndX != -1) { + // evaluate horizontally + if (segmentEndX > segmentStartX) { + xpos = (float) Math.ceil(segmentStartX); + float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX); + while (xpos < segmentEndX) { + ypos = slope * (xpos - segmentStartX) + segmentStartY; + plot(xpos, ypos, sample, sampleMatrixDimension); + xpos++; + } + } else if (segmentEndX < segmentStartX){ + xpos = (float) Math.ceil(segmentEndX); + float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX); + while (xpos < segmentStartX) { + ypos = slope * (xpos - segmentStartX) + segmentStartY; + plot(xpos, ypos, sample, sampleMatrixDimension); + xpos++; + } + } + + // evaluating vertically + if (segmentEndY > segmentStartY) { + ypos = (float) Math.ceil(segmentStartY); + float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY); + while (ypos < segmentEndY) { + xpos = invertSlope * (ypos - segmentStartY) + segmentStartX; + plot(xpos, ypos, sample, sampleMatrixDimension); + ypos++; + } + } else if (segmentEndY < segmentStartY) { + ypos = (float) Math.ceil(segmentEndY); + float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY); + while (ypos < segmentStartY) { + xpos = invertSlope * (ypos - segmentStartY) + segmentStartX; + plot(xpos, ypos, sample, sampleMatrixDimension); + ypos++; + } + } + } + + segmentEndX = segmentStartX; + segmentEndY = segmentStartY; + } + } + + + return sample; + } + + private static void plot(float x, float y, float[] sample, int sampleSize) { + x = x < 0 ? 0 : x; + y = y < 0 ? 0 : y; + int xFloor = (int) Math.floor(x); + int xCeiling = (int) Math.ceil(x); + int yFloor = (int) Math.floor(y); + int yCeiling = (int) Math.ceil(y); + + // if it's an integer + if (x == xFloor && y == yFloor) { + int index = yCeiling * sampleSize + xCeiling; + if (sample[index] < 1){ + sample[index] = 1; + } + } else { + double topLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yFloor - y, 2)); + double topRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yFloor - y, 2)); + double btmLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yCeiling - y, 2)); + double btmRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yCeiling - y, 2)); + double sum = topLeft + topRight + btmLeft + btmRight; + + double value = topLeft / sum; + int index = yFloor * sampleSize + xFloor; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = topRight / sum; + index = yFloor * sampleSize + xCeiling; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = btmLeft / sum; + index = yCeiling * sampleSize + xFloor; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = btmRight / sum; + index = yCeiling * sampleSize + xCeiling; + if (value > sample[index]){ + sample[index] = (float) value; + } + } + } + + /** + * Featurize a stroke into a vector of a given number of elements + * + * @param stroke + * @param sampleSize + * @return a float array + */ + static float[] temporalSampling(GestureStroke stroke, int sampleSize) { + final float increment = stroke.length / (sampleSize - 1); + int vectorLength = sampleSize * 2; + float[] vector = new float[vectorLength]; + float distanceSoFar = 0; + float[] pts = stroke.points; + float lstPointX = pts[0]; + float lstPointY = pts[1]; + int index = 0; + float currentPointX = Float.MIN_VALUE; + float currentPointY = Float.MIN_VALUE; + vector[index] = lstPointX; + index++; + vector[index] = lstPointY; + index++; + int i = 0; + int count = pts.length / 2; + while (i < count) { + if (currentPointX == Float.MIN_VALUE) { + i++; + if (i >= count) { + break; + } + currentPointX = pts[i * 2]; + currentPointY = pts[i * 2 + 1]; + } + float deltaX = currentPointX - lstPointX; + float deltaY = currentPointY - lstPointY; + float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (distanceSoFar + distance >= increment) { + float ratio = (increment - distanceSoFar) / distance; + float nx = lstPointX + ratio * deltaX; + float ny = lstPointY + ratio * deltaY; + vector[index] = nx; + index++; + vector[index] = ny; + index++; + lstPointX = nx; + lstPointY = ny; + distanceSoFar = 0; + } else { + lstPointX = currentPointX; + lstPointY = currentPointY; + currentPointX = Float.MIN_VALUE; + currentPointY = Float.MIN_VALUE; + distanceSoFar += distance; + } + } + + for (i = index; i < vectorLength; i += 2) { + vector[i] = lstPointX; + vector[i + 1] = lstPointY; + } + return vector; + } + + /** + * Calculate the centroid + * + * @param points + * @return the centroid + */ + static float[] computeCentroid(float[] points) { + float centerX = 0; + float centerY = 0; + int count = points.length; + for (int i = 0; i < count; i++) { + centerX += points[i]; + i++; + centerY += points[i]; + } + float[] center = new float[2]; + center[0] = 2 * centerX / count; + center[1] = 2 * centerY / count; + + return center; + } + + /** + * calculate the variance-covariance matrix, treat each point as a sample + * + * @param points + * @return the covariance matrix + */ + private static double[][] computeCoVariance(float[] points) { + double[][] array = new double[2][2]; + array[0][0] = 0; + array[0][1] = 0; + array[1][0] = 0; + array[1][1] = 0; + int count = points.length; + for (int i = 0; i < count; i++) { + float x = points[i]; + i++; + float y = points[i]; + array[0][0] += x * x; + array[0][1] += x * y; + array[1][0] = array[0][1]; + array[1][1] += y * y; + } + array[0][0] /= (count / 2); + array[0][1] /= (count / 2); + array[1][0] /= (count / 2); + array[1][1] /= (count / 2); + + return array; + } + + static float computeTotalLength(float[] points) { + float sum = 0; + int count = points.length - 4; + for (int i = 0; i < count; i += 2) { + float dx = points[i + 2] - points[i]; + float dy = points[i + 3] - points[i + 1]; + sum += Math.sqrt(dx * dx + dy * dy); + } + return sum; + } + + static double computeStraightness(float[] points) { + float totalLen = computeTotalLength(points); + float dx = points[2] - points[0]; + float dy = points[3] - points[1]; + return Math.sqrt(dx * dx + dy * dy) / totalLen; + } + + static double computeStraightness(float[] points, float totalLen) { + float dx = points[2] - points[0]; + float dy = points[3] - points[1]; + return Math.sqrt(dx * dx + dy * dy) / totalLen; + } + + /** + * Calculate the squared Euclidean distance between two vectors + * + * @param vector1 + * @param vector2 + * @return the distance + */ + static double squaredEuclideanDistance(float[] vector1, float[] vector2) { + double squaredDistance = 0; + int size = vector1.length; + for (int i = 0; i < size; i++) { + float difference = vector1[i] - vector2[i]; + squaredDistance += difference * difference; + } + return squaredDistance / size; + } + + /** + * Calculate the cosine distance between two instances + * + * @param vector1 + * @param vector2 + * @return the distance between 0 and Math.PI + */ + static double cosineDistance(float[] vector1, float[] vector2) { + float sum = 0; + int len = vector1.length; + for (int i = 0; i < len; i++) { + sum += vector1[i] * vector2[i]; + } + return Math.acos(sum); + } + + static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> pts) { + GestureStroke stroke = new GestureStroke(pts); + float[] points = temporalSampling(stroke, TEMPORAL_SAMPLING_RATE); + return computeOrientedBoundingBox(points); + } + + static OrientedBoundingBox computeOrientedBoundingBox(float[] points) { + float[] meanVector = computeCentroid(points); + return computeOrientedBoundingBox(points, meanVector); + } + + static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) { + translate(points, -centroid[0], -centroid[1]); + + double[][] array = computeCoVariance(points); + double[] targetVector = computeOrientation(array); + + float angle; + if (targetVector[0] == 0 && targetVector[1] == 0) { + angle = (float) -Math.PI/2; + } else { // -PI<alpha<PI + angle = (float) Math.atan2(targetVector[1], targetVector[0]); + rotate(points, -angle); + } + + float minx = Float.MAX_VALUE; + float miny = Float.MAX_VALUE; + float maxx = Float.MIN_VALUE; + float maxy = Float.MIN_VALUE; + int count = points.length; + for (int i = 0; i < count; i++) { + if (points[i] < minx) { + minx = points[i]; + } + if (points[i] > maxx) { + maxx = points[i]; + } + i++; + if (points[i] < miny) { + miny = points[i]; + } + if (points[i] > maxy) { + maxy = points[i]; + } + } + + return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny); + } + + private static double[] computeOrientation(double[][] covarianceMatrix) { + double[] targetVector = new double[2]; + if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) { + targetVector[0] = 1; + targetVector[1] = 0; + } + + double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1]; + double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1] + * covarianceMatrix[1][0]; + double value = a / 2; + double rightside = Math.sqrt(Math.pow(value, 2) - b); + double lambda1 = -value + rightside; + double lambda2 = -value - rightside; + if (lambda1 == lambda2) { + targetVector[0] = 0; + targetVector[1] = 0; + } else { + double lambda = lambda1 > lambda2 ? lambda1 : lambda2; + targetVector[0] = 1; + targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1]; + } + return targetVector; + } + + + static float[] rotate(float[] points, double angle) { + double cos = Math.cos(angle); + double sin = Math.sin(angle); + int size = points.length; + for (int i = 0; i < size; i += 2) { + float x = (float) (points[i] * cos - points[i + 1] * sin); + float y = (float) (points[i] * sin + points[i + 1] * cos); + points[i] = x; + points[i + 1] = y; + } + return points; + } + + static float[] translate(float[] points, float dx, float dy) { + int size = points.length; + for (int i = 0; i < size; i += 2) { + points[i] += dx; + points[i + 1] += dy; + } + return points; + } + + static float[] scale(float[] points, float sx, float sy) { + int size = points.length; + for (int i = 0; i < size; i += 2) { + points[i] *= sx; + points[i + 1] *= sy; + } + return points; + } +} diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java new file mode 100755 index 000000000000..ef208acf4ec7 --- /dev/null +++ b/core/java/android/gesture/Instance.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008-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.gesture; + + +/** + * An instance represents a sample if the label is available or a query if the + * label is null. + */ +class Instance { + private static final int SEQUENCE_SAMPLE_SIZE = 16; + + private static final int PATCH_SAMPLE_SIZE = 16; + + private final static float[] ORIENTATIONS = { + 0, (float) (Math.PI / 4), (float) (Math.PI / 2), (float) (Math.PI * 3 / 4), + (float) Math.PI, -0, (float) (-Math.PI / 4), (float) (-Math.PI / 2), + (float) (-Math.PI * 3 / 4), (float) -Math.PI + }; + + // the feature vector + final float[] vector; + + // the label can be null + final String label; + + // the id of the instance + final long id; + + private Instance(long id, float[] sample, String sampleName) { + this.id = id; + vector = sample; + label = sampleName; + } + + private void normalize() { + float[] sample = vector; + float sum = 0; + + int size = sample.length; + for (int i = 0; i < size; i++) { + sum += sample[i] * sample[i]; + } + + float magnitude = (float)Math.sqrt(sum); + for (int i = 0; i < size; i++) { + sample[i] /= magnitude; + } + } + + /** + * create a learning instance for a single stroke gesture + * + * @param gesture + * @param label + * @return the instance + */ + static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) { + float[] pts; + Instance instance; + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { + pts = temporalSampler(orientationType, gesture); + instance = new Instance(gesture.getID(), pts, label); + instance.normalize(); + } else { + pts = spatialSampler(gesture); + instance = new Instance(gesture.getID(), pts, label); + } + return instance; + } + + private static float[] spatialSampler(Gesture gesture) { + return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE); + } + + private static float[] temporalSampler(int orientationType, Gesture gesture) { + float[] pts = GestureUtilities.temporalSampling(gesture.getStrokes().get(0), + SEQUENCE_SAMPLE_SIZE); + float[] center = GestureUtilities.computeCentroid(pts); + float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]); + + float adjustment = -orientation; + if (orientationType == GestureStore.ORIENTATION_SENSITIVE) { + int count = ORIENTATIONS.length; + for (int i = 0; i < count; i++) { + float delta = ORIENTATIONS[i] - orientation; + if (Math.abs(delta) < Math.abs(adjustment)) { + adjustment = delta; + } + } + } + + GestureUtilities.translate(pts, -center[0], -center[1]); + GestureUtilities.rotate(pts, adjustment); + + return pts; + } + +} diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java new file mode 100644 index 000000000000..b93b76fa9a87 --- /dev/null +++ b/core/java/android/gesture/InstanceLearner.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.TreeMap; + +/** + * An implementation of an instance-based learner + */ + +class InstanceLearner extends Learner { + private static final Comparator<Prediction> sComparator = new Comparator<Prediction>() { + public int compare(Prediction object1, Prediction object2) { + double score1 = object1.score; + double score2 = object2.score; + if (score1 > score2) { + return -1; + } else if (score1 < score2) { + return 1; + } else { + return 0; + } + } + }; + + @Override + ArrayList<Prediction> classify(int sequenceType, float[] vector) { + ArrayList<Prediction> predictions = new ArrayList<Prediction>(); + ArrayList<Instance> instances = getInstances(); + int count = instances.size(); + TreeMap<String, Double> label2score = new TreeMap<String, Double>(); + for (int i = 0; i < count; i++) { + Instance sample = instances.get(i); + if (sample.vector.length != vector.length) { + continue; + } + double distance; + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { + distance = GestureUtilities.cosineDistance(sample.vector, vector); + } else { + distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector); + } + double weight; + if (distance == 0) { + weight = Double.MAX_VALUE; + } else { + weight = 1 / distance; + } + Double score = label2score.get(sample.label); + if (score == null || weight > score) { + label2score.put(sample.label, weight); + } + } + +// double sum = 0; + for (String name : label2score.keySet()) { + double score = label2score.get(name); +// sum += score; + predictions.add(new Prediction(name, score)); + } + + // normalize +// for (Prediction prediction : predictions) { +// prediction.score /= sum; +// } + + Collections.sort(predictions, sComparator); + + return predictions; + } +} diff --git a/core/java/android/gesture/Learner.java b/core/java/android/gesture/Learner.java new file mode 100755 index 000000000000..feacde5f9736 --- /dev/null +++ b/core/java/android/gesture/Learner.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.util.ArrayList; + +/** + * The abstract class of a gesture learner + */ +abstract class Learner { + private final ArrayList<Instance> mInstances = new ArrayList<Instance>(); + + /** + * Add an instance to the learner + * + * @param instance + */ + void addInstance(Instance instance) { + mInstances.add(instance); + } + + /** + * Retrieve all the instances + * + * @return instances + */ + ArrayList<Instance> getInstances() { + return mInstances; + } + + /** + * Remove an instance based on its id + * + * @param id + */ + void removeInstance(long id) { + ArrayList<Instance> instances = mInstances; + int count = instances.size(); + for (int i = 0; i < count; i++) { + Instance instance = instances.get(i); + if (id == instance.id) { + instances.remove(instance); + return; + } + } + } + + /** + * Remove all the instances of a category + * + * @param name the category name + */ + void removeInstances(String name) { + final ArrayList<Instance> toDelete = new ArrayList<Instance>(); + final ArrayList<Instance> instances = mInstances; + final int count = instances.size(); + + for (int i = 0; i < count; i++) { + final Instance instance = instances.get(i); + // the label can be null, as specified in Instance + if ((instance.label == null && name == null) || instance.label.equals(name)) { + toDelete.add(instance); + } + } + instances.removeAll(toDelete); + } + + abstract ArrayList<Prediction> classify(int gestureType, float[] vector); +} diff --git a/core/java/android/gesture/OrientedBoundingBox.java b/core/java/android/gesture/OrientedBoundingBox.java new file mode 100644 index 000000000000..f1335ee12232 --- /dev/null +++ b/core/java/android/gesture/OrientedBoundingBox.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.Matrix; +import android.graphics.Path; + +/** + * An oriented bounding box + */ +public class OrientedBoundingBox { + public final float squareness; + + public final float width; + public final float height; + + public final float orientation; + + public final float centerX; + public final float centerY; + + OrientedBoundingBox(float angle, float cx, float cy, float w, float h) { + orientation = angle; + width = w; + height = h; + centerX = cx; + centerY = cy; + float ratio = w / h; + if (ratio > 1) { + squareness = 1 / ratio; + } else { + squareness = ratio; + } + } + + /** + * Currently used for debugging purpose only. + * + * @hide + */ + public Path toPath() { + Path path = new Path(); + float[] point = new float[2]; + point[0] = -width / 2; + point[1] = height / 2; + Matrix matrix = new Matrix(); + matrix.setRotate(orientation); + matrix.postTranslate(centerX, centerY); + matrix.mapPoints(point); + path.moveTo(point[0], point[1]); + + point[0] = -width / 2; + point[1] = -height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + point[0] = width / 2; + point[1] = -height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + point[0] = width / 2; + point[1] = height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + path.close(); + + return path; + } +} diff --git a/core/java/android/gesture/Prediction.java b/core/java/android/gesture/Prediction.java new file mode 100755 index 000000000000..ce6ad5745df6 --- /dev/null +++ b/core/java/android/gesture/Prediction.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008-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.gesture; + +public class Prediction { + public final String name; + + public double score; + + Prediction(String label, double predictionScore) { + name = label; + score = predictionScore; + } + + @Override + public String toString() { + return name; + } +} diff --git a/core/java/android/gesture/package.html b/core/java/android/gesture/package.html new file mode 100644 index 000000000000..a54a01713c91 --- /dev/null +++ b/core/java/android/gesture/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +@hide +</BODY> +</HTML> diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index ca579b6b501b..091bc1700988 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; @@ -152,7 +155,11 @@ public class Camera { * @throws IOException if the method fails. */ public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { - setPreviewDisplay(holder.getSurface()); + if (holder != null) { + setPreviewDisplay(holder.getSurface()); + } else { + setPreviewDisplay((Surface)null); + } } private native final void setPreviewDisplay(Surface surface); @@ -231,22 +238,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 +263,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); @@ -363,7 +371,7 @@ public class Camera { } private native final void native_takePicture(); - // These match the enum in libs/android_runtime/android_hardware_Camera.cpp + // These match the enum in include/ui/Camera.h /** Unspecified camerar error. @see #ErrorCallback */ public static final int CAMERA_ERROR_UNKNOWN = 1; /** Media server died. In this case, the application must release the diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl index 04af2aec4523..67180bd90064 100644 --- a/core/java/android/hardware/ISensorService.aidl +++ b/core/java/android/hardware/ISensorService.aidl @@ -17,13 +17,13 @@ package android.hardware; -import android.os.ParcelFileDescriptor; +import android.os.Bundle; /** * {@hide} */ interface ISensorService { - ParcelFileDescriptor getDataChanel(); + Bundle getDataChannel(); boolean enableSensor(IBinder listener, String name, int sensor, int enable); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 67df23b270a4..bf945ec7bae3 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -18,7 +18,9 @@ package android.hardware; import android.content.Context; import android.os.Binder; +import android.os.Bundle; import android.os.Looper; +import android.os.Parcelable; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; @@ -280,8 +282,8 @@ public class SensorManager void startLocked(ISensorService service) { try { if (mThread == null) { - ParcelFileDescriptor fd = service.getDataChanel(); - mThread = new Thread(new SensorThreadRunnable(fd), + Bundle dataChannel = service.getDataChannel(); + mThread = new Thread(new SensorThreadRunnable(dataChannel), SensorThread.class.getName()); mThread.start(); } @@ -291,10 +293,52 @@ public class SensorManager } private class SensorThreadRunnable implements Runnable { - private ParcelFileDescriptor mSensorDataFd; - SensorThreadRunnable(ParcelFileDescriptor fd) { - mSensorDataFd = fd; + private Bundle mDataChannel; + SensorThreadRunnable(Bundle dataChannel) { + mDataChannel = dataChannel; } + + private boolean open() { + if (mDataChannel == null) { + Log.e(TAG, "mDataChannel == NULL, exiting"); + synchronized (sListeners) { + mThread = null; + } + return false; + } + + // this thread is guaranteed to be unique + Parcelable[] pfds = mDataChannel.getParcelableArray("fds"); + FileDescriptor[] fds; + if (pfds != null) { + int length = pfds.length; + fds = new FileDescriptor[length]; + for (int i = 0; i < length; i++) { + ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i]; + fds[i] = pfd.getFileDescriptor(); + } + } else { + fds = null; + } + int[] ints = mDataChannel.getIntArray("ints"); + sensors_data_open(fds, ints); + if (pfds != null) { + try { + // close our copies of the file descriptors, + // since we are just passing these to the JNI code and not using them here. + for (int i = pfds.length - 1; i >= 0; i--) { + ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i]; + pfd.close(); + } + } catch (IOException e) { + // *shrug* + Log.e(TAG, "IOException: ", e); + } + } + mDataChannel = null; + return true; + } + public void run() { //Log.d(TAG, "entering main sensor thread"); final float[] values = new float[3]; @@ -302,23 +346,9 @@ public class SensorManager final long timestamp[] = new long[1]; Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY); - if (mSensorDataFd == null) { - Log.e(TAG, "mSensorDataFd == NULL, exiting"); - synchronized (sListeners) { - mThread = null; - } + if (!open()) { return; } - // this thread is guaranteed to be unique - sensors_data_open(mSensorDataFd.getFileDescriptor()); - try { - mSensorDataFd.close(); - } catch (IOException e) { - // *shrug* - Log.e(TAG, "IOException: ", e); - } - mSensorDataFd = null; - while (true) { // wait for an event @@ -1469,7 +1499,7 @@ public class SensorManager // Used within this module from outside SensorManager, don't make private static native int sensors_data_init(); static native int sensors_data_uninit(); - static native int sensors_data_open(FileDescriptor fd); + static native int sensors_data_open(FileDescriptor[] fds, int[] ints); static native int sensors_data_close(); static native int sensors_data_poll(float[] values, int[] status, long[] timestamp); } diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index c4ee5b0da0e2..6a97951fecbd 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -159,11 +159,11 @@ public class RequestHandle { e.printStackTrace(); } - // update the "cookie" header based on the redirected url - mHeaders.remove("cookie"); + // update the "Cookie" header based on the redirected url + mHeaders.remove("Cookie"); String cookie = CookieManager.getInstance().getCookie(mUri); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 6c135825a99f..abfb27412de0 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/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8a0fd58530b0..528def5c4011 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -69,6 +69,20 @@ public abstract class BatteryStats implements Parcelable { public static final int WIFI_MULTICAST_ENABLED = 7; /** + * A constant indicating an audio turn on timer + * + * {@hide} + */ + public static final int AUDIO_TURNED_ON = 7; + + /** + * A constant indicating a video turn on timer + * + * {@hide} + */ + public static final int VIDEO_TURNED_ON = 8; + + /** * Include all of the data in the stats, including previously saved data. */ public static final int STATS_TOTAL = 0; @@ -164,7 +178,7 @@ public abstract class BatteryStats implements Parcelable { * @return a time in microseconds */ public abstract long getTotalTimeLocked(long batteryRealtime, int which); - + /** * Temporary for debugging. */ @@ -234,11 +248,17 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteScanWifiLockReleasedLocked(); public abstract void noteWifiMulticastEnabledLocked(); public abstract void noteWifiMulticastDisabledLocked(); + public abstract void noteAudioTurnedOnLocked(); + public abstract void noteAudioTurnedOffLocked(); + public abstract void noteVideoTurnedOnLocked(); + public abstract void noteVideoTurnedOffLocked(); public abstract long getWifiTurnedOnTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); public abstract long getScanWifiLockTime(long batteryRealtime, int which); public abstract long getWifiMulticastTime(long batteryRealtime, int which); + public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); + public abstract long getVideoTurnedOnTime(long batteryRealtime, int which); /** * Note that these must match the constants in android.os.LocalPowerManager. @@ -287,6 +307,13 @@ public abstract class BatteryStats implements Parcelable { * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. */ public abstract int getStarts(int which); + + /** + * Returns the cpu time spent in microseconds while the process was in the foreground. + * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED + * @return foreground cpu time in microseconds + */ + public abstract long getForegroundTime(int which); } /** @@ -344,7 +371,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getStartCount(); /** - * Returns the time in milliseconds that the screen has been on while the device was + * Returns the time in microseconds that the screen has been on while the device was * running on battery. * * {@hide} @@ -364,7 +391,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5; /** - * Returns the time in milliseconds that the screen has been on with + * Returns the time in microseconds that the screen has been on with * the given brightness * * {@hide} @@ -375,7 +402,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getInputEventCount(int which); /** - * Returns the time in milliseconds that the phone has been on while the device was + * Returns the time in microseconds that the phone has been on while the device was * running on battery. * * {@hide} @@ -395,7 +422,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_SIGNAL_STRENGTH_BINS = 5; /** - * Returns the time in milliseconds that the phone has been running with + * Returns the time in microseconds that the phone has been running with * the given signal strength. * * {@hide} @@ -423,7 +450,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_DATA_CONNECTION_TYPES = 5; /** - * Returns the time in milliseconds that the phone has been running with + * Returns the time in microseconds that the phone has been running with * the given data connection. * * {@hide} @@ -440,7 +467,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getPhoneDataConnectionCount(int dataType, int which); /** - * Returns the time in milliseconds that wifi has been on while the device was + * Returns the time in microseconds that wifi has been on while the device was * running on battery. * * {@hide} @@ -448,7 +475,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiOnTime(long batteryRealtime, int which); /** - * Returns the time in milliseconds that wifi has been on and the driver has + * Returns the time in microseconds that wifi has been on and the driver has * been in the running state while the device was running on battery. * * {@hide} @@ -456,7 +483,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiRunningTime(long batteryRealtime, int which); /** - * Returns the time in milliseconds that bluetooth has been on while the device was + * Returns the time in microseconds that bluetooth has been on while the device was * running on battery. * * {@hide} diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 5487c545c12a..830b0bd7ceb1 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -38,6 +38,12 @@ public class Build { /** The name of the underlying board, like "goldfish". */ public static final String BOARD = getString("ro.product.board"); + /** The name of the instruction set (CPU type + ABI convention) of native code. */ + public static final String CPU_ABI = getString("ro.product.cpu.abi"); + + /** The manufacturer of the product/hardware. */ + public static final String MANUFACTURER = getString("ro.product.manufacturer"); + /** The brand (e.g., carrier) the software is customized for, if any. */ public static final String BRAND = getString("ro.product.brand"); @@ -87,6 +93,12 @@ public class Build { */ public static class VERSION_CODES { /** + * Magic version number for a current development build, which has + * not yet turned into an official release. + */ + public static final int CUR_DEVELOPMENT = 10000; + + /** * October 2008: The original, first, version of Android. Yay! */ public static final int BASE = 1; @@ -98,6 +110,19 @@ public class Build { * May 2009: Android 1.5. */ public static final int CUPCAKE = 3; + /** + * Current work on "Donut" development branch. + * + * <p>Applications targeting this or a later release will get these + * new changes in behavior:</p> + * <ul> + * <li> They must explicitly request the + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission to be + * able to modify the contents of the SD card. (Apps targeting + * earlier versions will always request the permission.) + * </ul> + */ + public static final int DONUT = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index b669fa2d695f..a91655f8e8e9 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -78,6 +78,10 @@ public final class Bundle implements Parcelable, Cloneable { readFromParcel(parcelledData); } + /* package */ Bundle(Parcel parcelledData, int length) { + readFromParcelInner(parcelledData, length); + } + /** * Constructs a new, empty Bundle that uses a specific ClassLoader for * instantiating Parcelable and Serializable objects. @@ -155,13 +159,14 @@ public final class Bundle implements Parcelable, Cloneable { return; } - mParcelledData.setDataPosition(0); - Bundle b = mParcelledData.readBundleUnpacked(mClassLoader); - mMap = b.mMap; - - mHasFds = mParcelledData.hasFileDescriptors(); - mFdsKnown = true; - + int N = mParcelledData.readInt(); + if (N < 0) { + return; + } + if (mMap == null) { + mMap = new HashMap<String, Object>(); + } + mParcelledData.readMapInternal(mMap, N, mClassLoader); mParcelledData.recycle(); mParcelledData = null; } @@ -1427,7 +1432,25 @@ public final class Bundle implements Parcelable, Cloneable { * @param parcel The parcel to copy this bundle to. */ public void writeToParcel(Parcel parcel, int flags) { - parcel.writeBundle(this); + if (mParcelledData != null) { + int length = mParcelledData.dataSize(); + parcel.writeInt(length); + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + parcel.appendFrom(mParcelledData, 0, length); + } else { + parcel.writeInt(-1); // dummy, will hold length + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + + int oldPos = parcel.dataPosition(); + parcel.writeMapInternal(mMap); + int newPos = parcel.dataPosition(); + + // Backpatch length + parcel.setDataPosition(oldPos - 8); + int length = newPos - oldPos; + parcel.writeInt(length); + parcel.setDataPosition(newPos); + } } /** @@ -1436,8 +1459,33 @@ public final class Bundle implements Parcelable, Cloneable { * @param parcel The parcel to overwrite this bundle from. */ public void readFromParcel(Parcel parcel) { - mParcelledData = parcel; - mHasFds = mParcelledData.hasFileDescriptors(); + int length = parcel.readInt(); + if (length < 0) { + throw new RuntimeException("Bad length in parcel: " + length); + } + readFromParcelInner(parcel, length); + } + + void readFromParcelInner(Parcel parcel, int length) { + int magic = parcel.readInt(); + if (magic != 0x4C444E42) { + //noinspection ThrowableInstanceNeverThrown + String st = Log.getStackTraceString(new RuntimeException()); + Log.e("Bundle", "readBundle: bad magic number"); + Log.e("Bundle", "readBundle: trace = " + st); + } + + // Advance within this Parcel + int offset = parcel.dataPosition(); + parcel.setDataPosition(offset + length); + + Parcel p = Parcel.obtain(); + p.setDataPosition(0); + p.appendFrom(parcel, offset, length); + p.setDataPosition(0); + + mParcelledData = p; + mHasFds = p.hasFileDescriptors(); mFdsKnown = true; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 8fcb4d7a40a3..d40ea6b300c5 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/MemoryFile.java b/core/java/android/os/MemoryFile.java index 76e4f47f8d2b..c14925cd3839 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -18,6 +18,7 @@ package android.os; import android.util.Log; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -35,48 +36,120 @@ import java.io.OutputStream; public class MemoryFile { private static String TAG = "MemoryFile"; - - // returns fd - private native int native_open(String name, int length); + + // mmap(2) protection flags from <sys/mman.h> + private static final int PROT_READ = 0x1; + private static final int PROT_WRITE = 0x2; + + private static native FileDescriptor native_open(String name, int length) throws IOException; // returns memory address for ashmem region - private native int native_mmap(int fd, int length); - private native void native_close(int fd); - private native int native_read(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned); - private native void native_write(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned); - private native void native_pin(int fd, boolean pin); - - private int mFD; // ashmem file descriptor + private static native int native_mmap(FileDescriptor fd, int length, int mode) + throws IOException; + private static native void native_munmap(int addr, int length) throws IOException; + private static native void native_close(FileDescriptor fd); + private static native int native_read(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_write(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; + private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException; + + private FileDescriptor mFD; // ashmem file descriptor private int mAddress; // address of ashmem memory private int mLength; // total length of our ashmem region private boolean mAllowPurging = false; // true if our ashmem region is unpinned + private final boolean mOwnsRegion; // false if this is a ref to an existing ashmem region /** - * MemoryFile constructor. + * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). * @param length of the memory file in bytes. + * @throws IOException if the memory file could not be created. */ - public MemoryFile(String name, int length) { + public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); - mAddress = native_mmap(mFD, length); + mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); + mOwnsRegion = true; } /** - * Closes and releases all resources for the memory file. + * Creates a reference to an existing memory file. Changes to the original file + * will be available through this reference. + * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. + * + * @param fd File descriptor for an existing memory file, as returned by + * {@link #getFileDescriptor()}. This file descriptor will be closed + * by {@link #close()}. + * @param length Length of the memory file in bytes. + * @param mode File mode. Currently only "r" for read-only access is supported. + * @throws NullPointerException if <code>fd</code> is null. + * @throws IOException If <code>fd</code> does not refer to an existing memory file, + * or if the file mode of the existing memory file is more restrictive + * than <code>mode</code>. + * + * @hide + */ + public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException { + if (fd == null) { + throw new NullPointerException("File descriptor is null."); + } + if (!isMemoryFile(fd)) { + throw new IllegalArgumentException("Not a memory file."); + } + mLength = length; + mFD = fd; + mAddress = native_mmap(mFD, length, modeToProt(mode)); + mOwnsRegion = false; + } + + /** + * Closes the memory file. If there are no other open references to the memory + * file, it will be deleted. */ public void close() { - if (mFD > 0) { + deactivate(); + if (!isClosed()) { native_close(mFD); - mFD = 0; } } + /** + * Unmaps the memory file from the process's memory space, but does not close it. + * After this method has been called, read and write operations through this object + * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor. + * + * @hide + */ + public void deactivate() { + if (!isDeactivated()) { + try { + native_munmap(mAddress, mLength); + mAddress = 0; + } catch (IOException ex) { + Log.e(TAG, ex.toString()); + } + } + } + + /** + * Checks whether the memory file has been deactivated. + */ + private boolean isDeactivated() { + return mAddress == 0; + } + + /** + * Checks whether the memory file has been closed. + */ + private boolean isClosed() { + return !mFD.valid(); + } + @Override protected void finalize() { - if (mFD > 0) { + if (!isClosed()) { Log.e(TAG, "MemoryFile.finalize() called while ashmem still open"); close(); } @@ -108,6 +181,9 @@ public class MemoryFile * @return previous value of allowPurging */ synchronized public boolean allowPurging(boolean allowPurging) throws IOException { + if (!mOwnsRegion) { + throw new IOException("Only the owner can make ashmem regions purgable."); + } boolean oldValue = mAllowPurging; if (oldValue != allowPurging) { native_pin(mFD, !allowPurging); @@ -131,7 +207,6 @@ public class MemoryFile @return OutputStream */ public OutputStream getOutputStream() { - return new MemoryOutputStream(); } @@ -144,9 +219,13 @@ public class MemoryFile * @param destOffset offset into the byte array buffer to read into. * @param count number of bytes to read. * @return number of bytes read. + * @throws IOException if the memory file has been purged or deactivated. */ public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't read from deactivated memory file."); + } if (destOffset < 0 || destOffset > buffer.length || count < 0 || count > buffer.length - destOffset || srcOffset < 0 || srcOffset > mLength @@ -164,9 +243,13 @@ public class MemoryFile * @param srcOffset offset into the byte array buffer to write from. * @param destOffset offset into the memory file to write to. * @param count number of bytes to write. + * @throws IOException if the memory file has been purged or deactivated. */ public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't write to deactivated memory file."); + } if (srcOffset < 0 || srcOffset > buffer.length || count < 0 || count > buffer.length - srcOffset || destOffset < 0 || destOffset > mLength @@ -176,6 +259,64 @@ public class MemoryFile native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging); } + /** + * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()} + * for caveats. This must be here to allow classes outside <code>android.os</code< to + * make ParcelFileDescriptors from MemoryFiles, as + * {@link ParcelFileDescriptor#ParcelFileDescriptor(FileDescriptor)} is package private. + * + * + * @return The file descriptor owned by this memory file object. + * The file descriptor is not duplicated. + * @throws IOException If the memory file has been closed. + * + * @hide + */ + public ParcelFileDescriptor getParcelFileDescriptor() throws IOException { + return new ParcelFileDescriptor(getFileDescriptor()); + } + + /** + * Gets a FileDescriptor for the memory file. Note that this file descriptor + * is only safe to pass to {@link #MemoryFile(FileDescriptor,int)}). It + * should not be used with file descriptor operations that expect a file descriptor + * for a normal file. + * + * The returned file descriptor is not duplicated. + * + * @throws IOException If the memory file has been closed. + * + * @hide + */ + public FileDescriptor getFileDescriptor() throws IOException { + return mFD; + } + + /** + * Checks whether the given file descriptor refers to a memory file. + * + * @throws IOException If <code>fd</code> is not a valid file descriptor. + * + * @hide + */ + public static boolean isMemoryFile(FileDescriptor fd) throws IOException { + return native_is_ashmem_region(fd); + } + + /** + * Converts a file mode string to a <code>prot</code> value as expected by + * native_mmap(). + * + * @throws IllegalArgumentException if the file mode is invalid. + */ + private static int modeToProt(String mode) { + if ("r".equals(mode)) { + return PROT_READ; + } else { + throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'"); + } + } + private class MemoryInputStream extends InputStream { private int mMark = 0; @@ -212,13 +353,22 @@ public class MemoryFile } int result = read(mSingleByte, 0, 1); if (result != 1) { - throw new IOException("read() failed"); + return -1; } return mSingleByte[0]; } @Override public int read(byte buffer[], int offset, int count) throws IOException { + if (offset < 0 || count < 0 || offset + count > buffer.length) { + // readBytes() also does this check, but we need to do it before + // changing count. + throw new IndexOutOfBoundsException(); + } + count = Math.min(count, available()); + if (count < 1) { + return -1; + } int result = readBytes(buffer, mOffset, offset, count); if (result > 0) { mOffset += result; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 9a71f6e0f2d4..6cfcceedfa46 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -457,7 +457,7 @@ public final class Parcel { * Flatten a Map into the parcel at the current dataPosition(), * growing dataCapacity() if needed. The Map keys must be String objects. */ - private void writeMapInternal(Map<String,Object> val) { + /* package */ void writeMapInternal(Map<String,Object> val) { if (val == null) { writeInt(-1); return; @@ -480,23 +480,7 @@ public final class Parcel { return; } - if (val.mParcelledData != null) { - int length = val.mParcelledData.dataSize(); - appendFrom(val.mParcelledData, 0, length); - } else { - writeInt(-1); // dummy, will hold length - int oldPos = dataPosition(); - writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' - - writeMapInternal(val.mMap); - int newPos = dataPosition(); - - // Backpatch length - setDataPosition(oldPos - 4); - int length = newPos - oldPos; - writeInt(length); - setDataPosition(newPos); - } + val.writeToParcel(this, 0); } /** @@ -1352,27 +1336,12 @@ public final class Parcel { * Returns null if the previously written Bundle object was null. */ public final Bundle readBundle(ClassLoader loader) { - int offset = dataPosition(); int length = readInt(); if (length < 0) { return null; } - int magic = readInt(); - if (magic != 0x4C444E42) { - //noinspection ThrowableInstanceNeverThrown - String st = Log.getStackTraceString(new RuntimeException()); - Log.e("Bundle", "readBundle: bad magic number"); - Log.e("Bundle", "readBundle: trace = " + st); - } - - // Advance within this Parcel - setDataPosition(offset + length + 4); - - Parcel p = new Parcel(0); - p.setDataPosition(0); - p.appendFrom(this, offset, length + 4); - p.setDataPosition(0); - final Bundle bundle = new Bundle(p); + + final Bundle bundle = new Bundle(this, length); if (loader != null) { bundle.setClassLoader(loader); } @@ -1380,33 +1349,6 @@ public final class Parcel { } /** - * Read and return a new Bundle object from the parcel at the current - * dataPosition(). Returns null if the previously written Bundle object was - * null. The returned bundle will have its contents fully unpacked using - * the given ClassLoader. - */ - /* package */ Bundle readBundleUnpacked(ClassLoader loader) { - int length = readInt(); - if (length == -1) { - return null; - } - int magic = readInt(); - if (magic != 0x4C444E42) { - //noinspection ThrowableInstanceNeverThrown - String st = Log.getStackTraceString(new RuntimeException()); - Log.e("Bundle", "readBundleUnpacked: bad magic number"); - Log.e("Bundle", "readBundleUnpacked: trace = " + st); - } - Bundle m = new Bundle(loader); - int N = readInt(); - if (N < 0) { - return null; - } - readMapInternal(m.mMap, N, loader); - return m; - } - - /** * Read and return a byte[] object from the parcel. */ public final native byte[] createByteArray(); @@ -1998,7 +1940,7 @@ public final class Parcel { private native void init(int obj); private native void destroy(); - private void readMapInternal(Map outVal, int N, + /* package */ void readMapInternal(Map outVal, int N, ClassLoader loader) { while (N > 0) { Object key = readValue(loader); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 30acef9ee7b5..1214abcad634 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. * @@ -604,6 +618,20 @@ public class Process { */ public static final native void setThreadGroup(int tid, int group) throws IllegalArgumentException, SecurityException; + /** + * Sets the scheduling group for a process and all child threads + * @hide + * @param pid The indentifier of the process to change. + * @param group The target group for this process. + * + * @throws IllegalArgumentException Throws IllegalArgumentException if + * <var>tid</var> does not exist. + * @throws SecurityException Throws SecurityException if your process does + * not have permission to modify the given thread, or to use the given + * priority. + */ + public static final native void setProcessGroup(int pid, int group) + throws IllegalArgumentException, SecurityException; /** * Set the priority of the calling thread, based on Linux priorities. See diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java index edf69eea8a86..3ea9b4a89bbc 100644 --- a/core/java/android/pim/EventRecurrence.java +++ b/core/java/android/pim/EventRecurrence.java @@ -408,13 +408,13 @@ public class EventRecurrence private String dayToString(Resources r, int day) { switch (day) { - case SU: return r.getString(com.android.internal.R.string.sunday); - case MO: return r.getString(com.android.internal.R.string.monday); - case TU: return r.getString(com.android.internal.R.string.tuesday); - case WE: return r.getString(com.android.internal.R.string.wednesday); - case TH: return r.getString(com.android.internal.R.string.thursday); - case FR: return r.getString(com.android.internal.R.string.friday); - case SA: return r.getString(com.android.internal.R.string.saturday); + case SU: return r.getString(com.android.internal.R.string.day_of_week_long_sunday); + case MO: return r.getString(com.android.internal.R.string.day_of_week_long_monday); + case TU: return r.getString(com.android.internal.R.string.day_of_week_long_tuesday); + case WE: return r.getString(com.android.internal.R.string.day_of_week_long_wednesday); + case TH: return r.getString(com.android.internal.R.string.day_of_week_long_thursday); + case FR: return r.getString(com.android.internal.R.string.day_of_week_long_friday); + case SA: return r.getString(com.android.internal.R.string.day_of_week_long_saturday); default: throw new IllegalArgumentException("bad day argument: " + day); } } diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java index 1e9b7aed59cc..cf5664c3814e 100644 --- a/core/java/android/preference/CheckBoxPreference.java +++ b/core/java/android/preference/CheckBoxPreference.java @@ -16,6 +16,7 @@ package android.preference; +import android.app.Service; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; @@ -23,6 +24,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Checkable; import android.widget.TextView; @@ -42,6 +45,9 @@ public class CheckBoxPreference extends Preference { private CharSequence mSummaryOff; private boolean mChecked; + private boolean mSendAccessibilityEventViewClickedType; + + private AccessibilityManager mAccessibilityManager; private boolean mDisableDependentsState; @@ -55,6 +61,9 @@ public class CheckBoxPreference extends Preference { mDisableDependentsState = a.getBoolean( com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false); a.recycle(); + + mAccessibilityManager = + (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE); } public CheckBoxPreference(Context context, AttributeSet attrs) { @@ -64,14 +73,26 @@ public class CheckBoxPreference extends Preference { public CheckBoxPreference(Context context) { this(context, null); } - + @Override protected void onBindView(View view) { super.onBindView(view); - + View checkboxView = view.findViewById(com.android.internal.R.id.checkbox); if (checkboxView != null && checkboxView instanceof Checkable) { ((Checkable) checkboxView).setChecked(mChecked); + + // send an event to announce the value change of the CheckBox and is done here + // because clicking a preference does not immediately change the checked state + // for example when enabling the WiFi + if (mSendAccessibilityEventViewClickedType && + mAccessibilityManager.isEnabled() && + checkboxView.isEnabled()) { + mSendAccessibilityEventViewClickedType = false; + + int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED; + checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); + } } // Sync the summary view @@ -85,7 +106,7 @@ public class CheckBoxPreference extends Preference { summaryView.setText(mSummaryOff); useDefaultSummary = false; } - + if (useDefaultSummary) { final CharSequence summary = getSummary(); if (summary != null) { @@ -111,6 +132,10 @@ public class CheckBoxPreference extends Preference { boolean newValue = !isChecked(); + // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change + // not sending + mSendAccessibilityEventViewClickedType = true; + if (!callChangeListener(newValue)) { return; } @@ -124,10 +149,11 @@ public class CheckBoxPreference extends Preference { * @param checked The checked state. */ public void setChecked(boolean checked) { + mChecked = checked; persistBoolean(checked); - + notifyDependencyChange(shouldDisableDependents()); notifyChanged(); diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 5353b531dd06..95e54324f445 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.Adapter; @@ -147,13 +148,20 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi ListView listView = new ListView(context); bind(listView); - Dialog dialog = mDialog = new Dialog(context, com.android.internal.R.style.Theme_NoTitleBar); + // Set the title bar if title is available, else no title bar + final CharSequence title = getTitle(); + Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title) + ? com.android.internal.R.style.Theme_NoTitleBar + : com.android.internal.R.style.Theme); dialog.setContentView(listView); + if (!TextUtils.isEmpty(title)) { + dialog.setTitle(title); + } dialog.setOnDismissListener(this); if (state != null) { dialog.onRestoreInstanceState(state); } - + // Add the screen to the list of preferences screens opened as dialogs getPreferenceManager().addPreferencesScreen(dialog); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index c597b3c62125..1ba5e25e1d54 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -34,6 +34,12 @@ public class Browser { Uri.parse("content://browser/bookmarks"); /** + * The inline scheme to show embedded content in a browser. + * @hide + */ + public static final Uri INLINE_URI = Uri.parse("inline:"); + + /** * The name of extra data when starting Browser with ACTION_VIEW or * ACTION_SEARCH intent. * <p> @@ -53,7 +59,48 @@ public class Browser { * identifier. */ public static final String EXTRA_APPLICATION_ID = - "com.android.browser.application_id"; + "com.android.browser.application_id"; + + /** + * The content to be rendered when url's scheme is inline. + * @hide + */ + public static final String EXTRA_INLINE_CONTENT ="com.android.browser.inline.content"; + + /** + * The encoding of the inlined content for inline scheme. + * @hide + */ + public static final String EXTRA_INLINE_ENCODING ="com.android.browser.inline.encoding"; + + /** + * The url used when the inline content is falied to render. + * @hide + */ + public static final String EXTRA_INLINE_FAILURL ="com.android.browser.inline.failurl"; + + /** + * The name of the extra data in the VIEW intent. The data is in boolean. + * <p> + * If the Browser is handling the intent and the setting for + * USE_LOCATION_FOR_SERVICES is allow, the Browser will send the location in + * the POST data if this extra data is presented and it is true. + * <p> + * pending api approval + * @hide + */ + public static final String EXTRA_APPEND_LOCATION = "com.android.browser.append_location"; + + /** + * The name of the extra data in the VIEW intent. The data is in the format of + * a byte array. + * <p> + * Any value sent here will be passed in the http request to the provided url as post data. + * <p> + * pending api approval + * @hide + */ + public static final String EXTRA_POST_DATA = "com.android.browser.post_data"; /* if you change column order you must also change indices below */ @@ -132,6 +179,7 @@ public class Browser { /** * Return a cursor pointing to a list of all the bookmarks. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final Cursor getAllBookmarks(ContentResolver cr) throws @@ -143,6 +191,7 @@ public class Browser { /** * Return a cursor pointing to a list of all visited site urls. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final Cursor getAllVisitedUrls(ContentResolver cr) throws @@ -154,6 +203,8 @@ public class Browser { /** * Update the visited history to acknowledge that a site has been * visited. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param url The site being visited. * @param real Whether this is an actual visit, and should be added to the @@ -203,6 +254,8 @@ public class Browser { * of them. This is used to keep our history table to a * reasonable size. Note: it does not prune bookmarks. If the * user wants 1000 bookmarks, the user gets 1000 bookmarks. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * * @param cr The ContentResolver used to access the database. */ @@ -236,6 +289,7 @@ public class Browser { /** * Returns whether there is any history to clear. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @return boolean True if the history can be cleared. */ @@ -261,6 +315,7 @@ public class Browser { /** * Delete all entries from the bookmarks/history table which are * not bookmarks. Also set all visited bookmarks to unvisited. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final void clearHistory(ContentResolver cr) { @@ -270,6 +325,8 @@ public class Browser { /** * Helper function to delete all history items and revert all * bookmarks to zero visits which meet the criteria provided. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param whereClause String to limit the items affected. * null means all items. @@ -332,6 +389,7 @@ public class Browser { /** * Delete all history items from begin to end. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param begin First date to remove. If -1, all dates before end. * Inclusive. @@ -359,6 +417,7 @@ public class Browser { /** * Remove a specific url from the history database. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param url url to remove. */ @@ -372,6 +431,8 @@ public class Browser { /** * Add a search string to the searches database. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param search The string to add to the searches database. */ @@ -401,6 +462,7 @@ public class Browser { } /** * Remove all searches from the search database. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final void clearSearches(ContentResolver cr) { @@ -415,6 +477,7 @@ public class Browser { /** * Request all icons from the database. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param where Clause to be used to limit the query from the database. * Must be an allowable string to be passed into a database query. diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index abd6934a86b3..7d03801d7bd1 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -151,6 +151,9 @@ public class CallLog { int presentation, int callType, long start, int duration) { final ContentResolver resolver = context.getContentResolver(); + // TODO(Moto): Which is correct: original code, this only changes the + // number if the number is empty and never changes the caller info name. + if (false) { if (TextUtils.isEmpty(number)) { if (presentation == Connection.PRESENTATION_RESTRICTED) { number = CallerInfo.PRIVATE_NUMBER; @@ -160,7 +163,22 @@ public class CallLog { number = CallerInfo.UNKNOWN_NUMBER; } } - + } else { + // NEWCODE: From Motorola + + //If this is a private number then set the number to Private, otherwise check + //if the number field is empty and set the number to Unavailable + if (presentation == Connection.PRESENTATION_RESTRICTED) { + number = CallerInfo.PRIVATE_NUMBER; + ci.name = ""; + } else if (presentation == Connection.PRESENTATION_PAYPHONE) { + number = CallerInfo.PAYPHONE_NUMBER; + ci.name = ""; + } else if (TextUtils.isEmpty(number) || presentation == Connection.PRESENTATION_UNKNOWN) { + number = CallerInfo.UNKNOWN_NUMBER; + ci.name = ""; + } + } ContentValues values = new ContentValues(5); values.put(NUMBER, number); diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index 3c23db03ac0a..f2c275e69931 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -137,6 +137,8 @@ public final class Checkin { CRASHES_TRUNCATED, ELAPSED_REALTIME_SEC, ELAPSED_UPTIME_SEC, + HTTP_REQUEST, + HTTP_REUSED, HTTP_STATUS, PHONE_GSM_REGISTERED, PHONE_GPRS_ATTEMPTED, @@ -351,6 +353,3 @@ public final class Checkin { } } } - - - diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 3141f1aede53..84fe1841888e 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -340,27 +340,33 @@ public class Contacts { } /** - * Adds a person to the My Contacts group. - * - * @param resolver the resolver to use - * @param personId the person to add to the group - * @return the URI of the group membership row - * @throws IllegalStateException if the My Contacts group can't be found + * @hide Used in vCard parser code. */ - public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) { - long groupId = 0; + public static long tryGetMyContactsGroupId(ContentResolver resolver) { Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION, Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null); if (groupsCursor != null) { try { if (groupsCursor.moveToFirst()) { - groupId = groupsCursor.getLong(0); + return groupsCursor.getLong(0); } } finally { groupsCursor.close(); } } + return 0; + } + /** + * Adds a person to the My Contacts group. + * + * @param resolver the resolver to use + * @param personId the person to add to the group + * @return the URI of the group membership row + * @throws IllegalStateException if the My Contacts group can't be found + */ + public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) { + long groupId = tryGetMyContactsGroupId(resolver); if (groupId == 0) { throw new IllegalStateException("Failed to find the My Contacts group"); } @@ -869,6 +875,17 @@ public class Contacts { public static final int TYPE_OTHER = 3; /** + * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. + */ + public static final int MOBILE_EMAIL_TYPE_INDEX = 2; + + /** + * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. + * This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone. + */ + public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL"; + + /** * The user defined label for the the contact method. * <P>Type: TEXT</P> */ @@ -1005,7 +1022,13 @@ public class Contacts { } } else { if (!TextUtils.isEmpty(label)) { - display = label; + if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) { + display = + context.getString( + com.android.internal.R.string.mobileEmailTypeName); + } else { + display = label; + } } } break; diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index b6f96c4e0d4b..21e5865fef70 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -344,7 +344,10 @@ public final class MediaStore // Check if file exists with a FileInputStream FileInputStream stream = new FileInputStream(imagePath); try { - return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description); + Bitmap bm = BitmapFactory.decodeFile(imagePath); + String ret = insertImage(cr, bm, name, description); + bm.recycle(); + return ret; } finally { try { stream.close(); @@ -719,9 +722,15 @@ public final class MediaStore */ public static String keyFor(String name) { if (name != null) { + boolean sortfirst = false; if (name.equals(android.media.MediaFile.UNKNOWN_STRING)) { return "\001"; } + // Check if the first character is \001. We use this to + // force sorting of certain special files, like the silent ringtone. + if (name.startsWith("\001")) { + sortfirst = true; + } name = name.trim().toLowerCase(); if (name.startsWith("the ")) { name = name.substring(4); @@ -737,7 +746,7 @@ public final class MediaStore name.endsWith(", a") || name.endsWith(",a")) { name = name.substring(0, name.lastIndexOf(',')); } - name = name.replaceAll("[\\[\\]\\(\\)'.,?!]", "").trim(); + name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim(); if (name.length() > 0) { // Insert a separator between the characters to avoid // matches on a partial character. If we ever change @@ -750,7 +759,11 @@ public final class MediaStore b.append('.'); } name = b.toString(); - return DatabaseUtils.getCollationKey(name); + String key = DatabaseUtils.getCollationKey(name); + if (sortfirst) { + key = "\001" + key; + } + return key; } else { return ""; } @@ -797,7 +810,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = TITLE; + public static final String DEFAULT_SORT_ORDER = TITLE_KEY; /** * Activity Action: Start SoundRecorder application. @@ -894,7 +907,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = TITLE; + public static final String DEFAULT_SORT_ORDER = TITLE_KEY; /** * The ID of the audio file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4dd6524fd2c0..aa583ac35b5e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -148,7 +148,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS"; - + /** * Activity Action: Show settings to allow configuration of a static IP * address for Wi-Fi. @@ -305,7 +305,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; - + /** * Activity Action: Show settings to manage installed applications. * <p> @@ -319,7 +319,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; - + /** * Activity Action: Show settings for system update functionality. * <p> @@ -329,7 +329,7 @@ public final class Settings { * Input: Nothing. * <p> * Output: Nothing. - * + * * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -349,7 +349,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS"; - + /** * Activity Action: Show settings for selecting the network operator. * <p> @@ -404,7 +404,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; - + // End of Intent actions for Settings private static final String JID_RESOURCE_PREFIX = "android"; @@ -495,7 +495,7 @@ public final class Settings { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version"; private static volatile NameValueCache mNameValueCache = null; - + private static final HashSet<String> MOVED_TO_SECURE; static { MOVED_TO_SECURE = new HashSet<String>(30); @@ -901,12 +901,12 @@ public final class Settings { * plugged in. */ public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; - + /** * Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep. */ public static final int WIFI_SLEEP_POLICY_NEVER = 2; - + /** * Whether to use static IP and other static network attributes. * <p> @@ -1025,6 +1025,14 @@ public final class Settings { public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; /** + * If 0, the compatibility mode is off for all applications. + * If 1, older applications run under compatibility mode. + * TODO: remove this settings before code freeze (bug/1907571) + * @hide + */ + public static final String COMPATIBILITY_MODE = "compatibility_mode"; + + /** * The screen backlight brightness between 0 and 255. */ public static final String SCREEN_BRIGHTNESS = "screen_brightness"; @@ -1115,12 +1123,12 @@ public final class Settings { * Note: This is a one-off setting that will be removed in the future * when there is profile support. For this reason, it is kept hidden * from the public APIs. - * + * * @hide */ - public static final String NOTIFICATIONS_USE_RING_VOLUME = + public static final String NOTIFICATIONS_USE_RING_VOLUME = "notifications_use_ring_volume"; - + /** * The mapping of stream type (integer) to its setting. */ @@ -1188,7 +1196,7 @@ public final class Settings { * feature converts two spaces to a "." and space. */ public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate"; - + /** * Setting to showing password characters in text editors. 1 = On, 0 = Off */ @@ -1266,17 +1274,125 @@ 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). */ public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; - + /** * Whether the haptic feedback (long presses, ...) are enabled. The value is * boolean (1 or 0). */ public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; + /** + * Whether live web suggestions while the user types into search dialogs are + * enabled. Browsers and other search UIs should respect this, as it allows + * a user to avoid sending partial queries to a search engine, if it poses + * any privacy concern. The value is boolean (1 or 0). + */ + public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; + + /** + * Settings to backup. This is here so that it's in the same place as the settings + * keys and easy to update. + * @hide + */ + public static final String[] SETTINGS_TO_BACKUP = { + STAY_ON_WHILE_PLUGGED_IN, + END_BUTTON_BEHAVIOR, + WIFI_SLEEP_POLICY, + WIFI_USE_STATIC_IP, + WIFI_STATIC_IP, + WIFI_STATIC_GATEWAY, + WIFI_STATIC_NETMASK, + WIFI_STATIC_DNS1, + WIFI_STATIC_DNS2, + BLUETOOTH_DISCOVERABILITY, + BLUETOOTH_DISCOVERABILITY_TIMEOUT, + DIM_SCREEN, + SCREEN_OFF_TIMEOUT, + SCREEN_BRIGHTNESS, + VIBRATE_ON, + NOTIFICATIONS_USE_RING_VOLUME, + MODE_RINGER, + MODE_RINGER_STREAMS_AFFECTED, + MUTE_STREAMS_AFFECTED, + VOLUME_VOICE, + VOLUME_SYSTEM, + VOLUME_RING, + VOLUME_MUSIC, + VOLUME_ALARM, + VOLUME_NOTIFICATION, + VOLUME_VOICE + APPEND_FOR_LAST_AUDIBLE, + VOLUME_SYSTEM + APPEND_FOR_LAST_AUDIBLE, + VOLUME_RING + APPEND_FOR_LAST_AUDIBLE, + VOLUME_MUSIC + APPEND_FOR_LAST_AUDIBLE, + VOLUME_ALARM + APPEND_FOR_LAST_AUDIBLE, + VOLUME_NOTIFICATION + APPEND_FOR_LAST_AUDIBLE, + TEXT_AUTO_REPLACE, + TEXT_AUTO_CAPS, + TEXT_AUTO_PUNCTUATE, + TEXT_SHOW_PASSWORD, + AUTO_TIME, + TIME_12_24, + DATE_FORMAT, + ACCELEROMETER_ROTATION, + DTMF_TONE_WHEN_DIALING, + DTMF_TONE_TYPE_WHEN_DIALING, + EMERGENCY_TONE, + CALL_AUTO_RETRY, + HEARING_AID, + TTY_MODE, + SOUND_EFFECTS_ENABLED, + HAPTIC_FEEDBACK_ENABLED, + SHOW_WEB_SUGGESTIONS + }; + // Settings moved to Settings.Secure /** @@ -1321,7 +1437,7 @@ public final class Settings { */ @Deprecated public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS; - + /** * @deprecated Use {@link android.provider.Settings.Secure#LOCATION_PROVIDERS_ALLOWED} * instead @@ -1334,7 +1450,7 @@ public final class Settings { */ @Deprecated public static final String LOGGING_ID = Secure.LOGGING_ID; - + /** * @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead */ @@ -1374,7 +1490,7 @@ public final class Settings { */ @Deprecated public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED; - + /** * @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead */ @@ -1412,7 +1528,7 @@ public final class Settings { @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; - + /** * @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead @@ -1448,7 +1564,7 @@ public final class Settings { @Deprecated public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS; - + /** * @deprecated Use * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead @@ -1824,19 +1940,19 @@ public final class Settings { * Whether the device has been provisioned (0 = false, 1 = true) */ public static final String DEVICE_PROVISIONED = "device_provisioned"; - + /** * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; - + /** * Host name and port for a user-selected proxy. */ public static final String HTTP_PROXY = "http_proxy"; - + /** * Whether the package installer should allow installation of apps downloaded from * sources other than the Android Market (vending machine). @@ -1845,12 +1961,12 @@ public final class Settings { * 0 = only allow installing from the Android Market */ public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; - + /** * Comma-separated list of location providers that activities may access. */ public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; - + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. @@ -1872,19 +1988,19 @@ public final class Settings { * connectivity service should touch this. */ public static final String NETWORK_PREFERENCE = "network_preference"; - - /** + + /** */ public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled"; - - /** + + /** */ public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update"; - - /** + + /** */ public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; - + /** * Settings classname to launch when Settings is clicked from All * Applications. Needed because of user testing between the old @@ -1892,18 +2008,67 @@ public final class Settings { */ // TODO: 881807 public static final String SETTINGS_CLASSNAME = "settings_classname"; - + /** * USB Mass Storage Enabled */ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; - + /** * If this setting is set (to anything), then all references * to Gmail on the device must change to Google Mail. */ public static final String USE_GOOGLE_MAIL = "use_google_mail"; - + + /** + * If accessibility is enabled. + */ + public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; + + /** + * List of the enabled accessibility providers. + */ + public static final String ENABLED_ACCESSIBILITY_SERVICES = + "enabled_accessibility_services"; + + /** + * Setting to always use the default text-to-speech settings regardless + * of the application settings. + * 1 = override application settings, + * 0 = use application settings (if specified). + */ + public static final String TTS_USE_DEFAULTS = "tts_use_defaults"; + + /** + * Default text-to-speech engine speech rate. 100 = 1x + */ + public static final String TTS_DEFAULT_RATE = "tts_default_rate"; + + /** + * Default text-to-speech engine pitch. 100 = 1x + */ + public static final String TTS_DEFAULT_PITCH = "tts_default_pitch"; + + /** + * Default text-to-speech engine. + */ + public static final String TTS_DEFAULT_SYNTH = "tts_default_synth"; + + /** + * Default text-to-speech language. + */ + public static final String TTS_DEFAULT_LANG = "tts_default_lang"; + + /** + * Default text-to-speech country. + */ + public static final String TTS_DEFAULT_COUNTRY = "tts_default_country"; + + /** + * Default text-to-speech locale variant. + */ + public static final String TTS_DEFAULT_VARIANT = "tts_default_variant"; + /** * Whether to notify the user of open networks. * <p> @@ -1915,64 +2080,64 @@ public final class Settings { */ public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; - + /** * Delay (in seconds) before repeating the Wi-Fi networks available notification. * Connecting to a network will reset the timer. */ public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay"; - + /** * The number of radio channels that are allowed in the local * 802.11 regulatory domain. * @hide */ public static final String WIFI_NUM_ALLOWED_CHANNELS = "wifi_num_allowed_channels"; - + /** * When the number of open networks exceeds this number, the * least-recently-used excess networks will be removed. */ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; - + /** * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this. */ public static final String WIFI_ON = "wifi_on"; - + /** * The acceptable packet loss percentage (range 0 - 100) before trying * another AP on the same network. */ public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = "wifi_watchdog_acceptable_packet_loss_percentage"; - + /** * The number of access points required for a network in order for the * watchdog to monitor it. */ public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count"; - + /** * The delay between background checks. */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = "wifi_watchdog_background_check_delay_ms"; - + /** * Whether the Wi-Fi watchdog is enabled for background checking even * after it thinks the user has connected to a good access point. */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = "wifi_watchdog_background_check_enabled"; - + /** * The timeout for a background ping */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = "wifi_watchdog_background_check_timeout_ms"; - + /** * The number of initial pings to perform that *may* be ignored if they * fail. Again, if these fail, they will *not* be used in packet loss @@ -1981,7 +2146,7 @@ public final class Settings { */ public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = "wifi_watchdog_initial_ignored_ping_count"; - + /** * The maximum number of access points (per network) to attempt to test. * If this number is reached, the watchdog will no longer monitor the @@ -1989,7 +2154,7 @@ public final class Settings { * networks containing multiple APs whose DNS does not respond to pings. */ public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks"; - + /** * Whether the Wi-Fi watchdog is enabled. */ @@ -2004,24 +2169,24 @@ public final class Settings { * The number of pings to test if an access point is a good connection. */ public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count"; - + /** * The delay between pings. */ public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms"; - + /** * The timeout per ping. */ public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms"; - + /** * The maximum number of times we will retry a connection to an access * point for which we have failed in acquiring an IP address from DHCP. * A value of N means that we will make N+1 connection attempts in all. */ public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; - + /** * Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile * data connectivity to be established after a disconnect from Wi-Fi. @@ -2051,20 +2216,29 @@ public final class Settings { public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode"; /** - * represents current active phone class - * 1 = GSM-Phone, 0 = CDMA-Phone + * The preferred network mode 7 = Global + * 6 = EvDo only + * 5 = CDMA w/o EvDo + * 4 = CDMA / EvDo auto + * 3 = GSM / WCDMA auto + * 2 = WCDMA only + * 1 = GSM only + * 0 = GSM / WCDMA preferred * @hide */ - public static final String CURRENT_ACTIVE_PHONE = "current_active_phone"; + public static final String PREFERRED_NETWORK_MODE = + "preferred_network_mode"; /** - * The preferred network mode 7 = Global, CDMA default - * 4 = CDMA only - * 3 = GSM/UMTS only + * The preferred TTY mode 0 = TTy Off, CDMA default + * 1 = TTY Full + * 2 = TTY HCO + * 3 = TTY VCO * @hide */ - public static final String PREFERRED_NETWORK_MODE = - "preferred_network_mode"; + public static final String PREFERRED_TTY_MODE = + "preferred_tty_mode"; + /** * CDMA Cell Broadcast SMS @@ -2100,6 +2274,71 @@ public final class Settings { public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; /** + * Flag for allowing service provider to use location information to improve products and + * services. + * Type: int ( 0 = disallow, 1 = allow ) + * @hide + */ + public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + + /** + * Controls whether settings backup is enabled. + * Type: int ( 0 = disabled, 1 = enabled ) + * @hide + */ + public static final String BACKUP_ENABLED = "backup_enabled"; + + /** + * Indicates whether settings backup has been fully provisioned. + * Type: int ( 0 = unprovisioned, 1 = fully provisioned ) + * @hide + */ + public static final String BACKUP_PROVISIONED = "backup_provisioned"; + + /** + * Component of the transport to use for backup/restore. + * @hide + */ + public static final String BACKUP_TRANSPORT = "backup_transport"; + + /** + * Version for which the setup wizard was last shown. Bumped for + * each release when there is new setup information to show. + * @hide + */ + public static final String LAST_SETUP_SHOWN = "last_setup_shown"; + + /** + * @hide + */ + public static final String[] SETTINGS_TO_BACKUP = { + ADB_ENABLED, + ALLOW_MOCK_LOCATION, + INSTALL_NON_MARKET_APPS, + PARENTAL_CONTROL_ENABLED, + PARENTAL_CONTROL_REDIRECT_URL, + USB_MASS_STORAGE_ENABLED, + ACCESSIBILITY_ENABLED, + ENABLED_ACCESSIBILITY_SERVICES, + TTS_USE_DEFAULTS, + TTS_DEFAULT_RATE, + TTS_DEFAULT_PITCH, + TTS_DEFAULT_SYNTH, + TTS_DEFAULT_LANG, + TTS_DEFAULT_COUNTRY, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_NUM_ALLOWED_CHANNELS, + WIFI_NUM_OPEN_NETWORKS_KEPT, + BACKGROUND_DATA, + PREFERRED_NETWORK_MODE, + PREFERRED_TTY_MODE, + CDMA_CELL_BROADCAST_SMS, + PREFERRED_CDMA_SUBSCRIPTION, + ENHANCED_VOICE_PRIVACY_ENABLED + }; + + /** * Helper method for determining if a location provider is enabled. * @param cr the content resolver to use * @param provider the location provider to query @@ -2115,7 +2354,7 @@ public final class Settings { allowedProviders.startsWith(provider + ",") || allowedProviders.endsWith("," + provider)); } - return false; + return false; } /** @@ -2139,7 +2378,7 @@ public final class Settings { putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); } } - + /** * Gservices settings, containing the network names for Google's * various services. This table holds simple name/addr pairs. @@ -2160,6 +2399,13 @@ public final class Settings { public static final String CHANGED_ACTION = "com.google.gservices.intent.action.GSERVICES_CHANGED"; + /** + * Intent action to override Gservices for testing. (Requires WRITE_GSERVICES permission.) + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String OVERRIDE_ACTION = + "com.google.gservices.intent.action.GSERVICES_OVERRIDE"; + private static volatile NameValueCache mNameValueCache = null; private static final Object mNameValueCacheLock = new Object(); @@ -2260,7 +2506,7 @@ public final class Settings { * Event tags from the kernel event log to upload during checkin. */ public static final String CHECKIN_EVENTS = "checkin_events"; - + /** * Event tags for list of services to upload during checkin. */ @@ -2428,12 +2674,34 @@ public final class Settings { public static final String GMAIL_BUFFER_SERVER_RESPONSE = "gmail_buffer_server_response"; /** + * The maximum size in bytes allowed for the provider to gzip a protocol buffer uploaded to + * the server. + */ + public static final String GMAIL_MAX_GZIP_SIZE = "gmail_max_gzip_size_bytes"; + + /** * Controls whether Gmail will discard uphill operations that repeatedly fail. Value must be * an integer where non-zero means true. Defaults to 1. */ public static final String GMAIL_DISCARD_ERROR_UPHILL_OP = "gmail_discard_error_uphill_op"; /** + * Controls how many attempts Gmail will try to upload an uphill operations before it + * abandons the operation. Defaults to 20. + */ + public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_discard_error_uphill_op"; + + /** + * the transcoder URL for mobile devices. + */ + public static final String TRANSCODER_URL = "mobile_transcoder_url"; + + /** + * URL that points to the privacy terms of the Google Talk service. + */ + public static final String GTALK_TERMS_OF_SERVICE_URL = "gtalk_terms_of_service_url"; + + /** * Hostname of the GTalk server. */ public static final String GTALK_SERVICE_HOSTNAME = "gtalk_hostname"; @@ -2561,6 +2829,21 @@ public final class Settings { "gtalk_ssl_handshake_timeout_ms"; /** + * Compress the gtalk stream. + */ + public static final String GTALK_COMPRESS = "gtalk_compress"; + + /** + * This is the timeout for which Google Talk will send the message using bareJID. In a + * established chat between two XMPP endpoints, Google Talk uses fullJID in the format + * of user@domain/resource in order to send the message to the specific client. However, + * if Google Talk hasn't received a message from that client after some time, it would + * fall back to use the bareJID, which would broadcast the message to all clients for + * the other user. + */ + public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms"; + + /** * Enable use of ssl session caching. * 'db' - save each session in a (per process) database * 'file' - save each session in a (per process) file @@ -2657,6 +2940,20 @@ public final class Settings { public static final String VENDING_TAB_2_TITLE = "vending_tab_2_title"; /** + * Frequency in milliseconds at which we should request MCS heartbeats + * from the Vending Machine client. + */ + public static final String VENDING_HEARTBEAT_FREQUENCY_MS = + "vending_heartbeat_frequency_ms"; + + /** + * Frequency in milliseconds at which we should resend pending download + * requests to the API Server from the Vending Machine client. + */ + public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS = + "vending_pd_resend_frequency_ms"; + + /** * URL that points to the legal terms of service to display in Settings. * <p> * This should be a https URL. For a pretty user-friendly URL, use @@ -2796,12 +3093,12 @@ public final class Settings { * out without asking for use permit, to limit the un-authorized SMS * usage. */ - public static final String SMS_OUTGOING_CEHCK_INTERVAL_MS = + public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = "sms_outgoing_check_interval_ms"; /** * The number of outgoing SMS sent without asking for user permit - * (of {@link #SMS_OUTGOING_CEHCK_INTERVAL_MS} + * (of {@link #SMS_OUTGOING_CHECK_INTERVAL_MS} */ public static final String SMS_OUTGOING_CEHCK_MAX_COUNT = "sms_outgoing_check_max_count"; @@ -2950,13 +3247,21 @@ public final class Settings { public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold"; public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; - + /** * An email address that anr bugreports should be sent to. */ public static final String ANR_BUGREPORT_RECIPIENT = "anr_bugreport_recipient"; /** + * Flag for allowing service provider to use location information to improve products and + * services. + * Type: int ( 0 = disallow, 1 = allow ) + * @deprecated + */ + public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + + /** * @deprecated * @hide */ @@ -3094,7 +3399,7 @@ public final class Settings { /** * Add a new bookmark to the system. - * + * * @param cr The ContentResolver to query. * @param intent The desired target of the bookmark. * @param title Bookmark title that is shown to the user; null if none @@ -3159,7 +3464,7 @@ public final class Settings { /** * Return the title as it should be displayed to the user. This takes * care of localizing bookmarks that point to activities. - * + * * @param context A context. * @param cursor A cursor pointing to the row whose title should be * returned. The cursor must contain at least the {@link #TITLE} @@ -3174,24 +3479,24 @@ public final class Settings { throw new IllegalArgumentException( "The cursor must contain the TITLE and INTENT columns."); } - + String title = cursor.getString(titleColumn); if (!TextUtils.isEmpty(title)) { return title; } - + String intentUri = cursor.getString(intentColumn); if (TextUtils.isEmpty(intentUri)) { return ""; } - + Intent intent; try { intent = Intent.getIntent(intentUri); } catch (URISyntaxException e) { return ""; } - + PackageManager packageManager = context.getPackageManager(); ResolveInfo info = packageManager.resolveActivity(intent, 0); return info != null ? info.loadLabel(packageManager) : ""; @@ -3247,4 +3552,3 @@ public final class Settings { return "android-" + Long.toHexString(androidId); } } - diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index a4145c487696..4078fa6d5a73 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -466,6 +466,24 @@ public final class Telephony { */ public static final class Intents { /** + * Set by BroadcastReceiver. Indicates the message was handled + * successfully. + */ + public static final int RESULT_SMS_HANDLED = 1; + + /** + * Set by BroadcastReceiver. Indicates a generic error while + * processing the message. + */ + public static final int RESULT_SMS_GENERIC_ERROR = 2; + + /** + * Set by BroadcastReceiver. Indicates insufficient memory to store + * the message. + */ + public static final int RESULT_SMS_OUT_OF_MEMORY = 3; + + /** * Broadcast Action: A new text based SMS message has been received * by the device. The intent will have the following extra * values:</p> @@ -476,7 +494,10 @@ public final class Telephony { * </ul> * * <p>The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}</p> + * {@link #getMessagesFromIntent(Intent)}.</p> + * + * <p>If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.</p> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SMS_RECEIVED_ACTION = @@ -493,7 +514,10 @@ public final class Telephony { * </ul> * * <p>The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}</p> + * {@link #getMessagesFromIntent(Intent)}.</p> + * + * <p>If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.</p> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DATA_SMS_RECEIVED_ACTION = @@ -510,6 +534,9 @@ public final class Telephony { * <li><em>pduType (Integer)</em> - The WAP PDU type</li> * <li><em>data</em> - The data payload of the message</li> * </ul> + * + * <p>If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.</p> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WAP_PUSH_RECEIVED_ACTION = diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 8e5cee9bbfba..8c843efd7e4e 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -372,6 +372,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mEventLoop.onModeChanged(getModeNative()); } + if (mIsAirplaneSensitive && isAirplaneModeOn()) { + disable(false); + } + } } @@ -1220,6 +1224,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { break; } pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + headset.close(); } diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 03623d6bf411..373e61ff97f0 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -17,48 +17,69 @@ package android.server.search; import android.app.ISearchManager; +import android.app.ISearchManagerCallback; +import android.app.SearchDialog; +import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; /** * This is a simplified version of the Search Manager service. It no longer handles - * presentation (UI). Its function is to maintain the map & list of "searchable" + * presentation (UI). Its function is to maintain the map & list of "searchable" * items, which provides a mapping from individual activities (where a user might have * invoked search) to specific searchable activities (where the search will be dispatched). */ public class SearchManagerService extends ISearchManager.Stub + implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // general debugging support private static final String TAG = "SearchManagerService"; - private static final boolean DEBUG = false; - - // configuration choices - private static final boolean IMMEDIATE_SEARCHABLES_UPDATE = true; + private static final boolean DBG = false; // class maintenance and general shared data private final Context mContext; private final Handler mHandler; private boolean mSearchablesDirty; - private Searchables mSearchables; - + private final Searchables mSearchables; + + final SearchDialog mSearchDialog; + ISearchManagerCallback mCallback = null; + + private final boolean mDisabledOnBoot; + + private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog"; + /** * Initializes the Search Manager service in the provided system context. * Only one instance of this object should be created! * * @param context to use for accessing DB, window manager, etc. */ - public SearchManagerService(Context context) { + public SearchManagerService(Context context) { mContext = context; mHandler = new Handler(); mSearchablesDirty = true; mSearchables = new Searchables(context); - + mSearchDialog = new SearchDialog(context); + mSearchDialog.setOnCancelListener(this); + mSearchDialog.setOnDismissListener(this); + // Setup the infrastructure for updating and maintaining the list // of searchable activities. IntentFilter filter = new IntentFilter(); @@ -67,17 +88,18 @@ public class SearchManagerService extends ISearchManager.Stub filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); - + // After startup settles down, preload the searchables list, // which will reduce the delay when the search UI is invoked. - if (IMMEDIATE_SEARCHABLES_UPDATE) { - mHandler.post(mRunUpdateSearchable); - } + mHandler.post(mRunUpdateSearchable); + + // allows disabling of search dialog for stress testing runs + mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY)); } - + /** * Listens for intent broadcasts. - * + * * The primary purpose here is to refresh the "searchables" list * if packages are added/removed. */ @@ -85,29 +107,25 @@ public class SearchManagerService extends ISearchManager.Stub @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - + // First, test for intents that matter at any time if (action.equals(Intent.ACTION_PACKAGE_ADDED) || action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_CHANGED)) { mSearchablesDirty = true; - if (IMMEDIATE_SEARCHABLES_UPDATE) { - mHandler.post(mRunUpdateSearchable); - } + mHandler.post(mRunUpdateSearchable); return; } } }; - + /** * This runnable (for the main handler / UI thread) will update the searchables list. */ private Runnable mRunUpdateSearchable = new Runnable() { public void run() { - if (mSearchablesDirty) { - updateSearchables(); - } - } + updateSearchablesIfDirty(); + } }; /** @@ -115,42 +133,251 @@ public class SearchManagerService extends ISearchManager.Stub * a package add/remove broadcast message. */ private void updateSearchables() { + if (DBG) debug("updateSearchables()"); mSearchables.buildSearchableList(); mSearchablesDirty = false; } /** + * Updates the list of searchables if needed. + */ + private void updateSearchablesIfDirty() { + if (mSearchablesDirty) { + updateSearchables(); + } + } + + /** * Returns the SearchableInfo for a given activity * * @param launchActivity The activity from which we're launching this search. * @param globalSearch If false, this will only launch the search that has been specifically - * defined by the application (which is usually defined as a local search). If no default + * defined by the application (which is usually defined as a local search). If no default * search is defined in the current application or activity, no search will be launched. * If true, this will always launch a platform-global (e.g. web-based) search instead. * @return Returns a SearchableInfo record describing the parameters of the search, * or null if no searchable metadata was available. */ public SearchableInfo getSearchableInfo(ComponentName launchActivity, boolean globalSearch) { - // final check. however we should try to avoid this, because - // it slows down the entry into the UI. - if (mSearchablesDirty) { - updateSearchables(); - } + updateSearchablesIfDirty(); SearchableInfo si = null; if (globalSearch) { si = mSearchables.getDefaultSearchable(); } else { + if (launchActivity == null) { + Log.e(TAG, "getSearchableInfo(), activity == null"); + return null; + } si = mSearchables.getSearchableInfo(launchActivity); } return si; } - + /** * Returns a list of the searchable activities that can be included in global search. */ public List<SearchableInfo> getSearchablesInGlobalSearch() { + updateSearchablesIfDirty(); return mSearchables.getSearchablesInGlobalSearchList(); } + /** + * Launches the search UI on the main thread of the service. + * + * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) + */ + public void startSearch(final String initialQuery, + final boolean selectInitialQuery, + final ComponentName launchActivity, + final Bundle appSearchData, + final boolean globalSearch, + final ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("startSearch()"); + Runnable task = new Runnable() { + public void run() { + performStartSearch(initialQuery, + selectInitialQuery, + launchActivity, + appSearchData, + globalSearch, + searchManagerCallback); + } + }; + mHandler.post(task); + } + + /** + * Actually launches the search. This must be called on the service UI thread. + */ + /*package*/ void performStartSearch(String initialQuery, + boolean selectInitialQuery, + ComponentName launchActivity, + Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("performStartSearch()"); + + if (mDisabledOnBoot) { + Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY + + " system property is set."); + return; + } + + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch); + if (searchManagerCallback != null) { + mCallback = searchManagerCallback; + } + } + + /** + * Cancels the search dialog. Can be called from any thread. + */ + public void stopSearch() { + if (DBG) debug("stopSearch()"); + mHandler.post(new Runnable() { + public void run() { + performStopSearch(); + } + }); + } + + /** + * Cancels the search dialog. Must be called from the service UI thread. + */ + /*package*/ void performStopSearch() { + if (DBG) debug("performStopSearch()"); + mSearchDialog.cancel(); + } + + /** + * Determines if the Search UI is currently displayed. + * + * @see SearchManager#isVisible() + */ + public boolean isVisible() { + return postAndWait(mIsShowing, false, "isShowing()"); + } + + private final Callable<Boolean> mIsShowing = new Callable<Boolean>() { + public Boolean call() { + return mSearchDialog.isShowing(); + } + }; + + public Bundle onSaveInstanceState() { + return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()"); + } + + private final Callable<Bundle> mOnSaveInstanceState = new Callable<Bundle>() { + public Bundle call() { + if (mSearchDialog.isShowing()) { + return mSearchDialog.onSaveInstanceState(); + } else { + return null; + } + } + }; + + public void onRestoreInstanceState(final Bundle searchDialogState) { + if (searchDialogState != null) { + mHandler.post(new Runnable() { + public void run() { + mSearchDialog.onRestoreInstanceState(searchDialogState); + } + }); + } + } + + public void onConfigurationChanged(final Configuration newConfig) { + mHandler.post(new Runnable() { + public void run() { + if (mSearchDialog.isShowing()) { + mSearchDialog.onConfigurationChanged(newConfig); + } + } + }); + } + + /** + * Called by {@link SearchDialog} when it goes away. + */ + public void onDismiss(DialogInterface dialog) { + if (DBG) debug("onDismiss()"); + if (mCallback != null) { + try { + mCallback.onDismiss(); + } catch (RemoteException ex) { + Log.e(TAG, "onDismiss() failed: " + ex); + } + } + } + + /** + * Called by {@link SearchDialog} when the user or activity cancels search. + * When this is called, {@link #onDismiss} is called too. + */ + public void onCancel(DialogInterface dialog) { + if (DBG) debug("onCancel()"); + if (mCallback != null) { + try { + mCallback.onCancel(); + } catch (RemoteException ex) { + Log.e(TAG, "onCancel() failed: " + ex); + } + } + } + + /** + * Returns a list of the searchable activities that handle web searches. + */ + public List<SearchableInfo> getSearchablesForWebSearch() { + updateSearchablesIfDirty(); + return mSearchables.getSearchablesForWebSearchList(); + } + + /** + * Returns the default searchable activity for web searches. + */ + public SearchableInfo getDefaultSearchableForWebSearch() { + updateSearchablesIfDirty(); + return mSearchables.getDefaultSearchableForWebSearch(); + } + + /** + * Sets the default searchable activity for web searches. + */ + public void setDefaultWebSearch(ComponentName component) { + mSearchables.setDefaultWebSearch(component); + } + + /** + * Runs an operation on the handler for the service, blocks until it returns, + * and returns the value returned by the operation. + * + * @param <V> Return value type. + * @param callable Operation to run. + * @param errorResult Value to return if the operations throws an exception. + * @param name Operation name to include in error log messages. + * @return The value returned by the operation. + */ + private <V> V postAndWait(Callable<V> callable, V errorResult, String name) { + FutureTask<V> task = new FutureTask<V>(callable); + mHandler.post(task); + try { + return task.get(); + } catch (InterruptedException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } catch (ExecutionException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } } diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 842fc7573aef..8ef1f154d72d 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -40,7 +40,7 @@ import java.util.HashMap; public final class SearchableInfo implements Parcelable { // general debugging support - private static final boolean DBG = true; + private static final boolean DBG = false; private static final String LOG_TAG = "SearchableInfo"; // static strings used for XML lookups. @@ -66,6 +66,8 @@ public final class SearchableInfo implements Parcelable { private final int mSearchInputType; private final int mSearchImeOptions; private final boolean mIncludeInGlobalSearch; + private final boolean mQueryAfterZeroResults; + private final String mSettingsDescription; private final String mSuggestAuthority; private final String mSuggestPath; private final String mSuggestSelection; @@ -133,6 +135,14 @@ public final class SearchableInfo implements Parcelable { public boolean shouldRewriteQueryFromText() { return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT); } + + /** + * Gets the description to use for this source in system search settings, or null if + * none has been specified. + */ + public String getSettingsDescription() { + return mSettingsDescription; + } /** * Retrieve the path for obtaining search suggestions. @@ -276,7 +286,11 @@ public final class SearchableInfo implements Parcelable { EditorInfo.IME_ACTION_SEARCH); mIncludeInGlobalSearch = a.getBoolean( com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false); + mQueryAfterZeroResults = a.getBoolean( + com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false); + mSettingsDescription = a.getString( + com.android.internal.R.styleable.Searchable_searchSettingsDescription); mSuggestAuthority = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestAuthority); mSuggestPath = a.getString( @@ -317,7 +331,7 @@ public final class SearchableInfo implements Parcelable { // for now, implement some form of rules - minimal data if (mLabelId == 0) { - throw new IllegalArgumentException("No label."); + throw new IllegalArgumentException("Search label must be a resource reference."); } } @@ -438,13 +452,18 @@ public final class SearchableInfo implements Parcelable { xml.close(); if (DBG) { - Log.d(LOG_TAG, "Checked " + activityInfo.name - + ",label=" + searchable.getLabelId() - + ",icon=" + searchable.getIconId() - + ",suggestAuthority=" + searchable.getSuggestAuthority() - + ",target=" + searchable.getSearchActivity().getClassName() - + ",global=" + searchable.shouldIncludeInGlobalSearch() - + ",threshold=" + searchable.getSuggestThreshold()); + if (searchable != null) { + Log.d(LOG_TAG, "Checked " + activityInfo.name + + ",label=" + searchable.getLabelId() + + ",icon=" + searchable.getIconId() + + ",suggestAuthority=" + searchable.getSuggestAuthority() + + ",target=" + searchable.getSearchActivity().getClassName() + + ",global=" + searchable.shouldIncludeInGlobalSearch() + + ",settingsDescription=" + searchable.getSettingsDescription() + + ",threshold=" + searchable.getSuggestThreshold()); + } else { + Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); + } } return searchable; } @@ -637,6 +656,17 @@ public final class SearchableInfo implements Parcelable { } /** + * Checks whether this searchable activity should be invoked after a query returned zero + * results. + * + * @return The value of the <code>queryAfterZeroResults</code> attribute, + * or <code>false</code> if the attribute is not set. + */ + public boolean queryAfterZeroResults() { + return mQueryAfterZeroResults; + } + + /** * Support for parcelable and aidl operations. */ public static final Parcelable.Creator<SearchableInfo> CREATOR @@ -667,7 +697,9 @@ public final class SearchableInfo implements Parcelable { mSearchInputType = in.readInt(); mSearchImeOptions = in.readInt(); mIncludeInGlobalSearch = in.readInt() != 0; - + mQueryAfterZeroResults = in.readInt() != 0; + + mSettingsDescription = in.readString(); mSuggestAuthority = in.readString(); mSuggestPath = in.readString(); mSuggestSelection = in.readString(); @@ -702,7 +734,9 @@ public final class SearchableInfo implements Parcelable { dest.writeInt(mSearchInputType); dest.writeInt(mSearchImeOptions); dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); + dest.writeInt(mQueryAfterZeroResults ? 1 : 0); + dest.writeString(mSettingsDescription); dest.writeString(mSuggestAuthority); dest.writeString(mSuggestPath); dest.writeString(mSuggestSelection); diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index 9586d56e8e8d..c7cc8edf108d 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,49 +16,64 @@ package android.server.search; +import com.android.internal.app.ResolverActivity; +import com.android.internal.R; + import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.os.Bundle; +import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** - * This class maintains the information about all searchable activities. + * This class maintains the information about all searchable activities. */ public class Searchables { + private static final String LOG_TAG = "Searchables"; + // static strings used for XML lookups, etc. - // TODO how should these be documented for the developer, in a more structured way than + // TODO how should these be documented for the developer, in a more structured way than // the current long wordy javadoc in SearchManager.java ? private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable"; private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*"; - + private Context mContext; - + private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; + private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null; 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. */ public Searchables (Context context) { mContext = context; } - + /** * Look up, or construct, based on the activity. - * - * The activities fall into three cases, based on meta-data found in + * + * The activities fall into three cases, based on meta-data found in * the manifest entry: * <ol> * <li>The activity itself implements search. This is indicated by the @@ -70,16 +85,16 @@ public class Searchables { * case the factory will "redirect" and return the searchable data.</li> * <li>No searchability data is provided. We return null here and other * code will insert the "default" (e.g. contacts) search. - * + * * TODO: cache the result in the map, and check the map first. * TODO: it might make sense to implement the searchable reference as * an application meta-data entry. This way we don't have to pepper each * and every activity. * TODO: can we skip the constructor step if it's a non-searchable? - * TODO: does it make sense to plug the default into a slot here for + * TODO: does it make sense to plug the default into a slot here for * automatic return? Probably not, but it's one way to do it. * - * @param activity The name of the current activity, or null if the + * @param activity The name of the current activity, or null if the * activity does not define any explicit searchable metadata. */ public SearchableInfo getSearchableInfo(ComponentName activity) { @@ -89,18 +104,18 @@ public class Searchables { result = mSearchablesMap.get(activity); if (result != null) return result; } - + // Step 2. See if the current activity references a searchable. // Note: Conceptually, this could be a while(true) loop, but there's - // no point in implementing reference chaining here and risking a loop. + // no point in implementing reference chaining here and risking a loop. // References must point directly to searchable activities. - + ActivityInfo ai = null; try { ai = mContext.getPackageManager(). getActivityInfo(activity, PackageManager.GET_META_DATA ); String refActivityName = null; - + // First look for activity-specific reference Bundle md = ai.metaData; if (md != null) { @@ -113,11 +128,11 @@ public class Searchables { refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); } } - + // Irrespective of source, if a reference was found, follow it. if (refActivityName != null) { - // An app or activity can declare that we should simply launch + // An app or activity can declare that we should simply launch // "system default search" if search is invoked. if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { return getDefaultSearchable(); @@ -143,95 +158,212 @@ public class Searchables { } catch (PackageManager.NameNotFoundException e) { // case 3: no metadata } - + // Step 3. None found. Return null. return null; - + } - + /** * Provides the system-default search activity, which you can use * whenever getSearchableInfo() returns null; - * + * * @return Returns the system-default search activity, null if never defined */ public synchronized SearchableInfo getDefaultSearchable() { return mDefaultSearchable; } - + public synchronized boolean isDefaultSearchable(SearchableInfo searchable) { return searchable == mDefaultSearchable; } - + /** - * Builds an entire list (suitable for display) of - * activities that are searchable, by iterating the entire set of - * ACTION_SEARCH intents. - * + * Builds an entire list (suitable for display) of + * activities that are searchable, by iterating the entire set of + * ACTION_SEARCH & ACTION_WEB_SEARCH intents. + * * Also clears the hash of all activities -> searches which will * refill as the user clicks "search". - * + * * This should only be done at startup and again if we know that the * list has changed. - * + * * TODO: every activity that provides a ACTION_SEARCH intent should * also provide searchability meta-data. There are a bunch of checks here * that, if data is not found, silently skip to the next activity. This * won't help a developer trying to figure out why their activity isn't * showing up in the list, but an exception here is too rough. I would * like to find a better notification mechanism. - * + * * TODO: sort the list somehow? UI choice. */ public void buildSearchableList() { - // These will become the new values at the end of the method - HashMap<ComponentName, SearchableInfo> newSearchablesMap + HashMap<ComponentName, SearchableInfo> newSearchablesMap = new HashMap<ComponentName, SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesList = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); + ArrayList<SearchableInfo> newSearchablesForWebSearchList + = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); - - // use intent resolver to generate list of ACTION_SEARCH receivers - List<ResolveInfo> infoList; + + // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. + List<ResolveInfo> searchList; final Intent intent = new Intent(Intent.ACTION_SEARCH); - infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); - + searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); + + List<ResolveInfo> webSearchInfoList; + final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); + webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); + // analyze each one, generate a Searchables record, and record - if (infoList != null) { - int count = infoList.size(); + if (searchList != null || webSearchInfoList != null) { + int search_count = (searchList == null ? 0 : searchList.size()); + int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); + int count = search_count + web_search_count; for (int ii = 0; ii < count; ii++) { // for each component, try to find metadata - ResolveInfo info = infoList.get(ii); + ResolveInfo info = (ii < search_count) + ? searchList.get(ii) + : webSearchInfoList.get(ii - search_count); ActivityInfo ai = info.activityInfo; - SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); - if (searchable != null) { - newSearchablesList.add(searchable); - newSearchablesMap.put(searchable.getSearchActivity(), searchable); - if (searchable.shouldIncludeInGlobalSearch()) { - newSearchablesInGlobalSearchList.add(searchable); + // Check first to avoid duplicate entries. + if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { + SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); + if (searchable != null) { + newSearchablesList.add(searchable); + newSearchablesMap.put(searchable.getSearchActivity(), searchable); + if (searchable.shouldIncludeInGlobalSearch()) { + newSearchablesInGlobalSearchList.add(searchable); + } } } } } - + + if (webSearchInfoList != null) { + for (int i = 0; i < webSearchInfoList.size(); ++i) { + ActivityInfo ai = webSearchInfoList.get(i).activityInfo; + ComponentName component = new ComponentName(ai.packageName, ai.name); + newSearchablesForWebSearchList.add(newSearchablesMap.get(component)); + } + } + // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); + if (newDefaultSearchable == null) { + Log.w(LOG_TAG, "No searchable info found for new default searchable activity " + + globalSearchActivity); + } + + // Find the default web search provider. + ComponentName webSearchActivity = getPreferredWebSearchActivity(); + SearchableInfo newDefaultSearchableForWebSearch = null; + if (webSearchActivity != null) { + newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity); + } + if (newDefaultSearchableForWebSearch == null) { + Log.w(LOG_TAG, "No searchable info found for new default web search activity " + + webSearchActivity); + } + // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; + mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; + mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; + } + + // Inform all listeners that the list of searchables has been updated. + mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED)); + } + + /** + * Checks if the given activity component is present in the system and if so makes it the + * preferred activity for handling ACTION_WEB_SEARCH. + * @param component Name of the component to check and set as preferred. + * @param action Intent action for which this activity is to be set as preferred. + * @return true if component was detected and set as preferred activity, false if not. + */ + private boolean setPreferredActivity(ComponentName component, String action) { + Log.d(LOG_TAG, "Checking component " + component); + PackageManager pm = mContext.getPackageManager(); + ActivityInfo ai; + try { + ai = pm.getActivityInfo(component, 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + // The code here to find the value for bestMatch is heavily inspired by the code + // in ResolverActivity where the preferred activity is set. + Intent intent = new Intent(action); + intent.addCategory(Intent.CATEGORY_DEFAULT); + List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0); + ComponentName set[] = new ComponentName[webSearchActivities.size()]; + int bestMatch = 0; + for (int i = 0; i < webSearchActivities.size(); ++i) { + ResolveInfo ri = webSearchActivities.get(i); + set[i] = new ComponentName(ri.activityInfo.packageName, + ri.activityInfo.name); + if (ri.match > bestMatch) bestMatch = ri.match; + } + + Log.d(LOG_TAG, "Setting preferred web search activity to " + component); + IntentFilter filter = new IntentFilter(action); + filter.addCategory(Intent.CATEGORY_DEFAULT); + pm.replacePreferredActivity(filter, bestMatch, set, component); + return true; + } + + public ComponentName getPreferredWebSearchActivity() { + // Check if we have a preferred web search activity. + Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + + if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) { + Log.d(LOG_TAG, "No preferred activity set for action web search."); + + // The components in the providers array are checked in the order of declaration so the + // first one has the highest priority. If the component exists in the system it is set + // as the preferred activity to handle intent action web search. + String[] preferredActivities = mContext.getResources().getStringArray( + com.android.internal.R.array.default_web_search_providers); + for (String componentName : preferredActivities) { + ComponentName component = ComponentName.unflattenFromString(componentName); + if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) { + 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; + return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); } - + /** * Returns the list of searchable activities. */ @@ -239,11 +371,33 @@ public class Searchables { ArrayList<SearchableInfo> result = new ArrayList<SearchableInfo>(mSearchablesList); return result; } - + /** * Returns a list of the searchable activities that can be included in global search. */ public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() { return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList); } + + /** + * Returns a list of the searchable activities that handle web searches. + */ + public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() { + return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList); + } + + /** + * Returns the default searchable activity for web searches. + */ + public synchronized SearchableInfo getDefaultSearchableForWebSearch() { + return mDefaultSearchableForWebSearch; + } + + /** + * Sets the default searchable activity for web searches. + */ + public synchronized void setDefaultWebSearch(ComponentName component) { + setPreferredActivity(component, Intent.ACTION_WEB_SEARCH); + buildSearchableList(); + } } diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl index 6ed32b502b42..2da2258b4ee1 100644 --- a/core/java/android/speech/IRecognitionListener.aidl +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -17,6 +17,7 @@ package android.speech; import android.os.Bundle; +import android.speech.RecognitionResult; /** * Listener for speech recognition events, used with RecognitionService. @@ -43,13 +44,17 @@ interface IRecognitionListener { /** Called after the user stops speaking. */ void onEndOfSpeech(); - /** A network or recognition error occurred. */ - void onError(in String error); + /** + * A network or recognition error occurred. The code is defined in + * {@link android.speech.RecognitionResult} + */ + void onError(in int error); /** - * Called when recognition transcripts are ready. - * results: an ordered list of the most likely transcripts (N-best list). - * @hide + * Called when recognition results are ready. + * @param results: an ordered list of the most likely results (N-best list). + * @param key: a key associated with the results. The same results can + * be retrieved asynchronously later using the key, if available. */ - void onResults(in List<String> results); + void onResults(in List<RecognitionResult> results, long key); } diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl index 36d12e9aae64..a18c380c9e70 100644 --- a/core/java/android/speech/IRecognitionService.aidl +++ b/core/java/android/speech/IRecognitionService.aidl @@ -18,6 +18,7 @@ package android.speech; import android.content.Intent; import android.speech.IRecognitionListener; +import android.speech.RecognitionResult; // A Service interface to speech recognition. Call startListening when // you want to begin capturing audio; RecognitionService will automatically @@ -29,6 +30,8 @@ interface IRecognitionService { // see RecognizerIntent.java for constants used to specify the intent. void startListening(in Intent recognizerIntent, in IRecognitionListener listener); + + List<RecognitionResult> getRecognitionResults(in long key); void cancel(); } diff --git a/core/java/android/speech/RecognitionResult.aidl b/core/java/android/speech/RecognitionResult.aidl new file mode 100644 index 000000000000..59e53ab86fd1 --- /dev/null +++ b/core/java/android/speech/RecognitionResult.aidl @@ -0,0 +1,19 @@ +/* + * 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.speech; + +parcelable RecognitionResult; diff --git a/core/java/android/speech/RecognitionResult.java b/core/java/android/speech/RecognitionResult.java new file mode 100644 index 000000000000..8d031fcd3273 --- /dev/null +++ b/core/java/android/speech/RecognitionResult.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * 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.speech; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * RecognitionResult is a passive object that stores a single recognized + * query and its search result. + * TODO: revisit and improve. May be we should have a separate result + * object for each type, and put them (type/value) in bundle? + * + * {@hide} + */ +public class RecognitionResult implements Parcelable { + /** + * Status of the recognize request. + */ + public static final int NETWORK_TIMEOUT = 1; // Network operation timed out. + public static final int NETWORK_ERROR = 2; // Other networkrelated errors. + public static final int AUDIO_ERROR = 3; // Audio recording error. + public static final int SERVER_ERROR = 4; // Server sends error status. + public static final int CLIENT_ERROR = 5; // Other client side errors. + public static final int SPEECH_TIMEOUT = 6; // No speech input + public static final int NO_MATCH = 7; // No recognition result matched. + public static final int SERVICE_BUSY = 8; // RecognitionService busy. + + /** + * Type of the recognition results. + */ + public static final int RAW_RECOGNITION_RESULT = 0; + public static final int WEB_SEARCH_RESULT = 1; + public static final int CONTACT_RESULT = 2; + + /** + * A factory method to create a raw RecognitionResult + * + * @param sentence the recognized text. + */ + public static RecognitionResult newRawRecognitionResult(String sentence) { + return new RecognitionResult(RAW_RECOGNITION_RESULT, sentence, null, null); + } + + /** + * A factory method to create RecognitionResult for contacts. + * + * @param contact the contact name. + * @param phoneType the phone type. + */ + public static RecognitionResult newContactResult(String contact, int phoneType) { + return new RecognitionResult(CONTACT_RESULT, contact, phoneType); + } + + /** + * A factory method to create a RecognitionResult for Web Search Query. + * + * @param query the query string. + * @param html the html page of the search result. + * @param url the url that performs the search with the query. + */ + public static RecognitionResult newWebResult(String query, String html, String url) { + return new RecognitionResult(WEB_SEARCH_RESULT, query, html, url); + } + + public static final Parcelable.Creator<RecognitionResult> CREATOR + = new Parcelable.Creator<RecognitionResult>() { + + public RecognitionResult createFromParcel(Parcel in) { + return new RecognitionResult(in); + } + + public RecognitionResult[] newArray(int size) { + return new RecognitionResult[size]; + } + }; + + /** + * Result type. + */ + public final int mResultType; + + /** + * The recognized string when mResultType is WEB_SEARCH_RESULT. + * The name of the contact when mResultType is CONTACT_RESULT. + */ + public final String mText; + + /** + * The HTML result page for the query. If this is null, then the + * application must use the url field to get the HTML result page. + */ + public final String mHtml; + + /** + * The url to get the result page for the query string. The + * application must use this url instead of performing the search + * with the query. + */ + public final String mUrl; + + /** Phone number type. This is valid only when mResultType == CONTACT_RESULT */ + public final int mPhoneType; + + private RecognitionResult(int type, String query, String html, String url) { + mResultType = type; + mText = query; + mHtml = html; + mUrl = url; + mPhoneType = -1; + } + + private RecognitionResult(int type, String query, int at) { + mResultType = type; + mText = query; + mPhoneType = at; + mHtml = null; + mUrl = null; + } + + private RecognitionResult(Parcel in) { + mResultType = in.readInt(); + mText = in.readString(); + mHtml= in.readString(); + mUrl= in.readString(); + mPhoneType = in.readInt(); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mResultType); + out.writeString(mText); + out.writeString(mHtml); + out.writeString(mUrl); + out.writeInt(mPhoneType); + } + + + @Override + public String toString() { + String resultType[] = { "RAW", "WEB", "CONTACT" }; + return "[type=" + resultType[mResultType] + + ", text=" + mText+ ", mUrl=" + mUrl + ", html=" + mHtml + "]"; + } + + public int describeContents() { + // no special description + return 0; + } +} diff --git a/core/java/android/speech/RecognitionServiceUtil.java b/core/java/android/speech/RecognitionServiceUtil.java index 650c0fd24881..a8c78684f6cd 100644 --- a/core/java/android/speech/RecognitionServiceUtil.java +++ b/core/java/android/speech/RecognitionServiceUtil.java @@ -21,6 +21,9 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.speech.RecognitionResult; +import android.util.Log; import java.util.List; @@ -56,6 +59,11 @@ public class RecognitionServiceUtil { public static final Intent sDefaultIntent = new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + // Recognize request parameters + public static final String USE_LOCATION = "useLocation"; + public static final String CONTACT_AUTH_TOKEN = "contactAuthToken"; + + // Bundles public static final String NOISE_LEVEL = "NoiseLevel"; public static final String SIGNAL_NOISE_RATIO = "SignalNoiseRatio"; @@ -72,8 +80,8 @@ public class RecognitionServiceUtil { public void onRmsChanged(float rmsdB) {} public void onBufferReceived(byte[] buf) {} public void onEndOfSpeech() {} - public void onError(String error) {} - public void onResults(List<String> results) {} + public void onError(int error) {} + public void onResults(List<RecognitionResult> results, long key) {} } /** diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl new file mode 100755 index 000000000000..c9a6180d4e69 --- /dev/null +++ b/core/java/android/speech/tts/ITts.aidl @@ -0,0 +1,63 @@ +/*
+ * 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.speech.tts;
+
+import android.speech.tts.ITtsCallback;
+
+import android.content.Intent;
+
+/**
+ * AIDL for the TTS Service
+ * ITts.java is autogenerated from this.
+ *
+ * {@hide}
+ */
+interface ITts {
+ int setSpeechRate(in int speechRate);
+
+ int setPitch(in int pitch);
+
+ int speak(in String text, in int queueMode, in String[] params);
+
+ boolean isSpeaking();
+
+ int stop();
+
+ void addSpeech(in String text, in String packageName, in int resId);
+
+ void addSpeechFile(in String text, in String filename);
+
+ String[] getLanguage();
+
+ int isLanguageAvailable(in String language, in String country, in String variant);
+
+ int setLanguage(in String language, in String country, in String variant);
+
+ boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+
+ int playEarcon(in String earcon, in int queueMode, in String[] params);
+
+ void addEarcon(in String earcon, in String packageName, in int resId);
+
+ void addEarconFile(in String earcon, in String filename);
+
+ void registerCallback(ITtsCallback cb);
+
+ void unregisterCallback(ITtsCallback cb);
+
+ int playSilence(in long duration, in int queueMode, in String[] params);
+}
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITtsCallback.aidl new file mode 100755 index 000000000000..48ed73e02165 --- /dev/null +++ b/core/java/android/speech/tts/ITtsCallback.aidl @@ -0,0 +1,27 @@ +/* + * 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.speech.tts; + +/** + * AIDL for the callback from the TTS Service + * ITtsCallback.java is autogenerated from this. + * + * {@hide} + */ +oneway interface ITtsCallback { + void markReached(String mark); +} diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java new file mode 100644 index 000000000000..616b3f113ffb --- /dev/null +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * 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.speech.tts; + +import android.speech.tts.ITts; +import android.speech.tts.ITtsCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Locale; + +/** + * + * Synthesizes speech from text for immediate playback or to create a sound file. + * + */ +//TODO complete javadoc + add links to constants +public class TextToSpeech { + + /** + * Denotes a successful operation. + */ + public static final int TTS_SUCCESS = 0; + /** + * Denotes a generic operation failure. + */ + public static final int TTS_ERROR = -1; + + /** + * Queue mode where all entries in the playback queue (media to be played + * and text to be synthesized) are dropped and replaced by the new entry. + */ + public static final int TTS_QUEUE_FLUSH = 0; + /** + * Queue mode where the new entry is added at the end of the playback queue. + */ + 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. + * + * The InitListener must implement the onInit function. onInit is passed a + * status code indicating the result of the TTS initialization. + */ + public interface OnInitListener { + public void onInit(int status); + } + + /** + * Internal constants for the TTS functionality + * + * {@hide} + */ + public class Engine { + // default values for a TTS engine when settings are not found in the provider + public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x + public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x + public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false + public static final String FALLBACK_TTS_DEFAULT_LANG = "eng"; + public static final String FALLBACK_TTS_DEFAULT_COUNTRY = ""; + public static final String FALLBACK_TTS_DEFAULT_VARIANT = ""; + + // return codes for a TTS engine's check data activity + public static final int CHECK_VOICE_DATA_PASS = 1; + public static final int CHECK_VOICE_DATA_FAIL = 0; + 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; + + // return codes for a TTS engine's check data activity + public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; + public static final String VOICE_DATA_FILES = "dataFiles"; + public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo"; + + // 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; + } + + /** + * Connection needed for the TTS. + */ + private ServiceConnection mServiceConnection; + + private ITts mITts = null; + private Context mContext = null; + private OnInitListener mInitListener = null; + private boolean mStarted = false; + private final Object mStartLock = new Object(); + /** + * Used to store the cached parameters sent along with each synthesis request to the + * TTS service. + */ + private String[] mCachedParams; + + /** + * The constructor for the TTS. + * + * @param context + * The context + * @param listener + * The InitListener that will be called when the TTS has + * initialized successfully. + */ + public TextToSpeech(Context context, OnInitListener listener) { + mContext = context; + mInitListener = listener; + + 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; + + mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = + String.valueOf(Engine.FALLBACK_TTS_DEFAULT_RATE); + // initialize the language cached parameters with the current Locale + Locale defaultLoc = Locale.getDefault(); + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language(); + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country(); + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant(); + + initTts(); + } + + + private void initTts() { + mStarted = false; + + // Initialize the TTS, run the callback after the binding is successful + mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized(mStartLock) { + mITts = ITts.Stub.asInterface(service); + mStarted = true; + if (mInitListener != null) { + // TODO manage failures and missing resources + mInitListener.onInit(TTS_SUCCESS); + } + } + } + + public void onServiceDisconnected(ComponentName name) { + synchronized(mStartLock) { + mITts = null; + mInitListener = null; + mStarted = false; + } + } + }; + + Intent intent = new Intent("android.intent.action.START_TTS_SERVICE"); + intent.addCategory("android.intent.category.TTS"); + mContext.bindService(intent, mServiceConnection, + Context.BIND_AUTO_CREATE); + // TODO handle case where the binding works (should always work) but + // the plugin fails + } + + + /** + * Shuts down the TTS. It is good practice to call this in the onDestroy + * method of the Activity that is using the TTS so that the TTS is stopped + * cleanly. + */ + public void shutdown() { + try { + mContext.unbindService(mServiceConnection); + } catch (IllegalArgumentException e) { + // Do nothing and fail silently since an error here indicates that + // binding never succeeded in the first place. + } + } + + + /** + * Adds a mapping between a string of text and a sound resource in a + * package. + * + * @see #TTS.speak(String text, int queueMode, String[] params) + * + * @param text + * Example: <b><code>"south_south_east"</code></b><br/> + * + * @param packagename + * Pass the packagename of the application that contains the + * resource. If the resource is in your own application (this is + * the most common case), then put the packagename of your + * application here.<br/> + * Example: <b>"com.google.marvin.compass"</b><br/> + * The packagename can be found in the AndroidManifest.xml of + * your application. + * <p> + * <code><manifest xmlns:android="..." + * package="<b>com.google.marvin.compass</b>"></code> + * </p> + * + * @param resourceId + * Example: <b><code>R.raw.south_south_east</code></b> + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int addSpeech(String text, String packagename, int resourceId) { + synchronized(mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + mITts.addSpeech(text, packagename, resourceId); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + + + /** + * Adds a mapping between a string of text and a sound file. Using this, it + * is possible to add custom pronounciations for text. + * + * @param text + * The string of text + * @param filename + * The full path to the sound file (for example: + * "/sdcard/mysounds/hello.wav") + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int addSpeech(String text, String filename) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + mITts.addSpeechFile(text, filename); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + + + /** + * Speaks the string using the specified queuing strategy and speech + * parameters. Note that the speech parameters are not universally supported + * by all engines and will be treated as a hint. The TTS library will try to + * fulfill these parameters as much as possible, but there is no guarantee + * that the voice used will have the properties specified. + * + * @param text + * The string of 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. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int speak(String text, int queueMode, HashMap<String,String> params) + { + synchronized (mStartLock) { + int result = TTS_ERROR; + Log.i("TTS received: ", text); + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + result = mITts.speak(text, queueMode, mCachedParams); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Plays the earcon using the specified queueing mode and parameters. + * + * @param earcon + * The earcon that should be played + * @param queueMode + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * @param params + * The hashmap of parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int playEarcon(String earcon, int queueMode, + HashMap<String,String> params) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing null for the moment + result = mITts.playEarcon(earcon, queueMode, null); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + /** + * Plays silence for the specified amount of time using the specified + * queue mode. + * + * @param durationInMs + * A long that indicates how long the silence should last. + * @param queueMode + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int playSilence(long durationInMs, int queueMode) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + result = mITts.playSilence(durationInMs, queueMode, mCachedParams); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Returns whether or not the TTS is busy speaking. + * + * @return Whether or not the TTS is busy speaking. + */ + public boolean isSpeaking() { + synchronized (mStartLock) { + if (!mStarted) { + return false; + } + try { + return mITts.isSpeaking(); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return false; + } + } + + + /** + * Stops speech from the TTS. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int stop() { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + result = mITts.stop(); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Sets the speech rate for the TTS engine. + * + * Note that the speech rate is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * speech rate, but there is no guarantee. + * This has no effect on any pre-recorded speech. + * + * @param speechRate + * The speech rate for the TTS engine. 1 is the normal speed, + * lower values slow down the speech (0.5 is half the normal speech rate), + * greater values accelerate it (2 is twice the normal speech rate). + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int setSpeechRate(float speechRate) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + if (speechRate > 0) { + int rate = (int)(speechRate*100); + mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = String.valueOf(rate); + result = mITts.setSpeechRate(rate); + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * 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. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int setPitch(float pitch) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + if (pitch > 0) { + result = mITts.setPitch((int)(pitch*100)); + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Sets the language for the TTS engine. + * + * Note that the language is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * language as represented by the Locale, but there is no guarantee. + * + * @param loc + * The locale describing the language to be used. + * + * @return Code indicating the support status for the locale. See the TTS_LANG_ codes. + */ + public int setLanguage(Locale loc) { + synchronized (mStartLock) { + int result = TTS_LANG_NOT_SUPPORTED; + if (!mStarted) { + return result; + } + try { + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language(); + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country(); + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = loc.getVariant(); + result = mITts.setLanguage(mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1], + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1], + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] ); + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Returns a Locale instance describing the language currently being used by the TTS engine. + * @return language, country (if any) and variant (if any) used by the engine stored in a Locale + * instance, or null is the TTS engine has failed. + */ + public Locale getLanguage() { + synchronized (mStartLock) { + if (!mStarted) { + return null; + } + try { + String[] locStrings = mITts.getLanguage(); + if (locStrings.length == 3) { + return new Locale(locStrings[0], locStrings[1], locStrings[2]); + } else { + return null; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return null; + } + } + + /** + * 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) { + synchronized (mStartLock) { + int result = TTS_LANG_NOT_SUPPORTED; + if (!mStarted) { + return result; + } + try { + result = mITts.isLanguageAvailable(loc.getISO3Language(), + loc.getISO3Country(), loc.getVariant()); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Synthesizes the given text to a file using the specified parameters. + * + * @param text + * The String of text that should be synthesized + * @param params + * A hashmap of parameters. + * @param filename + * The string that gives the full output filename; it should be + * something like "/sdcard/myappsounds/mysound.wav". + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int synthesizeToFile(String text, HashMap<String,String> params, + String filename) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing null for the moment + if (mITts.synthesizeToFile(text, null, filename)){ + result = TTS_SUCCESS; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + +} diff --git a/core/java/android/syncml/pim/PropertyNode.java b/core/java/android/syncml/pim/PropertyNode.java index cc5249905efe..983ecb8a37f2 100644 --- a/core/java/android/syncml/pim/PropertyNode.java +++ b/core/java/android/syncml/pim/PropertyNode.java @@ -17,12 +17,16 @@ package android.syncml.pim; import android.content.ContentValues; -import android.util.Log; +import org.apache.commons.codec.binary.Base64; + +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Map.Entry; +import java.util.regex.Pattern; public class PropertyNode { @@ -52,7 +56,9 @@ public class PropertyNode { public Set<String> propGroupSet; public PropertyNode() { + propName = ""; propValue = ""; + propValue_vector = new ArrayList<String>(); paramMap = new ContentValues(); paramMap_TYPE = new HashSet<String>(); propGroupSet = new HashSet<String>(); @@ -62,13 +68,21 @@ public class PropertyNode { String propName, String propValue, List<String> propValue_vector, byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, Set<String> propGroupSet) { - this.propName = propName; + if (propName != null) { + this.propName = propName; + } else { + this.propName = ""; + } if (propValue != null) { this.propValue = propValue; } else { this.propValue = ""; } - this.propValue_vector = propValue_vector; + if (propValue_vector != null) { + this.propValue_vector = propValue_vector; + } else { + this.propValue_vector = new ArrayList<String>(); + } this.propValue_bytes = propValue_bytes; if (paramMap != null) { this.paramMap = paramMap; @@ -117,17 +131,9 @@ public class PropertyNode { // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector // is 1, the encoded value is stored in propValue, so we do not have to // check it. - if (propValue_vector != null) { - // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); - return (propValue_vector.equals(node.propValue_vector) || - (propValue_vector.size() == 1)); - } else if (node.propValue_vector != null) { - // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); - return (node.propValue_vector.equals(propValue_vector) || - (node.propValue_vector.size() == 1)); - } else { - return true; - } + return (propValue_vector.equals(node.propValue_vector) || + propValue_vector.size() == 1 || + node.propValue_vector.size() == 1); } } @@ -154,4 +160,164 @@ public class PropertyNode { builder.append(propValue); return builder.toString(); } + + /** + * Encode this object into a string which can be decoded. + */ + public String encode() { + // PropertyNode#toString() is for reading, not for parsing in the future. + // We construct appropriate String here. + StringBuilder builder = new StringBuilder(); + if (propName.length() > 0) { + builder.append("propName:["); + builder.append(propName); + builder.append("],"); + } + int size = propGroupSet.size(); + if (size > 0) { + Set<String> set = propGroupSet; + builder.append("propGroup:["); + int i = 0; + for (String group : set) { + // We do not need to double quote groups. + // group = 1*(ALPHA / DIGIT / "-") + builder.append(group); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { + ContentValues values = paramMap; + builder.append("paramMap:["); + size = paramMap.size(); + int i = 0; + for (Entry<String, Object> entry : values.valueSet()) { + // Assuming param-key does not contain NON-ASCII nor symbols. + // + // According to vCard 3.0: + // param-name = iana-token / x-name + builder.append(entry.getKey()); + + // param-value may contain any value including NON-ASCIIs. + // We use the following replacing rule. + // \ -> \\ + // , -> \, + // In String#replaceAll(), "\\\\" means a single backslash. + builder.append("="); + builder.append(entry.getValue().toString() + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + i++; + } + + Set<String> set = paramMap_TYPE; + size = paramMap_TYPE.size(); + if (i > 0 && size > 0) { + builder.append(","); + } + i = 0; + for (String type : set) { + builder.append("TYPE="); + builder.append(type + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + size = propValue_vector.size(); + if (size > 0) { + builder.append("propValue:["); + List<String> list = propValue_vector; + for (int i = 0; i < size; i++) { + builder.append(list.get(i) + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + } + builder.append("],"); + } + + return builder.toString(); + } + + public static PropertyNode decode(String encodedString) { + PropertyNode propertyNode = new PropertyNode(); + String trimed = encodedString.trim(); + if (trimed.length() == 0) { + return propertyNode; + } + String[] elems = trimed.split("],"); + + for (String elem : elems) { + int index = elem.indexOf('['); + String name = elem.substring(0, index - 1); + Pattern pattern = Pattern.compile("(?<!\\\\),"); + String[] values = pattern.split(elem.substring(index + 1), -1); + if (name.equals("propName")) { + propertyNode.propName = values[0]; + } else if (name.equals("propGroupSet")) { + for (String value : values) { + propertyNode.propGroupSet.add(value); + } + } else if (name.equals("paramMap")) { + ContentValues paramMap = propertyNode.paramMap; + Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE; + for (String value : values) { + String[] tmp = value.split("=", 2); + String mapKey = tmp[0]; + // \, -> , + // \\ -> \ + // In String#replaceAll(), "\\\\" means a single backslash. + String mapValue = + tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); + if (mapKey.equalsIgnoreCase("TYPE")) { + paramMap_TYPE.add(mapValue); + } else { + paramMap.put(mapKey, mapValue); + } + } + } else if (name.equals("propValue")) { + StringBuilder builder = new StringBuilder(); + List<String> list = propertyNode.propValue_vector; + int length = values.length; + for (int i = 0; i < length; i++) { + String normValue = values[i] + .replaceAll("\\\\,", ",") + .replaceAll("\\\\\\\\", "\\\\"); + list.add(normValue); + builder.append(normValue); + if (i < length - 1) { + builder.append(";"); + } + } + propertyNode.propValue = builder.toString(); + } + } + + // At this time, QUOTED-PRINTABLE is already decoded to Java String. + // We just need to decode BASE64 String to binary. + String encoding = propertyNode.paramMap.getAsString("ENCODING"); + if (encoding != null && + (encoding.equalsIgnoreCase("BASE64") || + encoding.equalsIgnoreCase("B"))) { + propertyNode.propValue_bytes = + Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); + } + + return propertyNode; + } } diff --git a/core/java/android/syncml/pim/VBuilderCollection.java b/core/java/android/syncml/pim/VBuilderCollection.java new file mode 100644 index 000000000000..f09c1c49445c --- /dev/null +++ b/core/java/android/syncml/pim/VBuilderCollection.java @@ -0,0 +1,100 @@ +/* + * 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.syncml.pim; + +import java.util.Collection; +import java.util.List; + +public class VBuilderCollection implements VBuilder { + + private final Collection<VBuilder> mVBuilderCollection; + + public VBuilderCollection(Collection<VBuilder> vBuilderCollection) { + mVBuilderCollection = vBuilderCollection; + } + + public Collection<VBuilder> getVBuilderCollection() { + return mVBuilderCollection; + } + + public void start() { + for (VBuilder builder : mVBuilderCollection) { + builder.start(); + } + } + + public void end() { + for (VBuilder builder : mVBuilderCollection) { + builder.end(); + } + } + + public void startRecord(String type) { + for (VBuilder builder : mVBuilderCollection) { + builder.startRecord(type); + } + } + + public void endRecord() { + for (VBuilder builder : mVBuilderCollection) { + builder.endRecord(); + } + } + + public void startProperty() { + for (VBuilder builder : mVBuilderCollection) { + builder.startProperty(); + } + } + + + public void endProperty() { + for (VBuilder builder : mVBuilderCollection) { + builder.endProperty(); + } + } + + public void propertyGroup(String group) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyGroup(group); + } + } + + public void propertyName(String name) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyName(name); + } + } + + public void propertyParamType(String type) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyParamType(type); + } + } + + public void propertyParamValue(String value) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyParamValue(value); + } + } + + public void propertyValues(List<String> values) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyValues(values); + } + } +} diff --git a/core/java/android/syncml/pim/VDataBuilder.java b/core/java/android/syncml/pim/VDataBuilder.java index 8c67cf5bfa03..f6e5b653125f 100644 --- a/core/java/android/syncml/pim/VDataBuilder.java +++ b/core/java/android/syncml/pim/VDataBuilder.java @@ -17,8 +17,10 @@ package android.syncml.pim; import android.content.ContentValues; +import android.util.CharsetUtils; import android.util.Log; +import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.net.QuotedPrintableCodec; @@ -26,9 +28,7 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Vector; /** * Store the parse result to custom datastruct: VNode, PropertyNode @@ -38,7 +38,13 @@ import java.util.Vector; */ public class VDataBuilder implements VBuilder { static private String LOG_TAG = "VDATABuilder"; - + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String DEFAULT_CHARSET = "UTF-8"; + /** type=VNode */ public List<VNode> vNodeList = new ArrayList<VNode>(); private int mNodeListPos = 0; @@ -47,34 +53,74 @@ public class VDataBuilder implements VBuilder { private String mCurrentParamType; /** - * Assumes that each String can be encoded into byte array using this encoding. + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. */ - private String mCharset; + private String mTargetCharset; private boolean mStrictLineBreakParsing; public VDataBuilder() { - mCharset = "ISO-8859-1"; - mStrictLineBreakParsing = false; + this(VParser.DEFAULT_CHARSET, DEFAULT_CHARSET, false); } - public VDataBuilder(String encoding, boolean strictLineBreakParsing) { - mCharset = encoding; - mStrictLineBreakParsing = strictLineBreakParsing; + public VDataBuilder(String charset, boolean strictLineBreakParsing) { + this(null, charset, strictLineBreakParsing); } + /** + * @hide sourceCharset is temporal. + */ + public VDataBuilder(String sourceCharset, String targetCharset, + boolean strictLineBreakParsing) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VParser.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = DEFAULT_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + } + public void start() { } public void end() { } + // Note: I guess that this code assumes the Record may nest like this: + // START:VPOS + // ... + // START:VPOS2 + // ... + // END:VPOS2 + // ... + // END:VPOS + // + // However the following code has a bug. + // When error occurs after calling startRecord(), the entry which is probably + // the cause of the error remains to be in vNodeList, while endRecord() is not called. + // + // I leave this code as is since I'm not familiar with vcalendar specification. + // But I believe we should refactor this code in the future. + // Until this, the last entry has to be removed when some error occurs. public void startRecord(String type) { + VNode vnode = new VNode(); vnode.parseStatus = 1; vnode.VName = type; + // I feel this should be done in endRecord(), but it cannot be done because of + // the reason above. vNodeList.add(vnode); - mNodeListPos = vNodeList.size()-1; + mNodeListPos = vNodeList.size() - 1; mCurrentVNode = vNodeList.get(mNodeListPos); } @@ -90,15 +136,14 @@ public class VDataBuilder implements VBuilder { } public void startProperty() { - // System.out.println("+ startProperty. "); + mCurrentPropNode = new PropertyNode(); } public void endProperty() { - // System.out.println("- endProperty. "); + mCurrentVNode.propList.add(mCurrentPropNode); } public void propertyName(String name) { - mCurrentPropNode = new PropertyNode(); mCurrentPropNode.propName = name; } @@ -122,139 +167,145 @@ public class VDataBuilder implements VBuilder { mCurrentParamType = null; } - private String encodeString(String originalString, String targetEncoding) { - Charset charset = Charset.forName(mCharset); + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); ByteBuffer byteBuffer = charset.encode(originalString); // byteBuffer.array() "may" return byte array which is larger than // byteBuffer.remaining(). Here, we keep on the safe side. byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); try { - return new String(bytes, targetEncoding); + return new String(bytes, targetCharset); } catch (UnsupportedEncodingException e) { - return null; + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); } } - public void propertyValues(List<String> values) { - ContentValues paramMap = mCurrentPropNode.paramMap; - - String charsetString = paramMap.getAsString("CHARSET"); - - boolean setupParamValues = false; - //decode value string to propValue_bytes - if (paramMap.containsKey("ENCODING")) { - String encoding = paramMap.getAsString("ENCODING"); - if (encoding.equalsIgnoreCase("BASE64") || - encoding.equalsIgnoreCase("B")) { - if (values.size() > 1) { - Log.e(LOG_TAG, - ("BASE64 encoding is used while " + - "there are multiple values (" + values.size())); - } + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + // Assume BASE64 is used only when the number of values is 1. mCurrentPropNode.propValue_bytes = - Base64.decodeBase64(values.get(0). - replaceAll(" ","").replaceAll("\t",""). - replaceAll("\r\n",""). - getBytes()); - } - - if(encoding.equalsIgnoreCase("QUOTED-PRINTABLE")){ - // if CHARSET is defined, we translate each String into the Charset. - List<String> tmpValues = new ArrayList<String>(); - Vector<byte[]> byteVector = new Vector<byte[]>(); - int size = 0; - try{ - for (String value : values) { - String quotedPrintable = value - .replaceAll("= ", " ").replaceAll("=\t", "\t"); - String[] lines; - if (mStrictLineBreakParsing) { - lines = quotedPrintable.split("\r\n"); - } else { - lines = quotedPrintable - .replace("\r\n", "\n").replace("\r", "\n").split("\n"); - } - StringBuilder builder = new StringBuilder(); - for (String line : lines) { - if (line.endsWith("=")) { - line = line.substring(0, line.length() - 1); - } - builder.append(line); - } - byte[] bytes = QuotedPrintableCodec.decodeQuotedPrintable( - builder.toString().getBytes()); - if (charsetString != null) { - try { - tmpValues.add(new String(bytes, charsetString)); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); - tmpValues.add(new String(bytes)); + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + String quotedPrintable = value + .replaceAll("= ", " ").replaceAll("=\t", "\t"); + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + StringBuilder builder = new StringBuilder(); + int length = quotedPrintable.length(); + ArrayList<String> list = new ArrayList<String>(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } } } else { - tmpValues.add(new String(bytes)); - } - byteVector.add(bytes); - size += bytes.length; - } // for (String value : values) { - mCurrentPropNode.propValue_vector = tmpValues; - mCurrentPropNode.propValue = listToString(tmpValues); - - mCurrentPropNode.propValue_bytes = new byte[size]; - - { - byte[] tmpBytes = mCurrentPropNode.propValue_bytes; - int index = 0; - for (byte[] bytes : byteVector) { - int length = bytes.length; - for (int i = 0; i < length; i++, index++) { - tmpBytes[index] = bytes[i]; - } + builder.append(ch); } } - setupParamValues = true; - } catch(Exception e) { - Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); } - } // QUOTED-PRINTABLE - } // ENCODING - - if (!setupParamValues) { - // if CHARSET is defined, we translate each String into the Charset. - if (charsetString != null) { - List<String> tmpValues = new ArrayList<String>(); - for (String value : values) { - String result = encodeString(value, charsetString); - if (result != null) { - tmpValues.add(result); - } else { - Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); - tmpValues.add(value); + StringBuilder builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); } - values = tmpValues; } - - mCurrentPropNode.propValue_vector = values; - mCurrentPropNode.propValue = listToString(values); + // Unknown encoding. Fall back to default. } - mCurrentVNode.propList.add(mCurrentPropNode); + return encodeString(value, targetCharset); } - - private String listToString(Collection<String> list){ - StringBuilder typeListB = new StringBuilder(); - for (String type : list) { - typeListB.append(type).append(";"); + + public void propertyValues(List<String> values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); } - int len = typeListB.length(); - if (len > 0 && typeListB.charAt(len - 1) == ';') { - return typeListB.substring(0, len - 1); + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + private String listToString(List<String> list){ + int size = list.size(); + if (size > 1) { + StringBuilder typeListB = new StringBuilder(); + for (String type : list) { + typeListB.append(type).append(";"); + } + int len = typeListB.length(); + if (len > 0 && typeListB.charAt(len - 1) == ';') { + return typeListB.substring(0, len - 1); + } + return typeListB.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; } - return typeListB.toString(); } public String getResult(){ return null; } } - diff --git a/core/java/android/syncml/pim/VParser.java b/core/java/android/syncml/pim/VParser.java index df93f38e9e38..57c5f7a5cfd3 100644 --- a/core/java/android/syncml/pim/VParser.java +++ b/core/java/android/syncml/pim/VParser.java @@ -26,6 +26,9 @@ import java.io.UnsupportedEncodingException; * */ abstract public class VParser { + // Assume that "iso-8859-1" is able to map "all" 8bit characters to some unicode and + // decode the unicode to the original charset. If not, this setting will cause some bug. + public static String DEFAULT_CHARSET = "iso-8859-1"; /** * The buffer used to store input stream @@ -96,6 +99,20 @@ abstract public class VParser { } /** + * Parse the given stream with the default encoding. + * + * @param is + * The source to parse. + * @param builder + * The v builder which used to construct data. + * @return Return true for success, otherwise false. + * @throws IOException + */ + public boolean parse(InputStream is, VBuilder builder) throws IOException { + return parse(is, DEFAULT_CHARSET, builder); + } + + /** * Copy the content of input stream and filter the "folding" */ protected void setInputStream(InputStream is, String encoding) diff --git a/core/java/android/syncml/pim/vcard/ContactStruct.java b/core/java/android/syncml/pim/vcard/ContactStruct.java index 8d9b7fab24fb..ecd719da069d 100644 --- a/core/java/android/syncml/pim/vcard/ContactStruct.java +++ b/core/java/android/syncml/pim/vcard/ContactStruct.java @@ -16,45 +16,103 @@ package android.syncml.pim.vcard; -import java.util.List; +import android.content.AbstractSyncableContentProvider; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.net.Uri; +import android.provider.Contacts; +import android.provider.Contacts.ContactMethods; +import android.provider.Contacts.Extensions; +import android.provider.Contacts.GroupMembership; +import android.provider.Contacts.Organizations; +import android.provider.Contacts.People; +import android.provider.Contacts.Phones; +import android.provider.Contacts.Photos; +import android.syncml.pim.PropertyNode; +import android.syncml.pim.VNode; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Log; + import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; /** - * The parameter class of VCardCreator. + * The parameter class of VCardComposer. * This class standy by the person-contact in * Android system, we must use this class instance as parameter to transmit to - * VCardCreator so that create vCard string. + * VCardComposer so that create vCard string. */ // TODO: rename the class name, next step public class ContactStruct { - public String company; + private static final String LOG_TAG = "ContactStruct"; + + // Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and + // space should be added between each element while it should not be in Japanese. + // But unfortunately, we currently do not have the data and are not sure whether we should + // support European version of name ordering. + // + // TODO: Implement the logic described above if we really need European version of + // phonetic name handling. Also, adding the appropriate test case of vCard would be + // highly appreciated. + public static final int NAME_ORDER_TYPE_ENGLISH = 0; + public static final int NAME_ORDER_TYPE_JAPANESE = 1; + /** MUST exist */ public String name; + public String phoneticName; /** maybe folding */ - public String notes; + public List<String> notes = new ArrayList<String>(); /** maybe folding */ public String title; /** binary bytes of pic. */ public byte[] photoBytes; - /** mime_type col of images table */ + /** The type of Photo (e.g. JPEG, BMP, etc.) */ public String photoType; /** Only for GET. Use addPhoneList() to PUT. */ public List<PhoneData> phoneList; /** Only for GET. Use addContactmethodList() to PUT. */ public List<ContactMethod> contactmethodList; + /** Only for GET. Use addOrgList() to PUT. */ + public List<OrganizationData> organizationList; + /** Only for GET. Use addExtension() to PUT */ + public Map<String, List<String>> extensionMap; - public static class PhoneData{ + // Use organizationList instead when handling ORG. + @Deprecated + public String company; + + public static class PhoneData { + public int type; /** maybe folding */ public String data; - public String type; public String label; + public boolean isPrimary; } - public static class ContactMethod{ - public String kind; - public String type; + public static class ContactMethod { + // Contacts.KIND_EMAIL, Contacts.KIND_POSTAL + public int kind; + // e.g. Contacts.ContactMethods.TYPE_HOME, Contacts.PhoneColumns.TYPE_HOME + // If type == Contacts.PhoneColumns.TYPE_CUSTOM, label is used. + public int type; public String data; + // Used only when TYPE is TYPE_CUSTOM. public String label; + public boolean isPrimary; + } + + public static class OrganizationData { + public int type; + public String companyName; + public String positionName; + public boolean isPrimary; } /** @@ -63,29 +121,858 @@ public class ContactStruct { * @param type type col of content://contacts/phones * @param label lable col of content://contacts/phones */ - public void addPhone(String data, String type, String label){ - if(phoneList == null) + public void addPhone(int type, String data, String label, boolean isPrimary){ + if (phoneList == null) { phoneList = new ArrayList<PhoneData>(); - PhoneData st = new PhoneData(); - st.data = data; - st.type = type; - st.label = label; - phoneList.add(st); + } + PhoneData phoneData = new PhoneData(); + phoneData.type = type; + + StringBuilder builder = new StringBuilder(); + String trimed = data.trim(); + int length = trimed.length(); + for (int i = 0; i < length; i++) { + char ch = trimed.charAt(i); + if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) { + builder.append(ch); + } + } + phoneData.data = PhoneNumberUtils.formatNumber(builder.toString()); + phoneData.label = label; + phoneData.isPrimary = isPrimary; + phoneList.add(phoneData); } + /** * Add a contactmethod info to contactmethodList. - * @param data contact data + * @param kind integer value defined in Contacts.java + * (e.g. Contacts.KIND_EMAIL) * @param type type col of content://contacts/contact_methods + * @param data contact data + * @param label extra string used only when kind is Contacts.KIND_CUSTOM. */ - public void addContactmethod(String kind, String data, String type, - String label){ - if(contactmethodList == null) + public void addContactmethod(int kind, int type, String data, + String label, boolean isPrimary){ + if (contactmethodList == null) { contactmethodList = new ArrayList<ContactMethod>(); - ContactMethod st = new ContactMethod(); - st.kind = kind; - st.data = data; - st.type = type; - st.label = label; - contactmethodList.add(st); + } + ContactMethod contactMethod = new ContactMethod(); + contactMethod.kind = kind; + contactMethod.type = type; + contactMethod.data = data; + contactMethod.label = label; + contactMethod.isPrimary = isPrimary; + contactmethodList.add(contactMethod); + } + + /** + * Add a Organization info to organizationList. + */ + public void addOrganization(int type, String companyName, String positionName, + boolean isPrimary) { + if (organizationList == null) { + organizationList = new ArrayList<OrganizationData>(); + } + OrganizationData organizationData = new OrganizationData(); + organizationData.type = type; + organizationData.companyName = companyName; + organizationData.positionName = positionName; + organizationData.isPrimary = isPrimary; + organizationList.add(organizationData); + } + + /** + * Set "position" value to the appropriate data. If there's more than one + * OrganizationData objects, the value is set to the last one. If there's no + * OrganizationData object, a new OrganizationData is created, whose company name is + * empty. + * + * TODO: incomplete logic. fix this: + * + * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not + * know how to handle it in general cases... + * ---- + * TITLE:Software Engineer + * ORG:Google + * ---- + */ + public void setPosition(String positionValue) { + if (organizationList == null) { + organizationList = new ArrayList<OrganizationData>(); + } + int size = organizationList.size(); + if (size == 0) { + addOrganization(Contacts.OrganizationColumns.TYPE_OTHER, "", null, false); + size = 1; + } + OrganizationData lastData = organizationList.get(size - 1); + lastData.positionName = positionValue; + } + + public void addExtension(PropertyNode propertyNode) { + if (propertyNode.propValue.length() == 0) { + return; + } + // Now store the string into extensionMap. + List<String> list; + String name = propertyNode.propName; + if (extensionMap == null) { + extensionMap = new HashMap<String, List<String>>(); + } + if (!extensionMap.containsKey(name)){ + list = new ArrayList<String>(); + extensionMap.put(name, list); + } else { + list = extensionMap.get(name); + } + + list.add(propertyNode.encode()); + } + + private static String getNameFromNProperty(List<String> elems, int nameOrderType) { + // Family, Given, Middle, Prefix, Suffix. (1 - 5) + int size = elems.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + // Prefix + if (size > 3 && elems.get(3).length() > 0) { + builder.append(elems.get(3)); + builderIsEmpty = false; + } + String first, second; + if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) { + first = elems.get(0); + second = elems.get(1); + } else { + first = elems.get(1); + second = elems.get(0); + } + if (first.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(first); + builderIsEmpty = false; + } + // Middle name + if (size > 2 && elems.get(2).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(2)); + builderIsEmpty = false; + } + if (second.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(second); + builderIsEmpty = false; + } + // Suffix + if (size > 4 && elems.get(4).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(4)); + builderIsEmpty = false; + } + return builder.toString(); + } else if (size == 1) { + return elems.get(0); + } else { + return ""; + } + } + + public static ContactStruct constructContactFromVNode(VNode node, + int nameOrderType) { + if (!node.VName.equals("VCARD")) { + // Impossible in current implementation. Just for safety. + Log.e(LOG_TAG, "Non VCARD data is inserted."); + return null; + } + + // For name, there are three fields in vCard: FN, N, NAME. + // We prefer FN, which is a required field in vCard 3.0 , but not in vCard 2.1. + // Next, we prefer NAME, which is defined only in vCard 3.0. + // Finally, we use N, which is a little difficult to parse. + String fullName = null; + String nameFromNProperty = null; + + // Some vCard has "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", and + // "X-PHONETIC-LAST-NAME" + String xPhoneticFirstName = null; + String xPhoneticMiddleName = null; + String xPhoneticLastName = null; + + ContactStruct contact = new ContactStruct(); + + // Each Column of four properties has ISPRIMARY field + // (See android.provider.Contacts) + // If false even after the following loop, we choose the first + // entry as a "primary" entry. + boolean prefIsSetAddress = false; + boolean prefIsSetPhone = false; + boolean prefIsSetEmail = false; + boolean prefIsSetOrganization = false; + + for (PropertyNode propertyNode: node.propList) { + String name = propertyNode.propName; + + if (TextUtils.isEmpty(propertyNode.propValue)) { + continue; + } + + if (name.equals("VERSION")) { + // vCard version. Ignore this. + } else if (name.equals("FN")) { + fullName = propertyNode.propValue; + } else if (name.equals("NAME") && fullName == null) { + // Only in vCard 3.0. Use this if FN does not exist. + // Though, note that vCard 3.0 requires FN. + fullName = propertyNode.propValue; + } else if (name.equals("N")) { + nameFromNProperty = getNameFromNProperty(propertyNode.propValue_vector, + nameOrderType); + } else if (name.equals("SORT-STRING")) { + contact.phoneticName = propertyNode.propValue; + } else if (name.equals("SOUND")) { + if (propertyNode.paramMap_TYPE.contains("X-IRMC-N") && + contact.phoneticName == null) { + // Some Japanese mobile phones use this field for phonetic name, + // since vCard 2.1 does not have "SORT-STRING" type. + // Also, in some cases, the field has some ';' in it. + // We remove them. + StringBuilder builder = new StringBuilder(); + String value = propertyNode.propValue; + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch != ';') { + builder.append(ch); + } + } + contact.phoneticName = builder.toString(); + } else { + contact.addExtension(propertyNode); + } + } else if (name.equals("ADR")) { + List<String> values = propertyNode.propValue_vector; + boolean valuesAreAllEmpty = true; + for (String value : values) { + if (value.length() > 0) { + valuesAreAllEmpty = false; + break; + } + } + if (valuesAreAllEmpty) { + continue; + } + + int kind = Contacts.KIND_POSTAL; + int type = -1; + String label = ""; + boolean isPrimary = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetAddress) { + // Only first "PREF" is considered. + prefIsSetAddress = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + label = ""; + } else if (typeString.equalsIgnoreCase("WORK") || + typeString.equalsIgnoreCase("COMPANY")) { + // "COMPANY" seems emitted by Windows Mobile, which is not + // specifically supported by vCard 2.1. We assume this is same + // as "WORK". + type = Contacts.ContactMethodsColumns.TYPE_WORK; + label = ""; + } else if (typeString.equalsIgnoreCase("POSTAL")) { + kind = Contacts.KIND_POSTAL; + } else if (typeString.equalsIgnoreCase("PARCEL") || + typeString.equalsIgnoreCase("DOM") || + typeString.equalsIgnoreCase("INTL")) { + // We do not have a kind or type matching these. + // TODO: fix this. We may need to split entries into two. + // (e.g. entries for KIND_POSTAL and KIND_PERCEL) + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters + // emit non-standard types. We do not handle their values now. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "HOME" as default + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } + + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal + // ; Code, Country Name + String address; + List<String> list = propertyNode.propValue_vector; + int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + if (Locale.getDefault().getCountry().equals(Locale.JAPAN.getCountry())) { + // In Japan, the order is reversed. + for (int i = size - 1; i >= 0; i--) { + String addressPart = list.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } else { + for (int i = 0; i < size; i++) { + String addressPart = list.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } + address = builder.toString().trim(); + } else { + address = propertyNode.propValue; + } + contact.addContactmethod(kind, type, address, label, isPrimary); + } else if (name.equals("ORG")) { + // vCard specification does not specify other types. + int type = Contacts.OrganizationColumns.TYPE_WORK; + boolean isPrimary = false; + + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetOrganization) { + // vCard specification officially does not have PREF in ORG. + // This is just for safety. + prefIsSetOrganization = true; + isPrimary = true; + } + // XXX: Should we cope with X- words? + } + + List<String> list = propertyNode.propValue_vector; + int size = list.size(); + StringBuilder builder = new StringBuilder(); + for (Iterator<String> iter = list.iterator(); iter.hasNext();) { + builder.append(iter.next()); + if (iter.hasNext()) { + builder.append(' '); + } + } + + contact.addOrganization(type, builder.toString(), "", isPrimary); + } else if (name.equals("TITLE")) { + contact.setPosition(propertyNode.propValue); + } else if (name.equals("ROLE")) { + contact.setPosition(propertyNode.propValue); + } else if (name.equals("PHOTO")) { + // We prefer PHOTO to LOGO. + String valueType = propertyNode.paramMap.getAsString("VALUE"); + if (valueType != null && valueType.equals("URL")) { + // TODO: do something. + } else { + // Assume PHOTO is stored in BASE64. In that case, + // data is already stored in propValue_bytes in binary form. + // It should be automatically done by VBuilder (VDataBuilder/VCardDatabuilder) + contact.photoBytes = propertyNode.propValue_bytes; + String type = propertyNode.paramMap.getAsString("TYPE"); + if (type != null) { + contact.photoType = type; + } + } + } else if (name.equals("LOGO")) { + // When PHOTO is not available this is not URL, + // we use this instead of PHOTO. + String valueType = propertyNode.paramMap.getAsString("VALUE"); + if (valueType != null && valueType.equals("URL")) { + // TODO: do something. + } else if (contact.photoBytes == null) { + contact.photoBytes = propertyNode.propValue_bytes; + String type = propertyNode.paramMap.getAsString("TYPE"); + if (type != null) { + contact.photoType = type; + } + } + } else if (name.equals("EMAIL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetEmail) { + // Only first "PREF" is considered. + prefIsSetEmail = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.ContactMethodsColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + // We do not have Contacts.ContactMethodsColumns.TYPE_MOBILE yet. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME; + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. + // We may have INTERNET (specified in vCard spec), + // SCHOOL, etc. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "OTHER" as default. + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_OTHER; + } + contact.addContactmethod(Contacts.KIND_EMAIL, + type, propertyNode.propValue,label, isPrimary); + } else if (name.equals("TEL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + boolean isFax = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetPhone) { + // Only first "PREF" is considered. + prefIsSetPhone = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.PhonesColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.PhonesColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + type = Contacts.PhonesColumns.TYPE_MOBILE; + } else if (typeString.equalsIgnoreCase("PAGER")) { + type = Contacts.PhonesColumns.TYPE_PAGER; + } else if (typeString.equalsIgnoreCase("FAX")) { + isFax = true; + } else if (typeString.equalsIgnoreCase("VOICE") || + typeString.equalsIgnoreCase("MSG")) { + // Defined in vCard 3.0. Ignore these because they + // conflict with "HOME", "WORK", etc. + // XXX: do something? + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0){ + // We may have MODEM, CAR, ISDN, etc... + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "HOME" as default + if (type < 0) { + type = Contacts.PhonesColumns.TYPE_HOME; + } + if (isFax) { + if (type == Contacts.PhonesColumns.TYPE_HOME) { + type = Contacts.PhonesColumns.TYPE_FAX_HOME; + } else if (type == Contacts.PhonesColumns.TYPE_WORK) { + type = Contacts.PhonesColumns.TYPE_FAX_WORK; + } + } + + contact.addPhone(type, propertyNode.propValue, label, isPrimary); + } else if (name.equals("NOTE")) { + contact.notes.add(propertyNode.propValue); + } else if (name.equals("BDAY")) { + contact.addExtension(propertyNode); + } else if (name.equals("URL")) { + contact.addExtension(propertyNode); + } else if (name.equals("REV")) { + // Revision of this VCard entry. I think we can ignore this. + contact.addExtension(propertyNode); + } else if (name.equals("UID")) { + contact.addExtension(propertyNode); + } else if (name.equals("KEY")) { + // Type is X509 or PGP? I don't know how to handle this... + contact.addExtension(propertyNode); + } else if (name.equals("MAILER")) { + contact.addExtension(propertyNode); + } else if (name.equals("TZ")) { + contact.addExtension(propertyNode); + } else if (name.equals("GEO")) { + contact.addExtension(propertyNode); + } else if (name.equals("NICKNAME")) { + // vCard 3.0 only. + contact.addExtension(propertyNode); + } else if (name.equals("CLASS")) { + // vCard 3.0 only. + // e.g. CLASS:CONFIDENTIAL + contact.addExtension(propertyNode); + } else if (name.equals("PROFILE")) { + // VCard 3.0 only. Must be "VCARD". I think we can ignore this. + contact.addExtension(propertyNode); + } else if (name.equals("CATEGORIES")) { + // VCard 3.0 only. + // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + contact.addExtension(propertyNode); + } else if (name.equals("SOURCE")) { + // VCard 3.0 only. + contact.addExtension(propertyNode); + } else if (name.equals("PRODID")) { + // VCard 3.0 only. + // To specify the identifier for the product that created + // the vCard object. + contact.addExtension(propertyNode); + } else if (name.equals("X-PHONETIC-FIRST-NAME")) { + xPhoneticFirstName = propertyNode.propValue; + } else if (name.equals("X-PHONETIC-MIDDLE-NAME")) { + xPhoneticMiddleName = propertyNode.propValue; + } else if (name.equals("X-PHONETIC-LAST-NAME")) { + xPhoneticLastName = propertyNode.propValue; + } else { + // Unknown X- words and IANA token. + contact.addExtension(propertyNode); + } + } + + if (fullName != null) { + contact.name = fullName; + } else if(nameFromNProperty != null) { + contact.name = nameFromNProperty; + } else { + contact.name = ""; + } + + if (contact.phoneticName == null && + (xPhoneticFirstName != null || xPhoneticMiddleName != null || + xPhoneticLastName != null)) { + // Note: In Europe, this order should be "LAST FIRST MIDDLE". See the comment around + // NAME_ORDER_TYPE_* for more detail. + String first; + String second; + if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) { + first = xPhoneticLastName; + second = xPhoneticFirstName; + } else { + first = xPhoneticFirstName; + second = xPhoneticLastName; + } + StringBuilder builder = new StringBuilder(); + if (first != null) { + builder.append(first); + } + if (xPhoneticMiddleName != null) { + builder.append(xPhoneticMiddleName); + } + if (second != null) { + builder.append(second); + } + contact.phoneticName = builder.toString(); + } + + // Remove unnecessary white spaces. + // It is found that some mobile phone emits phonetic name with just one white space + // when a user does not specify one. + // This logic is effective toward such kind of weird data. + if (contact.phoneticName != null) { + contact.phoneticName = contact.phoneticName.trim(); + } + + // If there is no "PREF", we choose the first entries as primary. + if (!prefIsSetPhone && + contact.phoneList != null && + contact.phoneList.size() > 0) { + contact.phoneList.get(0).isPrimary = true; + } + + if (!prefIsSetAddress && contact.contactmethodList != null) { + for (ContactMethod contactMethod : contact.contactmethodList) { + if (contactMethod.kind == Contacts.KIND_POSTAL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!prefIsSetEmail && contact.contactmethodList != null) { + for (ContactMethod contactMethod : contact.contactmethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!prefIsSetOrganization && + contact.organizationList != null && + contact.organizationList.size() > 0) { + contact.organizationList.get(0).isPrimary = true; + } + + return contact; + } + + public String displayString() { + if (name.length() > 0) { + return name; + } + if (contactmethodList != null && contactmethodList.size() > 0) { + for (ContactMethod contactMethod : contactmethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL && contactMethod.isPrimary) { + return contactMethod.data; + } + } + } + if (phoneList != null && phoneList.size() > 0) { + for (PhoneData phoneData : phoneList) { + if (phoneData.isPrimary) { + return phoneData.data; + } + } + } + return ""; + } + + private void pushIntoContentProviderOrResolver(Object contentSomething, + long myContactsGroupId) { + ContentResolver resolver = null; + AbstractSyncableContentProvider provider = null; + if (contentSomething instanceof ContentResolver) { + resolver = (ContentResolver)contentSomething; + } else if (contentSomething instanceof AbstractSyncableContentProvider) { + provider = (AbstractSyncableContentProvider)contentSomething; + } else { + Log.e(LOG_TAG, "Unsupported object came."); + return; + } + + ContentValues contentValues = new ContentValues(); + contentValues.put(People.NAME, name); + contentValues.put(People.PHONETIC_NAME, phoneticName); + + if (notes.size() > 1) { + StringBuilder builder = new StringBuilder(); + for (String note : notes) { + builder.append(note); + builder.append("\n"); + } + contentValues.put(People.NOTES, builder.toString()); + } else if (notes.size() == 1){ + contentValues.put(People.NOTES, notes.get(0)); + } + + Uri personUri; + long personId = 0; + if (resolver != null) { + personUri = Contacts.People.createPersonInMyContactsGroup( + resolver, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + } + } else { + personUri = provider.nonTransactionalInsert(People.CONTENT_URI, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + ContentValues values = new ContentValues(); + values.put(GroupMembership.PERSON_ID, personId); + values.put(GroupMembership.GROUP_ID, myContactsGroupId); + Uri resultUri = provider.nonTransactionalInsert( + GroupMembership.CONTENT_URI, values); + if (resultUri == null) { + Log.e(LOG_TAG, "Faild to insert the person to MyContact."); + provider.nonTransactionalDelete(personUri, null, null); + personUri = null; + } + } + } + + if (personUri == null) { + Log.e(LOG_TAG, "Failed to create the contact."); + return; + } + + if (photoBytes != null) { + if (resolver != null) { + People.setPhotoData(resolver, personUri, photoBytes); + } else { + Uri photoUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY); + ContentValues values = new ContentValues(); + values.put(Photos.DATA, photoBytes); + provider.update(photoUri, values, null, null); + } + } + + long primaryPhoneId = -1; + if (phoneList != null && phoneList.size() > 0) { + for (PhoneData phoneData : phoneList) { + ContentValues values = new ContentValues(); + values.put(Contacts.PhonesColumns.TYPE, phoneData.type); + if (phoneData.type == Contacts.PhonesColumns.TYPE_CUSTOM) { + values.put(Contacts.PhonesColumns.LABEL, phoneData.label); + } + // Already formatted. + values.put(Contacts.PhonesColumns.NUMBER, phoneData.data); + + // Not sure about Contacts.PhonesColumns.NUMBER_KEY ... + values.put(Contacts.PhonesColumns.ISPRIMARY, 1); + values.put(Contacts.Phones.PERSON_ID, personId); + Uri phoneUri; + if (resolver != null) { + phoneUri = resolver.insert(Phones.CONTENT_URI, values); + } else { + phoneUri = provider.nonTransactionalInsert(Phones.CONTENT_URI, values); + } + if (phoneData.isPrimary) { + primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment()); + } + } + } + + long primaryOrganizationId = -1; + if (organizationList != null && organizationList.size() > 0) { + for (OrganizationData organizationData : organizationList) { + ContentValues values = new ContentValues(); + // Currently, we do not use TYPE_CUSTOM. + values.put(Contacts.OrganizationColumns.TYPE, + organizationData.type); + values.put(Contacts.OrganizationColumns.COMPANY, + organizationData.companyName); + values.put(Contacts.OrganizationColumns.TITLE, + organizationData.positionName); + values.put(Contacts.OrganizationColumns.ISPRIMARY, 1); + values.put(Contacts.OrganizationColumns.PERSON_ID, personId); + + Uri organizationUri; + if (resolver != null) { + organizationUri = resolver.insert(Organizations.CONTENT_URI, values); + } else { + organizationUri = provider.nonTransactionalInsert( + Organizations.CONTENT_URI, values); + } + if (organizationData.isPrimary) { + primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment()); + } + } + } + + long primaryEmailId = -1; + if (contactmethodList != null && contactmethodList.size() > 0) { + for (ContactMethod contactMethod : contactmethodList) { + ContentValues values = new ContentValues(); + values.put(Contacts.ContactMethodsColumns.KIND, contactMethod.kind); + values.put(Contacts.ContactMethodsColumns.TYPE, contactMethod.type); + if (contactMethod.type == Contacts.ContactMethodsColumns.TYPE_CUSTOM) { + values.put(Contacts.ContactMethodsColumns.LABEL, contactMethod.label); + } + values.put(Contacts.ContactMethodsColumns.DATA, contactMethod.data); + values.put(Contacts.ContactMethodsColumns.ISPRIMARY, 1); + values.put(Contacts.ContactMethods.PERSON_ID, personId); + + if (contactMethod.kind == Contacts.KIND_EMAIL) { + Uri emailUri; + if (resolver != null) { + emailUri = resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + emailUri = provider.nonTransactionalInsert( + ContactMethods.CONTENT_URI, values); + } + if (contactMethod.isPrimary) { + primaryEmailId = Long.parseLong(emailUri.getLastPathSegment()); + } + } else { // probably KIND_POSTAL + if (resolver != null) { + resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + provider.nonTransactionalInsert( + ContactMethods.CONTENT_URI, values); + } + } + } + } + + if (extensionMap != null && extensionMap.size() > 0) { + ArrayList<ContentValues> contentValuesArray; + if (resolver != null) { + contentValuesArray = new ArrayList<ContentValues>(); + } else { + contentValuesArray = null; + } + for (Entry<String, List<String>> entry : extensionMap.entrySet()) { + String key = entry.getKey(); + List<String> list = entry.getValue(); + for (String value : list) { + ContentValues values = new ContentValues(); + values.put(Extensions.NAME, key); + values.put(Extensions.VALUE, value); + values.put(Extensions.PERSON_ID, personId); + if (resolver != null) { + contentValuesArray.add(values); + } else { + provider.nonTransactionalInsert(Extensions.CONTENT_URI, values); + } + } + } + if (resolver != null) { + resolver.bulkInsert(Extensions.CONTENT_URI, + contentValuesArray.toArray(new ContentValues[0])); + } + } + + if (primaryPhoneId >= 0 || primaryOrganizationId >= 0 || primaryEmailId >= 0) { + ContentValues values = new ContentValues(); + if (primaryPhoneId >= 0) { + values.put(People.PRIMARY_PHONE_ID, primaryPhoneId); + } + if (primaryOrganizationId >= 0) { + values.put(People.PRIMARY_ORGANIZATION_ID, primaryOrganizationId); + } + if (primaryEmailId >= 0) { + values.put(People.PRIMARY_EMAIL_ID, primaryEmailId); + } + if (resolver != null) { + resolver.update(personUri, values, null, null); + } else { + provider.nonTransactionalUpdate(personUri, values, null, null); + } + } + } + + /** + * Push this object into database in the resolver. + */ + public void pushIntoContentResolver(ContentResolver resolver) { + pushIntoContentProviderOrResolver(resolver, 0); + } + + /** + * Push this object into AbstractSyncableContentProvider object. + */ + public void pushIntoAbstractSyncableContentProvider( + AbstractSyncableContentProvider provider, long myContactsGroupId) { + boolean successful = false; + provider.beginTransaction(); + try { + pushIntoContentProviderOrResolver(provider, myContactsGroupId); + successful = true; + } finally { + provider.endTransaction(successful); + } + } + + public boolean isIgnorable() { + return TextUtils.isEmpty(name) && + TextUtils.isEmpty(phoneticName) && + (phoneList == null || phoneList.size() == 0) && + (contactmethodList == null || contactmethodList.size() == 0); } } diff --git a/core/java/android/syncml/pim/vcard/VCardComposer.java b/core/java/android/syncml/pim/vcard/VCardComposer.java index 05e8f407d0e6..192736ae59d9 100644 --- a/core/java/android/syncml/pim/vcard/VCardComposer.java +++ b/core/java/android/syncml/pim/vcard/VCardComposer.java @@ -124,9 +124,9 @@ public class VCardComposer { mResult.append("ORG:").append(struct.company).append(mNewline); } - if (!isNull(struct.notes)) { + if (struct.notes.size() > 0 && !isNull(struct.notes.get(0))) { mResult.append("NOTE:").append( - foldingString(struct.notes, vcardversion)).append(mNewline); + foldingString(struct.notes.get(0), vcardversion)).append(mNewline); } if (!isNull(struct.title)) { @@ -190,7 +190,7 @@ public class VCardComposer { */ private void appendPhotoStr(byte[] bytes, String type, int version) throws VCardException { - String value, apptype, encodingStr; + String value, encodingStr; try { value = foldingString(new String(Base64.encodeBase64(bytes, true)), version); @@ -198,20 +198,23 @@ public class VCardComposer { throw new VCardException(e.getMessage()); } - if (isNull(type)) { - type = "image/jpeg"; - } - if (type.indexOf("jpeg") > 0) { - apptype = "JPEG"; - } else if (type.indexOf("gif") > 0) { - apptype = "GIF"; - } else if (type.indexOf("bmp") > 0) { - apptype = "BMP"; + if (isNull(type) || type.toUpperCase().indexOf("JPEG") >= 0) { + type = "JPEG"; + } else if (type.toUpperCase().indexOf("GIF") >= 0) { + type = "GIF"; + } else if (type.toUpperCase().indexOf("BMP") >= 0) { + type = "BMP"; } else { - apptype = type.substring(type.indexOf("/")).toUpperCase(); + // Handle the string like "image/tiff". + int indexOfSlash = type.indexOf("/"); + if (indexOfSlash >= 0) { + type = type.substring(indexOfSlash + 1).toUpperCase(); + } else { + type = type.toUpperCase(); + } } - mResult.append("LOGO;TYPE=").append(apptype); + mResult.append("LOGO;TYPE=").append(type); if (version == VERSION_VCARD21_INT) { encodingStr = ";ENCODING=BASE64:"; value = value + mNewline; @@ -281,7 +284,7 @@ public class VCardComposer { private String getPhoneTypeStr(PhoneData phone) { - int phoneType = Integer.parseInt(phone.type); + int phoneType = phone.type; String typeStr, label; if (phoneTypeMap.containsKey(phoneType)) { @@ -308,7 +311,7 @@ public class VCardComposer { String joinMark = version == VERSION_VCARD21_INT ? ";" : ","; for (ContactStruct.ContactMethod contactMethod : contactMList) { // same with v2.1 and v3.0 - switch (Integer.parseInt(contactMethod.kind)) { + switch (contactMethod.kind) { case Contacts.KIND_EMAIL: String mailType = "INTERNET"; if (!isNull(contactMethod.data)) { diff --git a/core/java/android/syncml/pim/vcard/VCardDataBuilder.java b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java new file mode 100644 index 000000000000..a0513f1654f8 --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java @@ -0,0 +1,442 @@ +/* + * 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. + */ + +package android.syncml.pim.vcard; + +import android.app.ProgressDialog; +import android.content.AbstractSyncableContentProvider; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.IContentProvider; +import android.os.Handler; +import android.provider.Contacts; +import android.syncml.pim.PropertyNode; +import android.syncml.pim.VBuilder; +import android.syncml.pim.VNode; +import android.syncml.pim.VParser; +import android.util.CharsetUtils; +import android.util.Log; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * VBuilder for VCard. VCard may contain big photo images encoded by BASE64, + * If we store all VNode entries in memory like VDataBuilder.java, + * OutOfMemoryError may be thrown. Thus, this class push each VCard entry into + * ContentResolver immediately. + */ +public class VCardDataBuilder implements VBuilder { + static private String LOG_TAG = "VCardDataBuilder"; + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String DEFAULT_CHARSET = "UTF-8"; + + private class ProgressShower implements Runnable { + private ContactStruct mContact; + + public ProgressShower(ContactStruct contact) { + mContact = contact; + } + + public void run () { + mProgressDialog.setMessage(mProgressMessage + "\n" + + mContact.displayString()); + } + } + + /** type=VNode */ + private VNode mCurrentVNode; + private PropertyNode mCurrentPropNode; + private String mCurrentParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + private boolean mStrictLineBreakParsing; + private ContentResolver mContentResolver; + + // For letting VCardDataBuilder show the display name of VCard while handling it. + private Handler mHandler; + private ProgressDialog mProgressDialog; + private String mProgressMessage; + private Runnable mOnProgressRunnable; + private boolean mLastNameComesBeforeFirstName; + + // Just for testing. + private long mTimeCreateContactStruct; + private long mTimePushIntoContentResolver; + + // Ideally, this should be ContactsProvider but it seems Class loader cannot find it, + // even when it is subclass of ContactsProvider... + private AbstractSyncableContentProvider mProvider; + private long mMyContactsGroupId; + + public VCardDataBuilder(ContentResolver resolver) { + mTargetCharset = DEFAULT_CHARSET; + mContentResolver = resolver; + } + + /** + * Constructor which requires minimum requiredvariables. + * + * @param resolver insert each data into this ContentResolver + * @param progressDialog + * @param progressMessage + * @param handler if this importer works on the different thread than main one, + * set appropriate handler object. If not, it is ok to set this null. + */ + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler) { + this(resolver, progressDialog, progressMessage, handler, + null, null, false, false); + } + + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler, + String charset, + boolean strictLineBreakParsing, + boolean lastNameComesBeforeFirstName) { + this(resolver, progressDialog, progressMessage, handler, + null, charset, strictLineBreakParsing, + lastNameComesBeforeFirstName); + } + + /** + * @hide + */ + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler, + String sourceCharset, + String targetCharset, + boolean strictLineBreakParsing, + boolean lastNameComesBeforeFirstName) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VParser.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = DEFAULT_CHARSET; + } + mContentResolver = resolver; + mStrictLineBreakParsing = strictLineBreakParsing; + mHandler = handler; + mProgressDialog = progressDialog; + mProgressMessage = progressMessage; + mLastNameComesBeforeFirstName = lastNameComesBeforeFirstName; + + tryGetOriginalProvider(); + } + + private void tryGetOriginalProvider() { + final ContentResolver resolver = mContentResolver; + + if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) { + Log.e(LOG_TAG, "Could not get group id of MyContact"); + return; + } + + IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI); + ContentProvider contentProvider = + ContentProvider.coerceToLocalContentProvider(iProviderForName); + if (contentProvider == null) { + Log.e(LOG_TAG, "Fail to get ContentProvider object."); + return; + } + + if (!(contentProvider instanceof AbstractSyncableContentProvider)) { + Log.e(LOG_TAG, + "Acquired ContentProvider object is not AbstractSyncableContentProvider."); + return; + } + + mProvider = (AbstractSyncableContentProvider)contentProvider; + } + + public void setOnProgressRunnable(Runnable runnable) { + mOnProgressRunnable = runnable; + } + + public void start() { + } + + public void end() { + } + + /** + * Assume that VCard is not nested. In other words, this code does not accept + */ + public void startRecord(String type) { + if (mCurrentVNode != null) { + // This means startRecord() is called inside startRecord() - endRecord() block. + // TODO: should throw some Exception + Log.e(LOG_TAG, "Nested VCard code is not supported now."); + } + mCurrentVNode = new VNode(); + mCurrentVNode.parseStatus = 1; + mCurrentVNode.VName = type; + } + + public void endRecord() { + mCurrentVNode.parseStatus = 0; + long start = System.currentTimeMillis(); + ContactStruct contact = ContactStruct.constructContactFromVNode(mCurrentVNode, + mLastNameComesBeforeFirstName ? ContactStruct.NAME_ORDER_TYPE_JAPANESE : + ContactStruct.NAME_ORDER_TYPE_ENGLISH); + mTimeCreateContactStruct += System.currentTimeMillis() - start; + if (!contact.isIgnorable()) { + if (mProgressDialog != null && mProgressMessage != null) { + if (mHandler != null) { + mHandler.post(new ProgressShower(contact)); + } else { + mProgressDialog.setMessage(mProgressMessage + "\n" + + contact.displayString()); + } + } + start = System.currentTimeMillis(); + if (mProvider != null) { + contact.pushIntoAbstractSyncableContentProvider( + mProvider, mMyContactsGroupId); + } else { + contact.pushIntoContentResolver(mContentResolver); + } + mTimePushIntoContentResolver += System.currentTimeMillis() - start; + } + if (mOnProgressRunnable != null) { + mOnProgressRunnable.run(); + } + mCurrentVNode = null; + } + + public void startProperty() { + mCurrentPropNode = new PropertyNode(); + } + + public void endProperty() { + mCurrentVNode.propList.add(mCurrentPropNode); + mCurrentPropNode = null; + } + + public void propertyName(String name) { + mCurrentPropNode.propName = name; + } + + public void propertyGroup(String group) { + mCurrentPropNode.propGroupSet.add(group); + } + + public void propertyParamType(String type) { + mCurrentParamType = type; + } + + public void propertyParamValue(String value) { + if (mCurrentParamType == null || + mCurrentParamType.equalsIgnoreCase("TYPE")) { + mCurrentPropNode.paramMap_TYPE.add(value); + } else { + mCurrentPropNode.paramMap.put(mCurrentParamType, value); + } + + mCurrentParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + mCurrentPropNode.propValue_bytes = + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + // "= " -> " ", "=\t" -> "\t". + // Previous code had done this replacement. Keep on the safe side. + StringBuilder builder = new StringBuilder(); + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch == '=' && i < length - 1) { + char nextCh = value.charAt(i + 1); + if (nextCh == ' ' || nextCh == '\t') { + + builder.append(nextCh); + i++; + continue; + } + } + builder.append(ch); + } + String quotedPrintable = builder.toString(); + + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + builder = new StringBuilder(); + length = quotedPrintable.length(); + ArrayList<String> list = new ArrayList<String>(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); + } + + builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List<String> values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); + } + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + public void showDebugInfo() { + Log.d(LOG_TAG, "time for creating ContactStruct: " + mTimeCreateContactStruct + " ms"); + Log.d(LOG_TAG, "time for insert ContactStruct to database: " + + mTimePushIntoContentResolver + " ms"); + } + + private String listToString(List<String> list){ + int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + int i = 0; + for (String type : list) { + builder.append(type); + if (i < size - 1) { + builder.append(";"); + } + } + return builder.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardEntryCounter.java b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java new file mode 100644 index 000000000000..03cd1d9439fd --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java @@ -0,0 +1,63 @@ +/* + * 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.syncml.pim.vcard; + +import java.util.List; + +import android.syncml.pim.VBuilder; + +public class VCardEntryCounter implements VBuilder { + private int mCount; + + public int getCount() { + return mCount; + } + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void endRecord() { + mCount++; + } + + public void startProperty() { + } + + public void endProperty() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List<String> values) { + } +}
\ No newline at end of file diff --git a/core/java/android/syncml/pim/vcard/VCardNestedException.java b/core/java/android/syncml/pim/vcard/VCardNestedException.java new file mode 100644 index 000000000000..def6f3b785ff --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardNestedException.java @@ -0,0 +1,27 @@ +/* + * 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.syncml.pim.vcard; + +/** + * VCardException thrown when VCard is nested without VCardParser's being notified. + */ +public class VCardNestedException extends VCardException { + public VCardNestedException() {} + public VCardNestedException(String message) { + super(message); + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V21.java b/core/java/android/syncml/pim/vcard/VCardParser_V21.java index f853c5e383fa..d86566811d85 100644 --- a/core/java/android/syncml/pim/vcard/VCardParser_V21.java +++ b/core/java/android/syncml/pim/vcard/VCardParser_V21.java @@ -17,21 +17,26 @@ package android.syncml.pim.vcard; import android.syncml.pim.VBuilder; +import android.syncml.pim.VParser; +import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.regex.Pattern; /** - * This class is used to parse vcard. Please refer to vCard Specification 2.1 + * This class is used to parse vcard. Please refer to vCard Specification 2.1. */ public class VCardParser_V21 { - + private static final String LOG_TAG = "VCardParser_V21"; + + public static final String DEFAULT_CHARSET = VParser.DEFAULT_CHARSET; + /** Store the known-type */ private static final HashSet<String> sKnownTypeSet = new HashSet<String>( Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", @@ -42,19 +47,17 @@ public class VCardParser_V21 { "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI", "WAVE", "AIFF", "PCM", "X509", "PGP")); - + /** Store the known-value */ private static final HashSet<String> sKnownValueSet = new HashSet<String>( Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")); - /** Store the property name available in vCard 2.1 */ - // NICKNAME is not supported in vCard 2.1, but some vCard may contain. + /** Store the property names available in vCard 2.1 */ private static final HashSet<String> sAvailablePropertyNameV21 = new HashSet<String>(Arrays.asList( - "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", - "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", - "NICKNAME")); + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")); // Though vCard 2.1 specification does not allow "B" encoding, some data may have it. // We allow it for safety... @@ -76,6 +79,30 @@ public class VCardParser_V21 { // Should not directly read a line from this. Use getLine() instead. protected BufferedReader mReader; + private boolean mCanceled; + + // In some cases, vCard is nested. Currently, we only consider the most interior vCard data. + // See v21_foma_1.vcf in test directory for more information. + private int mNestCount; + + // In order to reduce warning message as much as possible, we hold the value which made Logger + // emit a warning message. + protected HashSet<String> mWarningValueMap = new HashSet<String>(); + + // Just for debugging + private long mTimeTotal; + private long mTimeStartRecord; + private long mTimeEndRecord; + private long mTimeStartProperty; + private long mTimeEndProperty; + private long mTimeParseItems; + private long mTimeParseItem1; + private long mTimeParseItem2; + private long mTimeParseItem3; + private long mTimeHandlePropertyValue1; + private long mTimeHandlePropertyValue2; + private long mTimeHandlePropertyValue3; + /** * Create a new VCard parser. */ @@ -83,12 +110,35 @@ public class VCardParser_V21 { super(); } + public VCardParser_V21(VCardSourceDetector detector) { + super(); + if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) { + mNestCount = 1; + } + } + /** * Parse the file at the given position * vcard_file = [wsls] vcard [wsls] */ protected void parseVCardFile() throws IOException, VCardException { - while (parseOneVCard()) { + boolean firstReading = true; + while (true) { + if (mCanceled) { + break; + } + if (!parseOneVCard(firstReading)) { + break; + } + firstReading = false; + } + + if (mNestCount > 0) { + boolean useCache = true; + for (int i = 0; i < mNestCount; i++) { + readEndVCard(useCache, true); + useCache = false; + } } } @@ -100,7 +150,13 @@ public class VCardParser_V21 { * @return true when the propertyName is a valid property name. */ protected boolean isValidPropertyName(String propertyName) { - return sAvailablePropertyNameV21.contains(propertyName.toUpperCase()); + if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); + } + return true; } /** @@ -129,7 +185,7 @@ public class VCardParser_V21 { line = getLine(); if (line == null) { throw new VCardException("Reached end of buffer."); - } else if (line.trim().length() > 0) { + } else if (line.trim().length() > 0) { return line; } } @@ -140,12 +196,37 @@ public class VCardParser_V21 { * items *CRLF * "END" [ws] ":" [ws] "VCARD" */ - private boolean parseOneVCard() throws IOException, VCardException { - if (!readBeginVCard()) { + private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException { + boolean allowGarbage = false; + if (firstReading) { + if (mNestCount > 0) { + for (int i = 0; i < mNestCount; i++) { + if (!readBeginVCard(allowGarbage)) { + return false; + } + allowGarbage = true; + } + } + } + + if (!readBeginVCard(allowGarbage)) { return false; } + long start; + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.startRecord("VCARD"); + mTimeStartRecord += System.currentTimeMillis() - start; + } + start = System.currentTimeMillis(); parseItems(); - readEndVCard(); + mTimeParseItems += System.currentTimeMillis() - start; + readEndVCard(true, false); + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.endRecord(); + mTimeEndRecord += System.currentTimeMillis() - start; + } return true; } @@ -154,46 +235,102 @@ public class VCardParser_V21 { * @throws IOException * @throws VCardException */ - protected boolean readBeginVCard() throws IOException, VCardException { + protected boolean readBeginVCard(boolean allowGarbage) + throws IOException, VCardException { String line; - while (true) { - line = getLine(); - if (line == null) { - return false; - } else if (line.trim().length() > 0) { - break; + do { + while (true) { + line = getLine(); + if (line == null) { + return false; + } else if (line.trim().length() > 0) { + break; + } } - } - String[] strArray = line.split(":", 2); - - // Though vCard specification does not allow lower cases, - // some data may have them, so we allow it. - if (!(strArray.length == 2 && - strArray[0].trim().equalsIgnoreCase("BEGIN") && - strArray[1].trim().equalsIgnoreCase("VCARD"))) { - throw new VCardException("BEGIN:VCARD != \"" + line + "\""); - } - - if (mBuilder != null) { - mBuilder.startRecord("VCARD"); - } + String[] strArray = line.split(":", 2); + int length = strArray.length; - return true; + // Though vCard 2.1/3.0 specification does not allow lower cases, + // some data may have them, so we allow it (Actually, previous code + // had explicitly allowed "BEGIN:vCard" though there's no example). + // + // TODO: ignore non vCard entry (e.g. vcalendar). + // XXX: Not sure, but according to VDataBuilder.java, vcalendar + // entry + // may be nested. Just seeking "END:SOMETHING" may not be enough. + // e.g. + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // BEGIN:VSOMETHING + // ... (Must ignore this) + // BEGIN:VSOMETHING2 + // ... (Must ignore this) + // END:VSOMETHING2 + // ... (Must ignore this!) + // END:VSOMETHING + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // INVALID_STRING (VCardException should be thrown) + if (length == 2 && + strArray[0].trim().equalsIgnoreCase("BEGIN") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return true; + } else if (!allowGarbage) { + if (mNestCount > 0) { + mPreviousLine = line; + return false; + } else { + throw new VCardException( + "Expected String \"BEGIN:VCARD\" did not come " + + "(Instead, \"" + line + "\" came)"); + } + } + } while(allowGarbage); + + throw new VCardException("Reached where must not be reached."); } - - protected void readEndVCard() throws VCardException { - // Though vCard specification does not allow lower cases, - // some data may have them, so we allow it. - String[] strArray = mPreviousLine.split(":", 2); - if (!(strArray.length == 2 && - strArray[0].trim().equalsIgnoreCase("END") && - strArray[1].trim().equalsIgnoreCase("VCARD"))) { - throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); - } - - if (mBuilder != null) { - mBuilder.endRecord(); - } + + /** + * The arguments useCache and allowGarbase are usually true and false accordingly when + * this function is called outside this function itself. + * + * @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine() + * is used. + * @param allowGarbage When true, ignore non "END:VCARD" line. + * @throws IOException + * @throws VCardException + */ + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { + String line; + do { + if (useCache) { + // Though vCard specification does not allow lower cases, + // some data may have them, so we allow it. + line = mPreviousLine; + } else { + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Expected END:VCARD was not found."); + } else if (line.trim().length() > 0) { + break; + } + } + } + + String[] strArray = line.split(":", 2); + if (strArray.length == 2 && + strArray[0].trim().equalsIgnoreCase("END") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return; + } else if (!allowGarbage) { + throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); + } + useCache = false; + } while (allowGarbage); } /** @@ -205,32 +342,33 @@ public class VCardParser_V21 { boolean ended = false; if (mBuilder != null) { + long start = System.currentTimeMillis(); mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; } - - try { - ended = parseItem(); - } finally { - if (mBuilder != null) { - mBuilder.endProperty(); - } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; } while (!ended) { // follow VCARD ,it wont reach endProperty if (mBuilder != null) { + long start = System.currentTimeMillis(); mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; } - try { - ended = parseItem(); - } finally { - if (mBuilder != null) { - mBuilder.endProperty(); - } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; } } } - + /** * item = [groups "."] name [params] ":" value CRLF * / [groups "."] "ADR" [params] ":" addressparts CRLF @@ -241,57 +379,134 @@ public class VCardParser_V21 { protected boolean parseItem() throws IOException, VCardException { mEncoding = sDefaultEncoding; - // params = ";" [ws] paramlist String line = getNonEmptyLine(); - String[] strArray = line.split(":", 2); - if (strArray.length < 2) { - throw new VCardException("Invalid line(\":\" does not exist): " + line); - } - String propertyValue = strArray[1]; - String[] groupNameParamsArray = strArray[0].split(";"); - String groupAndName = groupNameParamsArray[0].trim(); - String[] groupNameArray = groupAndName.split("\\."); - int length = groupNameArray.length; - String propertyName = groupNameArray[length - 1]; - if (mBuilder != null) { - mBuilder.propertyName(propertyName); - for (int i = 0; i < length - 1; i++) { - mBuilder.propertyGroup(groupNameArray[i]); - } - } - if (propertyName.equalsIgnoreCase("END")) { - mPreviousLine = line; + long start = System.currentTimeMillis(); + + String[] propertyNameAndValue = separateLineAndHandleGroup(line); + if (propertyNameAndValue == null) { return true; } - - length = groupNameParamsArray.length; - for (int i = 1; i < length; i++) { - handleParams(groupNameParamsArray[i]); + if (propertyNameAndValue.length != 2) { + throw new VCardException("Invalid line \"" + line + "\""); } - - if (isValidPropertyName(propertyName) || - propertyName.startsWith("X-")) { - if (propertyName.equals("VERSION") && - !propertyValue.equals(getVersion())) { - throw new VCardVersionException("Incompatible version: " + - propertyValue + " != " + getVersion()); - } - handlePropertyValue(propertyName, propertyValue); - return false; - } else if (propertyName.equals("ADR") || + String propertyName = propertyNameAndValue[0].toUpperCase(); + String propertyValue = propertyNameAndValue[1]; + + mTimeParseItem1 += System.currentTimeMillis() - start; + + if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) { + start = System.currentTimeMillis(); handleMultiplePropertyValue(propertyName, propertyValue); + mTimeParseItem3 += System.currentTimeMillis() - start; return false; } else if (propertyName.equals("AGENT")) { handleAgent(propertyValue); return false; + } else if (isValidPropertyName(propertyName)) { + if (propertyName.equals("BEGIN")) { + if (propertyValue.equals("VCARD")) { + throw new VCardNestedException("This vCard has nested vCard data in it."); + } else { + throw new VCardException("Unknown BEGIN type: " + propertyValue); + } + } else if (propertyName.equals("VERSION") && + !propertyValue.equals(getVersion())) { + throw new VCardVersionException("Incompatible version: " + + propertyValue + " != " + getVersion()); + } + start = System.currentTimeMillis(); + handlePropertyValue(propertyName, propertyValue); + mTimeParseItem2 += System.currentTimeMillis() - start; + return false; } throw new VCardException("Unknown property name: \"" + propertyName + "\""); } + static private final int STATE_GROUP_OR_PROPNAME = 0; + static private final int STATE_PARAMS = 1; + // vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not. + // This is just for safety. + static private final int STATE_PARAMS_IN_DQUOTE = 2; + + protected String[] separateLineAndHandleGroup(String line) throws VCardException { + int length = line.length(); + int state = STATE_GROUP_OR_PROPNAME; + int nameIndex = 0; + + String[] propertyNameAndValue = new String[2]; + + for (int i = 0; i < length; i++) { + char ch = line.charAt(i); + switch (state) { + case STATE_GROUP_OR_PROPNAME: + if (ch == ':') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } else if (ch == '.') { + String groupName = line.substring(nameIndex, i); + if (mBuilder != null) { + mBuilder.propertyGroup(groupName); + } + nameIndex = i + 1; + } else if (ch == ';') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + nameIndex = i + 1; + state = STATE_PARAMS; + } + break; + case STATE_PARAMS: + if (ch == '"') { + state = STATE_PARAMS_IN_DQUOTE; + } else if (ch == ';') { + handleParams(line.substring(nameIndex, i)); + nameIndex = i + 1; + } else if (ch == ':') { + handleParams(line.substring(nameIndex, i)); + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } + break; + case STATE_PARAMS_IN_DQUOTE: + if (ch == '"') { + state = STATE_PARAMS; + } + break; + } + } + + throw new VCardException("Invalid line: \"" + line + "\""); + } + + /** * params = ";" [ws] paramlist * paramlist = paramlist [ws] ";" [ws] param @@ -330,18 +545,19 @@ public class VCardParser_V21 { } /** - * typeval = knowntype / "X-" word + * ptypeval = knowntype / "X-" word */ - protected void handleType(String ptypeval) throws VCardException { - if (sKnownTypeSet.contains(ptypeval.toUpperCase()) || - ptypeval.startsWith("X-")) { - if (mBuilder != null) { - mBuilder.propertyParamType("TYPE"); - mBuilder.propertyParamValue(ptypeval.toUpperCase()); - } - } else { - throw new VCardException("Unknown type: \"" + ptypeval + "\""); - } + protected void handleType(String ptypeval) { + String upperTypeValue = ptypeval; + if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) && + !mWarningValueMap.contains(ptypeval)) { + mWarningValueMap.add(ptypeval); + Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval); + } + if (mBuilder != null) { + mBuilder.propertyParamType("TYPE"); + mBuilder.propertyParamValue(upperTypeValue); + } } /** @@ -427,31 +643,48 @@ public class VCardParser_V21 { protected void handlePropertyValue( String propertyName, String propertyValue) throws IOException, VCardException { - if (mEncoding == null || mEncoding.equalsIgnoreCase("7BIT") - || mEncoding.equalsIgnoreCase("8BIT") - || mEncoding.toUpperCase().startsWith("X-")) { - if (mBuilder != null) { - ArrayList<String> v = new ArrayList<String>(); - v.add(maybeUnescapeText(propertyValue)); - mBuilder.propertyValues(v); - } - } else if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + long start = System.currentTimeMillis(); String result = getQuotedPrintable(propertyValue); if (mBuilder != null) { ArrayList<String> v = new ArrayList<String>(); v.add(result); mBuilder.propertyValues(v); } + mTimeHandlePropertyValue2 += System.currentTimeMillis() - start; } else if (mEncoding.equalsIgnoreCase("BASE64") || mEncoding.equalsIgnoreCase("B")) { - String result = getBase64(propertyValue); + long start = System.currentTimeMillis(); + // It is very rare, but some BASE64 data may be so big that + // OutOfMemoryError occurs. To ignore such cases, use try-catch. + try { + String result = getBase64(propertyValue); + if (mBuilder != null) { + ArrayList<String> v = new ArrayList<String>(); + v.add(result); + mBuilder.propertyValues(v); + } + } catch (OutOfMemoryError error) { + Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); + if (mBuilder != null) { + mBuilder.propertyValues(null); + } + } + mTimeHandlePropertyValue3 += System.currentTimeMillis() - start; + } else { + if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT") + || mEncoding.equalsIgnoreCase("8BIT") + || mEncoding.toUpperCase().startsWith("X-"))) { + Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\"."); + } + + long start = System.currentTimeMillis(); if (mBuilder != null) { ArrayList<String> v = new ArrayList<String>(); - v.add(result); + v.add(maybeUnescapeText(propertyValue)); mBuilder.propertyValues(v); - } - } else { - throw new VCardException("Unknown encoding: \"" + mEncoding + "\""); + } + mTimeHandlePropertyValue1 += System.currentTimeMillis() - start; } } @@ -546,57 +779,51 @@ public class VCardParser_V21 { if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { propertyValue = getQuotedPrintable(propertyValue); } - - if (propertyValue.endsWith("\\")) { + + if (mBuilder != null) { + // TODO: limit should be set in accordance with propertyName? StringBuilder builder = new StringBuilder(); - // builder.append(propertyValue); - builder.append(propertyValue.substring(0, propertyValue.length() - 1)); - try { - String line; - while (true) { - line = getNonEmptyLine(); - // builder.append("\r\n"); - // builder.append(line); - if (!line.endsWith("\\")) { - builder.append(line); - break; + ArrayList<String> list = new ArrayList<String>(); + int length = propertyValue.length(); + for (int i = 0; i < length; i++) { + char ch = propertyValue.charAt(i); + if (ch == '\\' && i < length - 1) { + char nextCh = propertyValue.charAt(i + 1); + String unescapedString = maybeUnescape(nextCh); + if (unescapedString != null) { + builder.append(unescapedString); + i++; } else { - builder.append(line.substring(0, line.length() - 1)); + builder.append(ch); } + } else if (ch == ';') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else { + builder.append(ch); } - } catch (IOException e) { - throw new VCardException( - "IOException is throw during reading propertyValue" + e); } - // Now, propertyValue may contain "\r\n" - propertyValue = builder.toString(); - } - - if (mBuilder != null) { - // In String#replaceAll() and Pattern class, "\\\\" means single slash. - - final String IMPOSSIBLE_STRING = "\0"; - // First replace two backslashes with impossible strings. - propertyValue = propertyValue.replaceAll("\\\\\\\\", IMPOSSIBLE_STRING); - - // Now, split propertyValue with ; whose previous char is not back slash. - Pattern pattern = Pattern.compile("(?<!\\\\);"); - // TODO: limit should be set in accordance with propertyName? - String[] strArray = pattern.split(propertyValue, -1); - ArrayList<String> arrayList = new ArrayList<String>(); - for (String str : strArray) { - // Replace impossible strings with original two backslashes - arrayList.add( - unescapeText(str.replaceAll(IMPOSSIBLE_STRING, "\\\\\\\\"))); - } - mBuilder.propertyValues(arrayList); + list.add(builder.toString()); + mBuilder.propertyValues(list); } } /** * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all. + * + * item = ... + * / [groups "."] "AGENT" + * [params] ":" vcard CRLF + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF "END" [ws] ":" [ws] "VCARD" + * */ - protected void handleAgent(String propertyValue) throws IOException, VCardException { + protected void handleAgent(String propertyValue) throws VCardException { + throw new VCardException("AGENT Property is not supported."); + /* This is insufficient support. Also, AGENT Property is very rare. + Ignore it for now. + TODO: fix this. + String[] strArray = propertyValue.split(":", 2); if (!(strArray.length == 2 || strArray[0].trim().equalsIgnoreCase("BEGIN") && @@ -605,6 +832,7 @@ public class VCardParser_V21 { } parseItems(); readEndVCard(); + */ } /** @@ -615,17 +843,18 @@ public class VCardParser_V21 { } /** - * Convert escaped text into unescaped text. + * Returns unescaped String if the character should be unescaped. Return null otherwise. + * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be. */ - protected String unescapeText(String text) { + protected String maybeUnescape(char ch) { // Original vCard 2.1 specification does not allow transformation // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of // this class allowed them, so keep it as is. - // In String#replaceAll(), "\\\\" means single slash. - return text.replaceAll("\\\\;", ";") - .replaceAll("\\\\:", ":") - .replaceAll("\\\\,", ",") - .replaceAll("\\\\\\\\", "\\\\"); + if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { + return String.valueOf(ch); + } else { + return null; + } } /** @@ -656,12 +885,15 @@ public class VCardParser_V21 { */ public boolean parse(InputStream is, String charset, VBuilder builder) throws IOException, VCardException { + // TODO: make this count error entries instead of just throwing VCardException. + // TODO: If we really need to allow only CRLF as line break, // we will have to develop our own BufferedReader(). - mReader = new BufferedReader(new InputStreamReader(is, charset)); + mReader = new CustomBufferedReader(new InputStreamReader(is, charset)); mBuilder = builder; + long start = System.currentTimeMillis(); if (mBuilder != null) { mBuilder.start(); } @@ -669,9 +901,50 @@ public class VCardParser_V21 { if (mBuilder != null) { mBuilder.end(); } + mTimeTotal += System.currentTimeMillis() - start; + return true; } + public boolean parse(InputStream is, VBuilder builder) throws IOException, VCardException { + return parse(is, DEFAULT_CHARSET, builder); + } + + /** + * Cancel parsing. + * Actual cancel is done after the end of the current one vcard entry parsing. + */ + public void cancel() { + mCanceled = true; + } + + /** + * It is very, very rare case, but there is a case where + * canceled may be already true outside this object. + * @hide + */ + public void parse(InputStream is, String charset, VBuilder builder, boolean canceled) + throws IOException, VCardException { + mCanceled = canceled; + parse(is, charset, builder); + } + + public void showDebugInfo() { + Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms"); + if (mReader instanceof CustomBufferedReader) { + Log.d(LOG_TAG, "total readLine time: " + + ((CustomBufferedReader)mReader).getTotalmillisecond() + " ms"); + } + Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms"); + Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms"); + Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " ms"); + } + private boolean isLetter(char ch) { if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { return true; @@ -679,3 +952,24 @@ public class VCardParser_V21 { return false; } } + +class CustomBufferedReader extends BufferedReader { + private long mTime; + + public CustomBufferedReader(Reader in) { + super(in); + } + + @Override + public String readLine() throws IOException { + long start = System.currentTimeMillis(); + String ret = super.readLine(); + long end = System.currentTimeMillis(); + mTime += end - start; + return ret; + } + + public long getTotalmillisecond() { + return mTime; + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V30.java b/core/java/android/syncml/pim/vcard/VCardParser_V30.java index 901bd49ef006..e67525eec332 100644 --- a/core/java/android/syncml/pim/vcard/VCardParser_V30.java +++ b/core/java/android/syncml/pim/vcard/VCardParser_V30.java @@ -16,8 +16,9 @@ package android.syncml.pim.vcard; +import android.util.Log; + import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -26,9 +27,11 @@ import java.util.HashSet; * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426) */ public class VCardParser_V30 extends VCardParser_V21 { + private static final String LOG_TAG = "VCardParser_V30"; + private static final HashSet<String> acceptablePropsWithParam = new HashSet<String>( Arrays.asList( - "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", @@ -51,8 +54,14 @@ public class VCardParser_V30 extends VCardParser_V21 { @Override protected boolean isValidPropertyName(String propertyName) { - return acceptablePropsWithParam.contains(propertyName) || - acceptablePropsWithoutParam.contains(propertyName); + if (!(acceptablePropsWithParam.contains(propertyName) || + acceptablePropsWithoutParam.contains(propertyName) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName); + } + return true; } @Override @@ -100,7 +109,21 @@ public class VCardParser_V30 extends VCardParser_V21 { } } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') { if (builder != null) { - // TODO: Check whether MIME requires only one whitespace. + // See Section 5.8.1 of RFC 2425 (MIME-DIR document). + // Following is the excerpts from it. + // + // DESCRIPTION:This is a long description that exists on a long line. + // + // Can be represented as: + // + // DESCRIPTION:This is a long description + // that exists on a long line. + // + // It could also be represented as: + // + // DESCRIPTION:This is a long descrip + // tion that exists o + // n a long line. builder.append(line.substring(1)); } else if (mPreviousLine != null) { builder = new StringBuilder(); @@ -113,10 +136,13 @@ public class VCardParser_V30 extends VCardParser_V21 { } else { if (mPreviousLine == null) { mPreviousLine = line; + if (builder != null) { + return builder.toString(); + } } else { String ret = mPreviousLine; mPreviousLine = line; - return ret; + return ret; } } } @@ -130,15 +156,16 @@ public class VCardParser_V30 extends VCardParser_V21 { * [group "."] "END" ":" "VCARD" 1*CRLF */ @Override - protected boolean readBeginVCard() throws IOException, VCardException { + protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { // TODO: vCard 3.0 supports group. - return super.readBeginVCard(); + return super.readBeginVCard(allowGarbage); } @Override - protected void readEndVCard() throws VCardException { + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { // TODO: vCard 3.0 supports group. - super.readEndVCard(); + super.readEndVCard(useCache, allowGarbage); } /** @@ -214,23 +241,6 @@ public class VCardParser_V30 extends VCardParser_V21 { throw new VCardException("AGENT in vCard 3.0 is not supported yet."); } - // vCard 3.0 supports "B" as BASE64 encoding. - @Override - protected void handlePropertyValue( - String propertyName, String propertyValue) throws - IOException, VCardException { - if (mEncoding != null && mEncoding.equalsIgnoreCase("B")) { - String result = getBase64(propertyValue); - if (mBuilder != null) { - ArrayList<String> v = new ArrayList<String>(); - v.add(result); - mBuilder.propertyValues(v); - } - } - - super.handlePropertyValue(propertyName, propertyValue); - } - /** * vCard 3.0 does not require two CRLF at the last of BASE64 data. * It only requires that data should be MIME-encoded. @@ -259,27 +269,38 @@ public class VCardParser_V30 extends VCardParser_V21 { } /** - * Return unescapeText(text). - * In vCard 3.0, 8bit text is always encoded. - */ - @Override - protected String maybeUnescapeText(String text) { - return unescapeText(text); - } - - /** * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") * ; \\ encodes \, \n or \N encodes newline * ; \; encodes ;, \, encodes , - */ + * + * Note: Apple escape ':' into '\:' while does not escape '\' + */ @Override - protected String unescapeText(String text) { - // In String#replaceAll(), "\\\\" means single slash. - return text.replaceAll("\\\\;", ";") - .replaceAll("\\\\:", ":") - .replaceAll("\\\\,", ",") - .replaceAll("\\\\n", "\r\n") - .replaceAll("\\\\N", "\r\n") - .replaceAll("\\\\\\\\", "\\\\"); + protected String maybeUnescapeText(String text) { + StringBuilder builder = new StringBuilder(); + int length = text.length(); + for (int i = 0; i < length; i++) { + char ch = text.charAt(i); + if (ch == '\\' && i < length - 1) { + char next_ch = text.charAt(++i); + if (next_ch == 'n' || next_ch == 'N') { + builder.append("\r\n"); + } else { + builder.append(next_ch); + } + } else { + builder.append(ch); + } + } + return builder.toString(); + } + + @Override + protected String maybeUnescape(char ch) { + if (ch == 'n' || ch == 'N') { + return "\r\n"; + } else { + return String.valueOf(ch); + } } } diff --git a/core/java/android/syncml/pim/vcard/VCardSourceDetector.java b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java new file mode 100644 index 000000000000..8c483912335b --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java @@ -0,0 +1,140 @@ +/* + * 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.syncml.pim.vcard; + +import android.syncml.pim.VBuilder; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Class which tries to detects the source of the vCard from its properties. + * Currently this implementation is very premature. + * @hide + */ +public class VCardSourceDetector implements VBuilder { + // Should only be used in package. + static final int TYPE_UNKNOWN = 0; + static final int TYPE_APPLE = 1; + static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones. + static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones. + static final int TYPE_WINDOWS_MOBILE_JP = 4; + // TODO: Excel, etc. + + private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList( + "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", + "X-ABADR", "X-ABUID")); + + private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( + "X-GNO", "X-GN", "X-REDUCTION")); + + private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( + "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); + + // Note: these signes appears before the signs of the other type (e.g. "X-GN"). + // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. + private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList( + "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", + "X-SD-DESCRIPTION")); + private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; + + private int mType = TYPE_UNKNOWN; + // Some mobile phones (like FOMA) tells us the charset of the data. + private boolean mNeedParseSpecifiedCharset; + private String mSpecifiedCharset; + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void startProperty() { + mNeedParseSpecifiedCharset = false; + } + + public void endProperty() { + } + + public void endRecord() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { + mType = TYPE_FOMA; + mNeedParseSpecifiedCharset = true; + return; + } + if (mType != TYPE_UNKNOWN) { + return; + } + if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_WINDOWS_MOBILE_JP; + } else if (FOMA_SIGNS.contains(name)) { + mType = TYPE_FOMA; + } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_JAPANESE_MOBILE_PHONE; + } else if (APPLE_SIGNS.contains(name)) { + mType = TYPE_APPLE; + } + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List<String> values) { + if (mNeedParseSpecifiedCharset && values.size() > 0) { + mSpecifiedCharset = values.get(0); + } + } + + int getType() { + return mType; + } + + /** + * Return charset String guessed from the source's properties. + * This method must be called after parsing target file(s). + * @return Charset String. Null is returned if guessing the source fails. + */ + public String getEstimatedCharset() { + if (mSpecifiedCharset != null) { + return mSpecifiedCharset; + } + switch (mType) { + case TYPE_WINDOWS_MOBILE_JP: + case TYPE_FOMA: + case TYPE_JAPANESE_MOBILE_PHONE: + return "SHIFT_JIS"; + case TYPE_APPLE: + return "UTF-8"; + default: + return null; + } + } +} diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java index 9bafa32e42a9..de0587ab7366 100644 --- a/core/java/android/test/AndroidTestCase.java +++ b/core/java/android/test/AndroidTestCase.java @@ -16,12 +16,14 @@ package android.test; +import android.content.ContentValues; import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import junit.framework.TestCase; import java.lang.reflect.Field; -import junit.framework.TestCase; - /** * Extend this if you need to access Resources or other things that depend on Activity Context. */ @@ -53,6 +55,72 @@ public class AndroidTestCase extends TestCase { } /** + * Asserts that launching a given activity is protected by a particular permission by + * attempting to start the activity and validating that a {@link SecurityException} + * is thrown that mentions the permission in its error message. + * + * Note that an instrumentation isn't needed because all we are looking for is a security error + * and we don't need to wait for the activity to launch and get a handle to the activity. + * + * @param packageName The package name of the activity to launch. + * @param className The class of the activity to launch. + * @param permission The name of the permission. + */ + public void assertActivityRequiresPermission( + String packageName, String className, String permission) { + final Intent intent = new Intent(); + intent.setClassName(packageName, className); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + getContext().startActivity(intent); + fail("expected security exception for " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + + + /** + * Asserts that reading from the content uri requires a particular permission by querying the + * uri and ensuring a {@link SecurityException} is thrown mentioning the particular permission. + * + * @param uri The uri that requires a permission to query. + * @param permission The permission that should be required. + */ + public void assertReadingContentUriRequiresPermission(Uri uri, String permission) { + try { + getContext().getContentResolver().query(uri, null, null, null, null); + fail("expected SecurityException requiring " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + + /** + * Asserts that writing to the content uri requires a particular permission by inserting into + * the uri and ensuring a {@link SecurityException} is thrown mentioning the particular + * permission. + * + * @param uri The uri that requires a permission to query. + * @param permission The permission that should be required. + */ + public void assertWritingContentUriRequiresPermission(Uri uri, String permission) { + try { + getContext().getContentResolver().insert(uri, new ContentValues()); + fail("expected SecurityException requiring " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + + /** * This function is called by various TestCase implementations, at tearDown() time, in order * to scrub out any class variables. This protects against memory leaks in the case where a * test case creates a non-static inner class (thus referencing the test case) and gives it to diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java index 470ab0d9a48e..2145d7cc7891 100644 --- a/core/java/android/test/InstrumentationTestCase.java +++ b/core/java/android/test/InstrumentationTestCase.java @@ -241,7 +241,13 @@ public class InstrumentationTestCase extends TestCase { try { final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key); final int keyCode = keyCodeField.getInt(null); - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } catch (NoSuchFieldException e) { Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key); break; @@ -266,7 +272,13 @@ public class InstrumentationTestCase extends TestCase { final Instrumentation instrumentation = getInstrumentation(); for (int i = 0; i < count; i++) { - instrumentation.sendKeyDownUpSync(keys[i]); + try { + instrumentation.sendKeyDownUpSync(keys[i]); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } instrumentation.waitForIdleSync(); @@ -292,7 +304,13 @@ public class InstrumentationTestCase extends TestCase { final int keyCount = keys[i]; final int keyCode = keys[i + 1]; for (int j = 0; j < keyCount; j++) { - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } } diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java index 27c703f1f083..9045c09fb660 100644 --- a/core/java/android/text/LoginFilter.java +++ b/core/java/android/text/LoginFilter.java @@ -49,10 +49,6 @@ public abstract class LoginFilter implements InputFilter { */ public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - char[] out = new char[end - start]; // reserve enough space for whole string - int outidx = 0; - boolean changed = false; - onStart(); // Scan through beginning characters in dest, calling onInvalidCharacter() @@ -63,14 +59,26 @@ public abstract class LoginFilter implements InputFilter { } // Scan through changed characters rejecting disallowed chars + SpannableStringBuilder modification = null; + int modoff = 0; + for (int i = start; i < end; i++) { char c = source.charAt(i); if (isAllowed(c)) { - // Character allowed. Add it to the sequence. - out[outidx++] = c; + // Character allowed. + modoff++; } else { - if (mAppendInvalid) out[outidx++] = c; - else changed = true; // we changed the original string + if (mAppendInvalid) { + modoff++; + } else { + if (modification == null) { + modification = new SpannableStringBuilder(source, start, end); + modoff = i - start; + } + + modification.delete(modoff, modoff + 1); + } + onInvalidCharacter(c); } } @@ -84,20 +92,9 @@ public abstract class LoginFilter implements InputFilter { onStop(); - if (!changed) { - return null; - } - - String s = new String(out, 0, outidx); - - if (source instanceof Spanned) { - SpannableString sp = new SpannableString(s); - TextUtils.copySpansFrom((Spanned) source, - start, end, null, sp, 0); - return sp; - } else { - return s; - } + // Either returns null if we made no changes, + // or what we wanted to change it to if there were changes. + return modification; } /** diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 5b4c3802b3ce..53096dddefb5 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -916,6 +916,17 @@ public class TextUtils { sp.setSpan(o, p.readInt(), p.readInt(), p.readInt()); } + /** + * Copies the spans from the region <code>start...end</code> in + * <code>source</code> to the region + * <code>destoff...destoff+end-start</code> in <code>dest</code>. + * Spans in <code>source</code> that begin before <code>start</code> + * or end after <code>end</code> but overlap this range are trimmed + * as if they began at <code>start</code> or ended at <code>end</code>. + * + * @throws IndexOutOfBoundsException if any of the copied spans + * are out of range in <code>dest</code>. + */ public static void copySpansFrom(Spanned source, int start, int end, Class kind, Spannable dest, int destoff) { diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 0dc96c369b78..3d10f171538c 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -242,7 +242,7 @@ public class DateFormat { /** * Returns a {@link java.text.DateFormat} object that can format the time according - * to the current user preference. + * to the current locale and the user's 12-/24-hour clock preference. * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the time. */ @@ -260,46 +260,88 @@ public class DateFormat { } /** - * Returns a {@link java.text.DateFormat} object that can format the date according - * to the current user preference. + * Returns a {@link java.text.DateFormat} object that can format the date + * in short form (such as 12/31/1999) according + * to the current locale and the user's date-order preference. * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the date. */ public static final java.text.DateFormat getDateFormat(Context context) { - String value = getDateFormatString(context); + String value = Settings.System.getString(context.getContentResolver(), + Settings.System.DATE_FORMAT); + + return getDateFormatForSetting(context, value); + } + + /** + * Returns a {@link java.text.DateFormat} object to format the date + * as if the date format setting were set to <code>value</code>, + * including null to use the locale's default format. + * @param context the application context + * @param value the date format setting string to interpret for + * the current locale + * @hide + */ + public static java.text.DateFormat getDateFormatForSetting(Context context, + String value) { + if (value != null) { + int month = value.indexOf('M'); + int day = value.indexOf('d'); + int year = value.indexOf('y'); + + if (month >= 0 && day >= 0 && year >= 0) { + String template = context.getString(R.string.numeric_date_template); + if (year < month) { + if (month < day) { + value = String.format(template, "yyyy", "MM", "dd"); + } else { + value = String.format(template, "yyyy", "dd", "MM"); + } + } else if (month < day) { + if (day < year) { + value = String.format(template, "MM", "dd", "yyyy"); + } else { // unlikely + value = String.format(template, "MM", "yyyy", "dd"); + } + } else { // day < month + if (month < year) { + value = String.format(template, "dd", "MM", "yyyy"); + } else { // unlikely + value = String.format(template, "dd", "yyyy", "MM"); + } + } + + return new java.text.SimpleDateFormat(value); + } + } + + /* + * The setting is not set; use the default. + * We use a resource string here instead of just DateFormat.SHORT + * so that we get a four-digit year instead a two-digit year. + */ + value = context.getString(R.string.numeric_date_format); return new java.text.SimpleDateFormat(value); } /** * Returns a {@link java.text.DateFormat} object that can format the date - * in long form (such as December 31, 1999) based on user preference. + * in long form (such as December 31, 1999) for the current locale. * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ public static final java.text.DateFormat getLongDateFormat(Context context) { - String value = getDateFormatString(context); - if (value.indexOf('M') < value.indexOf('d')) { - value = context.getString(R.string.full_date_month_first); - } else { - value = context.getString(R.string.full_date_day_first); - } - return new java.text.SimpleDateFormat(value); + return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG); } /** * Returns a {@link java.text.DateFormat} object that can format the date - * in medium form (such as Dec. 31, 1999) based on user preference. + * in medium form (such as Dec. 31, 1999) for the current locale. * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ public static final java.text.DateFormat getMediumDateFormat(Context context) { - String value = getDateFormatString(context); - if (value.indexOf('M') < value.indexOf('d')) { - value = context.getString(R.string.medium_date_month_first); - } else { - value = context.getString(R.string.medium_date_day_first); - } - return new java.text.SimpleDateFormat(value); + return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM); } /** @@ -338,6 +380,12 @@ public class DateFormat { } private static String getDateFormatString(Context context) { + java.text.DateFormat df; + df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT); + if (df instanceof SimpleDateFormat) { + return ((SimpleDateFormat) df).toPattern(); + } + String value = Settings.System.getString(context.getContentResolver(), Settings.System.DATE_FORMAT); if (value == null || value.length() < 6) { diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 8a7cdd92950f..1a4eb699b56d 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -62,15 +62,6 @@ public class DateUtils com.android.internal.R.string.day_of_week_short_friday, com.android.internal.R.string.day_of_week_short_saturday, }; - private static final int[] sDaysShorter = new int[] { - com.android.internal.R.string.day_of_week_shorter_sunday, - com.android.internal.R.string.day_of_week_shorter_monday, - com.android.internal.R.string.day_of_week_shorter_tuesday, - com.android.internal.R.string.day_of_week_shorter_wednesday, - com.android.internal.R.string.day_of_week_shorter_thursday, - com.android.internal.R.string.day_of_week_shorter_friday, - com.android.internal.R.string.day_of_week_shorter_saturday, - }; private static final int[] sDaysShortest = new int[] { com.android.internal.R.string.day_of_week_shortest_sunday, com.android.internal.R.string.day_of_week_shortest_monday, @@ -80,6 +71,20 @@ public class DateUtils com.android.internal.R.string.day_of_week_shortest_friday, com.android.internal.R.string.day_of_week_shortest_saturday, }; + private static final int[] sMonthsStandaloneLong = new int [] { + com.android.internal.R.string.month_long_standalone_january, + com.android.internal.R.string.month_long_standalone_february, + com.android.internal.R.string.month_long_standalone_march, + com.android.internal.R.string.month_long_standalone_april, + com.android.internal.R.string.month_long_standalone_may, + com.android.internal.R.string.month_long_standalone_june, + com.android.internal.R.string.month_long_standalone_july, + com.android.internal.R.string.month_long_standalone_august, + com.android.internal.R.string.month_long_standalone_september, + com.android.internal.R.string.month_long_standalone_october, + com.android.internal.R.string.month_long_standalone_november, + com.android.internal.R.string.month_long_standalone_december, + }; private static final int[] sMonthsLong = new int [] { com.android.internal.R.string.month_long_january, com.android.internal.R.string.month_long_february, @@ -127,7 +132,7 @@ public class DateUtils com.android.internal.R.string.pm, }; private static Configuration sLastConfig; - private static String sStatusTimeFormat; + private static java.text.DateFormat sStatusTimeFormat; private static String sElapsedFormatMMSS; private static String sElapsedFormatHMMSS; @@ -142,6 +147,9 @@ public class DateUtils public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + /** + * This constant is actually the length of 364 days, not of a year! + */ public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52; // The following FORMAT_* symbols are used for specifying the format of @@ -171,8 +179,14 @@ public class DateUtils // Date and time format strings that are constant and don't need to be // translated. + /** + * This is not actually the preferred 24-hour date format in all locales. + */ public static final String HOUR_MINUTE_24 = "%H:%M"; public static final String MONTH_FORMAT = "%B"; + /** + * This is not actually a useful month name in all locales. + */ public static final String ABBREV_MONTH_FORMAT = "%b"; public static final String NUMERIC_MONTH_FORMAT = "%m"; public static final String MONTH_DAY_FORMAT = "%-d"; @@ -255,18 +269,15 @@ public class DateUtils * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. * @more * <p>e.g. "Su" or "Jan" - * <p>In some languages, the results returned for LENGTH_SHORT may be the same as - * return for {@link #LENGTH_MEDIUM}. + * <p>In most languages, the results returned for LENGTH_SHORT will be the same as + * the results returned for {@link #LENGTH_MEDIUM}. */ public static final int LENGTH_SHORT = 30; /** * Request an even shorter abbreviated version of the name. - * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. - * @more - * <p>e.g. "M", "Tu", "Th" or "J" - * <p>In some languages, the results returned for LENGTH_SHORTEST may be the same as - * return for {@link #LENGTH_SHORTER}. + * Do not use this. Currently this will always return the same result + * as {@link #LENGTH_SHORT}. */ public static final int LENGTH_SHORTER = 40; @@ -275,8 +286,8 @@ public class DateUtils * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. * @more * <p>e.g. "S", "T", "T" or "J" - * <p>In some languages, the results returned for LENGTH_SHORTEST may be the same as - * return for {@link #LENGTH_SHORTER}. + * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as + * the results returned for {@link #LENGTH_SHORT}. */ public static final int LENGTH_SHORTEST = 50; @@ -284,9 +295,12 @@ public class DateUtils * Return a string for the day of the week. * @param dayOfWeek One of {@link Calendar#SUNDAY Calendar.SUNDAY}, * {@link Calendar#MONDAY Calendar.MONDAY}, etc. - * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, {@link #LENGTH_SHORTER} - * or {@link #LENGTH_SHORTEST}. For forward compatibility, anything else - * will return the same as {#LENGTH_MEDIUM}. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, + * {@link #LENGTH_MEDIUM}, or {@link #LENGTH_SHORTEST}. + * Note that in most languages, {@link #LENGTH_SHORT} + * will return the same as {@link #LENGTH_MEDIUM}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds. */ public static String getDayOfWeekString(int dayOfWeek, int abbrev) { @@ -295,7 +309,7 @@ public class DateUtils case LENGTH_LONG: list = sDaysLong; break; case LENGTH_MEDIUM: list = sDaysMedium; break; case LENGTH_SHORT: list = sDaysShort; break; - case LENGTH_SHORTER: list = sDaysShorter; break; + case LENGTH_SHORTER: list = sDaysShort; break; case LENGTH_SHORTEST: list = sDaysShortest; break; default: list = sDaysMedium; break; } @@ -316,13 +330,14 @@ public class DateUtils } /** - * Return a localized string for the day of the week. + * Return a localized string for the month of the year. * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, * {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. - * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, {@link #LENGTH_SHORTER} - * or {@link #LENGTH_SHORTEST}. For forward compatibility, anything else - * will return the same as {#LENGTH_MEDIUM}. - * @return Localized day of the week. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, + * or {@link #LENGTH_SHORTEST}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. + * @return Localized month of the year. */ public static String getMonthString(int month, int abbrev) { // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. @@ -344,6 +359,40 @@ public class DateUtils } /** + * Return a localized string for the month of the year, for + * contexts where the month is not formatted together with + * a day of the month. + * + * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, + * {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, + * or {@link #LENGTH_SHORTEST}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. + * @return Localized month of the year. + * @hide Pending API council approval + */ + public static String getStandaloneMonthString(int month, int abbrev) { + // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. + // This is a shortcut to not spam the translators with too many variations + // of the same string. If we find that in a language the distinction + // is necessary, we can can add more without changing this API. + int[] list; + switch (abbrev) { + case LENGTH_LONG: list = sMonthsStandaloneLong; + break; + case LENGTH_MEDIUM: list = sMonthsMedium; break; + case LENGTH_SHORT: list = sMonthsMedium; break; + case LENGTH_SHORTER: list = sMonthsMedium; break; + case LENGTH_SHORTEST: list = sMonthsShortest; break; + default: list = sMonthsMedium; break; + } + + Resources r = Resources.getSystem(); + return r.getString(list[month - Calendar.JANUARY]); + } + + /** * Returns a string describing the elapsed time since startTime. * @param startTime some time in the past. * @return a String object containing the elapsed time. @@ -572,7 +621,7 @@ public class DateUtils Configuration cfg = r.getConfiguration(); if (sLastConfig == null || !sLastConfig.equals(cfg)) { sLastConfig = cfg; - sStatusTimeFormat = r.getString(com.android.internal.R.string.status_bar_time_format); + sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT); sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss); sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss); } @@ -586,7 +635,7 @@ public class DateUtils */ public static final CharSequence timeString(long millis) { initFormatStrings(); - return DateFormat.format(sStatusTimeFormat, millis); + return sStatusTimeFormat.format(millis); } /** @@ -1066,7 +1115,9 @@ public class DateUtils * * <p> * If FORMAT_CAP_AMPM is set and 12-hour time is used, then the "AM" - * and "PM" are capitalized. + * and "PM" are capitalized. You should not use this flag + * because in some locales these terms cannot be capitalized, and in + * many others it doesn't make sense to do so even though it is possible. * * <p> * If FORMAT_NO_NOON is set and 12-hour time is used, then "12pm" is @@ -1074,15 +1125,19 @@ public class DateUtils * * <p> * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Noon" is - * shown instead of "noon". + * shown instead of "noon". You should probably not use this flag + * because in many locales it will not make sense to capitalize + * the term. * * <p> * If FORMAT_NO_MIDNIGHT is set and 12-hour time is used, then "12am" is * shown instead of "midnight". * * <p> - * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Midnight" is - * shown instead of "midnight". + * If FORMAT_CAP_MIDNIGHT is set and 12-hour time is used, then "Midnight" + * is shown instead of "midnight". You should probably not use this + * flag because in many locales it will not make sense to capitalize + * the term. * * <p> * If FORMAT_12HOUR is set and the time is shown, then the time is @@ -1224,8 +1279,8 @@ public class DateUtils use24Hour = DateFormat.is24HourFormat(context); } if (use24Hour) { - startTimeFormat = HOUR_MINUTE_24; - endTimeFormat = HOUR_MINUTE_24; + startTimeFormat = endTimeFormat = + res.getString(com.android.internal.R.string.hour_minute_24); } else { boolean abbrevTime = (flags & (FORMAT_ABBREV_TIME | FORMAT_ABBREV_ALL)) != 0; boolean capAMPM = (flags & FORMAT_CAP_AMPM) != 0; @@ -1392,7 +1447,8 @@ public class DateUtils if (numericDate) { monthFormat = NUMERIC_MONTH_FORMAT; } else if (abbrevMonth) { - monthFormat = ABBREV_MONTH_FORMAT; + monthFormat = + res.getString(com.android.internal.R.string.short_format_month); } else { monthFormat = MONTH_FORMAT; } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 1b30aa0a218c..367b26ce45d3 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -59,9 +59,15 @@ public final class Formatter { result = result / 1024; } if (result < 100) { - return String.format("%.2f%s", result, context.getText(suffix).toString()); + String value = String.format("%.2f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } - return String.format("%.0f%s", result, context.getText(suffix).toString()); + String value = String.format("%.0f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } /** diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index daa99c250bfa..8eae111ee5d3 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -135,6 +135,7 @@ public class Time { private static Locale sLocale; private static String[] sShortMonths; private static String[] sLongMonths; + private static String[] sLongStandaloneMonths; private static String[] sShortWeekdays; private static String[] sLongWeekdays; private static String sTimeOnlyFormat; @@ -321,6 +322,20 @@ public class Time { r.getString(com.android.internal.R.string.month_long_november), r.getString(com.android.internal.R.string.month_long_december), }; + sLongStandaloneMonths = new String[] { + r.getString(com.android.internal.R.string.month_long_standalone_january), + r.getString(com.android.internal.R.string.month_long_standalone_february), + r.getString(com.android.internal.R.string.month_long_standalone_march), + r.getString(com.android.internal.R.string.month_long_standalone_april), + r.getString(com.android.internal.R.string.month_long_standalone_may), + r.getString(com.android.internal.R.string.month_long_standalone_june), + r.getString(com.android.internal.R.string.month_long_standalone_july), + r.getString(com.android.internal.R.string.month_long_standalone_august), + r.getString(com.android.internal.R.string.month_long_standalone_september), + r.getString(com.android.internal.R.string.month_long_standalone_october), + r.getString(com.android.internal.R.string.month_long_standalone_november), + r.getString(com.android.internal.R.string.month_long_standalone_december), + }; sShortWeekdays = new String[] { r.getString(com.android.internal.R.string.day_of_week_medium_sunday), r.getString(com.android.internal.R.string.day_of_week_medium_monday), @@ -438,6 +453,7 @@ public class Time { * * @param s the string to parse * @return true if the resulting time value is in UTC time + * @throws android.util.TimeFormatException if s cannot be parsed. */ public boolean parse3339(String s) { if (nativeParse3339(s)) { diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java index b121e608b5b5..584e83f53e53 100644 --- a/core/java/android/text/method/DialerKeyListener.java +++ b/core/java/android/text/method/DialerKeyListener.java @@ -106,7 +106,7 @@ public class DialerKeyListener extends NumberKeyListener */ public static final char[] CHARACTERS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*', - '+', '-', '(', ')', ',', '/', 'N', '.', ' ' + '+', '-', '(', ')', ',', '/', 'N', '.', ' ', ';' }; private static DialerKeyListener sInstance; diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index f2fb9cb68d7f..dfc16f5adf7f 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -81,6 +81,12 @@ public class Touch { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: + ds = buffer.getSpans(0, buffer.length(), DragState.class); + + for (int i = 0; i < ds.length; i++) { + buffer.removeSpan(ds[i]); + } + buffer.setSpan(new DragState(event.getX(), event.getY(), widget.getScrollX(), widget.getScrollY()), 0, 0, Spannable.SPAN_MARK_MARK); diff --git a/core/java/android/util/CharsetUtils.java b/core/java/android/util/CharsetUtils.java index 75530296d2d9..9d91acaa5f87 100644 --- a/core/java/android/util/CharsetUtils.java +++ b/core/java/android/util/CharsetUtils.java @@ -142,20 +142,25 @@ public final class CharsetUtils { /** * Returns whether the given character set name indicates the Shift-JIS - * encoding. + * encoding. Returns false if the name is null. * * @param charsetName the character set name * @return {@code true} if the name corresponds to Shift-JIS or * {@code false} if not */ private static boolean isShiftJis(String charsetName) { - if (charsetName.length() != 9) { - // Bail quickly if the length doesn't match. + // Bail quickly if the length doesn't match. + if (charsetName == null) { + return false; + } + int length = charsetName.length(); + if (length != 4 && length != 9) { return false; } return charsetName.equalsIgnoreCase("shift_jis") - || charsetName.equalsIgnoreCase("shift-jis"); + || charsetName.equalsIgnoreCase("shift-jis") + || charsetName.equalsIgnoreCase("sjis"); } /** diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index e4dd020e1a81..4179edbe7f9c 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,6 +16,8 @@ package android.util; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.os.*; @@ -35,8 +37,7 @@ public class DisplayMetrics { * The device's density. * @hide */ - public static final int DEVICE_DENSITY = SystemProperties.getInt("ro.sf.lcd_density", - DEFAULT_DENSITY); + public static final int DEVICE_DENSITY = getDeviceDensity(); /** * The absolute width of the display in pixels. @@ -101,22 +102,83 @@ public class DisplayMetrics { } /** - * Set the display metrics' density and update parameters depend on it. - * @hide + * 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 updateDensity(float newDensity) { - float ratio = newDensity / density; - density = newDensity; - scaledDensity = density; - widthPixels *= ratio; - heightPixels *= ratio; - xdpi *= ratio; - ydpi *= ratio; + 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. + 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) { + // 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 + ", xdpi=" + xdpi + ", ydpi=" + ydpi + "}"; } + + private static int getDeviceDensity() { + // qemu.sf.lcd_density can be used to override ro.sf.lcd_density + // when running in the emulator, allowing for dynamic configurations. + // The reason for this is that ro.sf.lcd_density is write-once and is + // set by the init process when it parses build.prop before anything else. + return SystemProperties.getInt("qemu.sf.lcd_density", + SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY)); + } } diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java new file mode 100644 index 000000000000..d90045f8625c --- /dev/null +++ b/core/java/android/util/LongSparseArray.java @@ -0,0 +1,342 @@ +/* + * 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.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseArrays map longs to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Longs to Objects. + * + * @hide + */ +public class LongSparseArray<E> { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public LongSparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public LongSparseArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new long[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or <code>null</code> + * if no such mapping has been made. + */ + public E get(long key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(long key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(long key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(long)}. + */ + public void remove(long key) { + delete(key); + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + long[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(long key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public long keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, sets a new + * value for the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(long key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(long key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(long[] a, int start, int len, long key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private long[] mKeys; + private Object[] mValues; + private int mSize; +}
\ No newline at end of file diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 23f3e3c3ece9..1e558be1ccc7 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -198,6 +198,7 @@ public class GestureDetector { private int mTouchSlopSquare; private int mDoubleTapSlopSquare; private int mMinimumFlingVelocity; + private int mMaximumFlingVelocity; private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); @@ -361,11 +362,13 @@ public class GestureDetector { doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); //noinspection deprecation mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); } else { final ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); doubleTapSlop = configuration.getScaledDoubleTapSlop(); mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity(); } mTouchSlopSquare = touchSlop * touchSlop; mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; @@ -505,7 +508,7 @@ public class GestureDetector { // A fling must travel the minimum tap distance final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); final float velocityY = velocityTracker.getYVelocity(); final float velocityX = velocityTracker.getXVelocity(); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 86261c4a7edb..a224ed306b0a 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -59,32 +59,32 @@ public final class MotionEvent implements Parcelable { public static final int ACTION_OUTSIDE = 4; private static final boolean TRACK_RECYCLED_LOCATION = false; - + /** * Flag indicating the motion event intersected the top edge of the screen. */ public static final int EDGE_TOP = 0x00000001; - + /** * Flag indicating the motion event intersected the bottom edge of the screen. */ public static final int EDGE_BOTTOM = 0x00000002; - + /** * Flag indicating the motion event intersected the left edge of the screen. */ public static final int EDGE_LEFT = 0x00000004; - + /** * Flag indicating the motion event intersected the right edge of the screen. */ public static final int EDGE_RIGHT = 0x00000008; - + static private final int MAX_RECYCLED = 10; static private Object gRecyclerLock = new Object(); static private int gRecyclerUsed = 0; static private MotionEvent gRecyclerTop = null; - + private long mDownTime; private long mEventTime; private int mAction; @@ -109,7 +109,7 @@ public final class MotionEvent implements Parcelable { private MotionEvent() { } - + static private MotionEvent obtain() { synchronized (gRecyclerLock) { if (gRecyclerTop == null) { @@ -123,26 +123,26 @@ public final class MotionEvent implements Parcelable { return ev; } } - + /** * Create a new MotionEvent, filling in all of the basic values that * define the motion. - * - * @param downTime The time (in ms) when the user originally pressed down to start + * + * @param downTime The time (in ms) when the user originally pressed down to start * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. - * @param eventTime The the time (in ms) when this specific event was generated. This + * @param eventTime The the time (in ms) when this specific event was generated. This * must be obtained from {@link SystemClock#uptimeMillis()}. * @param action The kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or * {@link #ACTION_CANCEL}. * @param x The X coordinate of this event. * @param y The Y coordinate of this event. - * @param pressure The current pressure of this event. The pressure generally - * ranges from 0 (no pressure at all) to 1 (normal pressure), however - * values higher than 1 may be generated depending on the calibration of + * @param pressure The current pressure of this event. The pressure generally + * ranges from 0 (no pressure at all) to 1 (normal pressure), however + * values higher than 1 may be generated depending on the calibration of * the input device. * @param size A scaled value of the approximate size of the area being pressed when - * touched with the finger. The actual value in pixels corresponding to the finger + * touched with the finger. The actual value in pixels corresponding to the finger * touch is normalized with a device specific range of values * and scaled to a value between 0 and 1. * @param metaState The state of any meta / modifier keys that were in effect when @@ -174,15 +174,15 @@ public final class MotionEvent implements Parcelable { return ev; } - + /** * Create a new MotionEvent, filling in a subset of the basic motion * values. Those not specified here are: device id (always 0), pressure * and size (always 1), x and y precision (always 1), and edgeFlags (always 0). - * - * @param downTime The time (in ms) when the user originally pressed down to start + * + * @param downTime The time (in ms) when the user originally pressed down to start * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. - * @param eventTime The the time (in ms) when this specific event was generated. This + * @param eventTime The the time (in ms) when this specific event was generated. This * must be obtained from {@link SystemClock#uptimeMillis()}. * @param action The kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or @@ -212,27 +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; - history[i + 2] *= scale; - history[i + 3] *= scale; - } + 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) } } } @@ -265,7 +285,7 @@ public final class MotionEvent implements Parcelable { } return ev; } - + /** * Recycle the MotionEvent, to be re-used by a later caller. After calling * this function you must not ever touch the event again. @@ -291,7 +311,7 @@ public final class MotionEvent implements Parcelable { } } } - + /** * Return the kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or @@ -302,8 +322,8 @@ public final class MotionEvent implements Parcelable { } /** - * Returns the time (in ms) when the user originally pressed down to start - * a stream of position events. + * Returns the time (in ms) when the user originally pressed down to start + * a stream of position events. */ public final long getDownTime() { return mDownTime; @@ -317,25 +337,25 @@ public final class MotionEvent implements Parcelable { } /** - * Returns the X coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * Returns the X coordinate of this event. Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. */ public final float getX() { return mX; } /** - * Returns the Y coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * Returns the Y coordinate of this event. Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. */ public final float getY() { return mY; } /** - * Returns the current pressure of this event. The pressure generally - * ranges from 0 (no pressure at all) to 1 (normal pressure), however - * values higher than 1 may be generated depending on the calibration of + * Returns the current pressure of this event. The pressure generally + * ranges from 0 (no pressure at all) to 1 (normal pressure), however + * values higher than 1 may be generated depending on the calibration of * the input device. */ public final float getPressure() { @@ -344,9 +364,9 @@ public final class MotionEvent implements Parcelable { /** * Returns a scaled value of the approximate size, of the area being pressed when - * touched with the finger. The actual value in pixels corresponding to the finger + * touched with the finger. The actual value in pixels corresponding to the finger * touch is normalized with the device specific range of values - * and scaled to a value between 0 and 1. The value of size can be used to + * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ public final float getSize() { @@ -396,7 +416,7 @@ public final class MotionEvent implements Parcelable { public final float getXPrecision() { return mXPrecision; } - + /** * Return the precision of the Y coordinates being reported. You can * multiple this number with {@link #getY} to find the actual hardware @@ -406,89 +426,89 @@ public final class MotionEvent implements Parcelable { public final float getYPrecision() { return mYPrecision; } - + /** * Returns the number of historical points in this event. These are * movements that have occurred between this event and the previous event. * This only applies to ACTION_MOVE events -- all other actions will have * a size of 0. - * + * * @return Returns the number of historical points in the event. */ public final int getHistorySize() { return mNumHistory; } - + /** * Returns the time that a historical movement occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getEventTime */ public final long getHistoricalEventTime(int pos) { return mHistoryTimes[pos]; } - + /** * Returns a historical X coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getX */ public final float getHistoricalX(int pos) { return mHistory[pos*4]; } - + /** * Returns a historical Y coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getY */ public final float getHistoricalY(int pos) { return mHistory[pos*4 + 1]; } - + /** * Returns a historical pressure coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getPressure */ public final float getHistoricalPressure(int pos) { return mHistory[pos*4 + 2]; } - + /** * Returns a historical size coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getSize */ public final float getHistoricalSize(int pos) { return mHistory[pos*4 + 3]; } - + /** * Return the id for the device that this event came from. An id of * zero indicates that the event didn't come from a physical device; other @@ -497,12 +517,12 @@ public final class MotionEvent implements Parcelable { public final int getDeviceId() { return mDeviceId; } - + /** * Returns a bitfield indicating which edges, if any, where touched by this - * MotionEvent. For touch events, clients can use this to determine if the - * user's finger was touching the edge of the display. - * + * MotionEvent. For touch events, clients can use this to determine if the + * user's finger was touching the edge of the display. + * * @see #EDGE_LEFT * @see #EDGE_TOP * @see #EDGE_RIGHT @@ -511,12 +531,12 @@ public final class MotionEvent implements Parcelable { public final int getEdgeFlags() { return mEdgeFlags; } - + /** * Sets the bitfield indicating which edges, if any, where touched by this - * MotionEvent. - * + * MotionEvent. + * * @see #getEdgeFlags() */ public final void setEdgeFlags(int flags) { @@ -548,11 +568,11 @@ public final class MotionEvent implements Parcelable { pos[i+1] += deltaY; } } - + /** * Set this event's location. Applies {@link #offsetLocation} with a * delta from the current location to the given new location. - * + * * @param x New absolute X location. * @param y New absolute Y location. */ @@ -563,13 +583,13 @@ public final class MotionEvent implements Parcelable { offsetLocation(deltaX, deltaY); } } - + /** * Add a new movement to the batch of movements in this event. The event's * current location, position and size is updated to the new values. In * the future, the current values in the event will be added to a list of * historic values. - * + * * @param x The new X position. * @param y The new Y position. * @param pressure The new pressure. @@ -599,16 +619,16 @@ public final class MotionEvent implements Parcelable { mHistoryTimes = historyTimes = newHistoryTimes; } } - + historyTimes[N] = mEventTime; - + final int pos = N*4; history[pos] = mX; history[pos+1] = mY; history[pos+2] = mPressure; history[pos+3] = mSize; mNumHistory = N+1; - + mEventTime = eventTime; mX = mRawX = x; mY = mRawY = y; @@ -616,7 +636,7 @@ public final class MotionEvent implements Parcelable { mSize = size; mMetaState |= metaState; } - + @Override public String toString() { return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this)) diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 3d023f7a9691..45b0f0a79082 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -17,6 +17,8 @@ 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; @@ -100,6 +102,8 @@ public class SurfaceView extends View { static final int KEEP_SCREEN_ON_MSG = 1; static final int GET_NEW_SURFACE_MSG = 2; + int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + boolean mIsCreating = false; final Handler mHandler = new Handler() { @@ -135,28 +139,21 @@ public class SurfaceView extends View { int mFormat = -1; int mType = -1; final Rect mSurfaceFrame = new Rect(); - private final float mAppScale; - private final float mAppScaleInverted; + private Translator mTranslator; public SurfaceView(Context context) { super(context); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } /** @@ -259,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 && mAppScale != 1.0f) { + if (mRequestedWidth < 0 && mTranslator != null) { MotionEvent scaledBack = MotionEvent.obtain(event); - scaledBack.scale(mAppScale); + scaledBack.scale(mTranslator.applicationScale); try { return super.dispatchTouchEvent(scaledBack); } finally { @@ -285,20 +282,33 @@ public class SurfaceView extends View { super.dispatchDraw(canvas); } + /** + * Hack to allow special layering of windows. The type is one of the + * types in WindowManager.LayoutParams. This is a hack so: + * @hide + */ + public void setWindowType(int type) { + mWindowType = type; + } + private void updateWindow(boolean force) { if (!mHaveFrame) { return; } + 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) { - myWidth *= mAppScale; - myHeight *= mAppScale; + // 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; } getLocationInWindow(mLocation); @@ -316,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]; @@ -326,23 +336,30 @@ public class SurfaceView extends View { mFormat = mRequestedFormat; mType = mRequestedType; - // Scaling window's layout here beause mLayout is not used elsewhere. - mLayout.x = (int) (mLeft * mAppScale); - mLayout.y = (int) (mTop * mAppScale); - mLayout.width = (int) (getWidth() * mAppScale); - mLayout.height = (int) (getHeight() * mAppScale); + // 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; if (mWindow == null) { mWindow = new MyWindow(this); - mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + mLayout.type = mWindowType; mLayout.gravity = Gravity.LEFT|Gravity.TOP; mSession.add(mWindow, mLayout, mVisible ? VISIBLE : GONE, mContentInsets); @@ -356,15 +373,12 @@ public class SurfaceView extends View { mSurfaceLock.lock(); mDrawingStopped = !visible; + final int relayoutResult = mSession.relayout( mWindow, mLayout, mWidth, mHeight, visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurface); - mContentInsets.scale(mAppScaleInverted); - mVisibleInsets.scale(mAppScaleInverted); - mWinFrame.scale(mAppScaleInverted); - if (localLOGV) Log.i(TAG, "New surface: " + mSurface + ", vis=" + visible + ", frame=" + mWinFrame); mSurfaceFrame.left = 0; @@ -433,24 +447,14 @@ public class SurfaceView extends View { private static class MyWindow extends IWindow.Stub { private final WeakReference<SurfaceView> mSurfaceView; - private final float mAppScale; - private final float mAppScaleInverted; public MyWindow(SurfaceView surfaceView) { mSurfaceView = new WeakReference<SurfaceView>(surfaceView); - mAppScale = surfaceView.getContext().getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw) { SurfaceView surfaceView = mSurfaceView.get(); - float scale = mAppScaleInverted; - w *= scale; - h *= scale; - coveredInsets.scale(scale); - visibleInsets.scale(scale); - if (surfaceView != null) { if (localLOGV) Log.v( "SurfaceView", surfaceView + " got resized: w=" + @@ -613,7 +617,6 @@ public class SurfaceView extends View { Canvas c = null; if (!mDrawingStopped && mWindow != null) { Rect frame = dirty != null ? dirty : mSurfaceFrame; - frame.scale(mAppScale); try { c = mSurface.lockCanvas(frame); } catch (Exception e) { diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index c708f547642a..5d89c46a3db1 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -165,7 +165,17 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { pastTime[i] = 0; } } - + + /** + * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum + * velocity of Float.MAX_VALUE. + * + * @see #computeCurrentVelocity(int, float) + */ + public void computeCurrentVelocity(int units) { + computeCurrentVelocity(units, Float.MAX_VALUE); + } + /** * Compute the current velocity based on the points that have been * collected. Only call this when you actually want to retrieve velocity @@ -175,8 +185,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * * @param units The units you would like the velocity in. A value of 1 * provides pixels per millisecond, 1000 provides pixels per second, etc. + * @param maxVelocity The maximum velocity that can be computed by this method. + * This value must be declared in the same unit as the units parameter. This value + * must be positive. */ - public void computeCurrentVelocity(int units) { + public void computeCurrentVelocity(int units, float maxVelocity) { final float[] pastX = mPastX; final float[] pastY = mPastY; final long[] pastTime = mPastTime; @@ -210,8 +223,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { if (accumY == 0) accumY = vel; else accumY = (accumY + vel) * .5f; } - mXVelocity = accumX; - mYVelocity = accumY; + mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); + mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" + mXVelocity + " N=" + N); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9e709cfd1f47..ff8868bb0f2f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,6 +16,9 @@ package android.view; +import com.android.internal.R; +import com.android.internal.view.menu.MenuBuilder; + import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -25,12 +28,12 @@ import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Shader; -import android.graphics.Point; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -42,47 +45,47 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.AttributeSet; +import android.util.Config; import android.util.EventLog; import android.util.Log; -import android.util.SparseArray; -import android.util.Poolable; import android.util.Pool; -import android.util.Pools; +import android.util.Poolable; import android.util.PoolableManager; -import android.util.Config; +import android.util.Pools; +import android.util.SparseArray; import android.view.ContextMenu.ContextMenuInfo; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityEventSource; +import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; import android.widget.ScrollBarDrawable; -import com.android.internal.R; -import com.android.internal.view.menu.MenuBuilder; - +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.WeakHashMap; -import java.lang.ref.SoftReference; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; /** * <p> * This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and * event handling. View is the base class for <em>widgets</em>, which are - * used to create interactive UI components (buttons, text fields, etc.). The + * used to create interactive UI components (buttons, text fields, etc.). The * {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which * are invisible containers that hold other Views (or other ViewGroups) and define * their layout properties. * </p> * * <div class="special"> - * <p>For an introduction to using this class to develop your - * application's user interface, read the Developer Guide documentation on + * <p>For an introduction to using this class to develop your + * application's user interface, read the Developer Guide documentation on * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics - * include: + * include: * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a> * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a> @@ -93,7 +96,7 @@ import java.lang.reflect.InvocationTargetException; * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>. * </p> * </div> - * + * * <a name="Using"></a> * <h3>Using Views</h3> * <p> @@ -419,7 +422,7 @@ import java.lang.reflect.InvocationTargetException; * </p> * * <p> - * Note that the framework will not draw views that are not in the invalid region. + * Note that the framework will not draw views that are not in the invalid region. * </p> * * <p> @@ -535,25 +538,52 @@ import java.lang.reflect.InvocationTargetException; * take care of redrawing the appropriate views until the animation completes. * </p> * + * @attr ref android.R.styleable#View_background + * @attr ref android.R.styleable#View_clickable + * @attr ref android.R.styleable#View_contentDescription + * @attr ref android.R.styleable#View_drawingCacheQuality + * @attr ref android.R.styleable#View_duplicateParentState + * @attr ref android.R.styleable#View_id + * @attr ref android.R.styleable#View_fadingEdge + * @attr ref android.R.styleable#View_fadingEdgeLength * @attr ref android.R.styleable#View_fitsSystemWindows + * @attr ref android.R.styleable#View_isScrollContainer + * @attr ref android.R.styleable#View_focusable + * @attr ref android.R.styleable#View_focusableInTouchMode + * @attr ref android.R.styleable#View_hapticFeedbackEnabled + * @attr ref android.R.styleable#View_keepScreenOn + * @attr ref android.R.styleable#View_longClickable + * @attr ref android.R.styleable#View_minHeight + * @attr ref android.R.styleable#View_minWidth * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp + * @attr ref android.R.styleable#View_onClick + * @attr ref android.R.styleable#View_padding + * @attr ref android.R.styleable#View_paddingBottom + * @attr ref android.R.styleable#View_paddingLeft + * @attr ref android.R.styleable#View_paddingRight + * @attr ref android.R.styleable#View_paddingTop + * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_scrollX * @attr ref android.R.styleable#View_scrollY - * @attr ref android.R.styleable#View_scrollbarTrackHorizontal - * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarSize + * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars + * @attr ref android.R.styleable#View_scrollbarTrackHorizontal + * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack + * @attr ref android.R.styleable#View_soundEffectsEnabled + * @attr ref android.R.styleable#View_tag + * @attr ref android.R.styleable#View_visibility * * @see android.view.ViewGroup */ -public class View implements Drawable.Callback, KeyEvent.Callback { +public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { private static final boolean DBG = false; /** @@ -851,6 +881,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; /** + * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} + * should add all focusable Views regardless if they are focusable in touch mode. + */ + public static final int FOCUSABLES_ALL = 0x00000000; + + /** + * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} + * should add only Views focusable in touch mode. + */ + public static final int FOCUSABLES_TOUCH_MODE = 0x00000001; + + /** * Use with {@link #focusSearch}. Move focus to the previous selectable * item. */ @@ -1428,6 +1470,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback { static final int DIRTY_MASK = 0x00600000; /** + * Indicates whether the background is opaque. + * + * @hide + */ + static final int OPAQUE_BACKGROUND = 0x00800000; + + /** + * Indicates whether the scrollbars are opaque. + * + * @hide + */ + static final int OPAQUE_SCROLLBARS = 0x01000000; + + /** + * Indicates whether the view is opaque. + * + * @hide + */ + static final int OPAQUE_MASK = 0x01800000; + + /** * The parent this view is attached to. * {@hide} * @@ -1449,7 +1512,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { @ViewDebug.FlagToString(mask = LAYOUT_REQUIRED, equals = LAYOUT_REQUIRED, name = "LAYOUT_REQUIRED"), @ViewDebug.FlagToString(mask = DRAWING_CACHE_VALID, equals = DRAWING_CACHE_VALID, - name = "DRAWING_CACHE_VALID", outputIf = false), + name = "DRAWING_CACHE_INVALID", outputIf = false), @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "DRAWN", outputIf = true), @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "NOT_DRAWN", outputIf = false), @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY_OPAQUE, name = "DIRTY_OPAQUE"), @@ -1551,6 +1614,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { protected int mPaddingBottom; /** + * Briefly describes the view and is primarily used for accessibility support. + */ + private CharSequence mContentDescription; + + /** * Cache the paddingRight set by the user to append to the scrollbar's size. */ @ViewDebug.ExportedProperty @@ -1622,6 +1690,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { private int[] mDrawableState = null; private SoftReference<Bitmap> mDrawingCache; + private SoftReference<Bitmap> mUnscaledDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -1701,7 +1770,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; - mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED; + mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; ++sInstanceCount; } @@ -1762,7 +1831,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { int viewFlagMasks = 0; boolean setScrollContainer = false; - + int x = 0; int y = 0; @@ -1858,16 +1927,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback { viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK; } break; + case com.android.internal.R.styleable.View_contentDescription: + mContentDescription = a.getString(attr); + break; case com.android.internal.R.styleable.View_soundEffectsEnabled: if (!a.getBoolean(attr, true)) { 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) { @@ -1922,6 +1996,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mMinHeight = a.getDimensionPixelSize(attr, 0); break; case R.styleable.View_onClick: + if (context.isRestricted()) { + throw new IllegalStateException("The android:onClick attribute cannot " + + "be used within a restricted context"); + } + final String handlerName = a.getString(attr); if (handlerName != null) { setOnClickListener(new OnClickListener() { @@ -1990,7 +2069,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) { setScrollContainer(true); } - + + computeOpaqueFlags(); + a.recycle(); } @@ -2255,6 +2336,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * otherwise is returned. */ public boolean performClick() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); + if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); @@ -2272,6 +2355,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * otherwise is returned. */ public boolean performLongClick() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); + boolean handled = false; if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); @@ -2387,7 +2472,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (!(parent instanceof View)) { break; } - + child = (View) parent; parent = child.getParent(); } @@ -2479,7 +2564,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * and previouslyFocusedRect provide insight into where the focus is coming from. * When overriding, be sure to call up through to the super class so that * the standard focus handling will occur. - * + * * @param gainFocus True if the View has focus; false otherwise. * @param direction The direction focus has moved when requestFocus() * is called to give this view focus. Values are @@ -2492,6 +2577,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * from (in addition to direction). Will be <code>null</code> otherwise. */ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { + if (gainFocus) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + InputMethodManager imm = InputMethodManager.peekInstance(); if (!gainFocus) { if (isPressed()) { @@ -2506,7 +2595,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { && mAttachInfo.mHasWindowFocus) { imm.focusIn(this); } - + invalidate(); if (mOnFocusChangeListener != null) { mOnFocusChangeListener.onFocusChange(this, gainFocus); @@ -2514,6 +2603,79 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * {@inheritDoc} + */ + public void sendAccessibilityEvent(int eventType) { + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); + } + } + + /** + * {@inheritDoc} + */ + public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(getContext().getPackageName()); + event.setEnabled(isEnabled()); + event.setContentDescription(mContentDescription); + + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) { + ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList; + getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL); + event.setItemCount(focusablesTempList.size()); + event.setCurrentItemIndex(focusablesTempList.indexOf(this)); + focusablesTempList.clear(); + } + + dispatchPopulateAccessibilityEvent(event); + + AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event); + } + + /** + * Dispatches an {@link AccessibilityEvent} to the {@link View} children + * to be populated. + * + * @param event The event. + * + * @return True if the event population was completed. + */ + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return false; + } + + /** + * Gets the {@link View} description. It briefly describes the view and is + * primarily used for accessibility support. Set this property to enable + * better accessibility support for your application. This is especially + * true for views that do not have textual representation (For example, + * ImageButton). + * + * @return The content descriptiopn. + * + * @attr ref android.R.styleable#View_contentDescription + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the {@link View} description. It briefly describes the view and is + * primarily used for accessibility support. Set this property to enable + * better accessibility support for your application. This is especially + * true for views that do not have textual representation (For example, + * ImageButton). + * + * @param contentDescription The content description. + * + * @attr ref android.R.styleable#View_contentDescription + */ + public void setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + } + + /** * Invoked whenever this view loses focus, either by losing window focus or by losing * focus within its window. This method can be used to clear any state tied to the * focus. For instance, if a button is held pressed with the trackball and the window @@ -2522,7 +2684,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * Subclasses of View overriding this method should always call super.onFocusLost(). * * @see #onFocusChanged(boolean, int, android.graphics.Rect) - * @see #onWindowFocusChanged(boolean) + * @see #onWindowFocusChanged(boolean) * * @hide pending API council approval */ @@ -3222,11 +3384,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @param direction The direction of the focus */ public void addFocusables(ArrayList<View> views, int direction) { - if (!isFocusable()) return; + addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); + } - if (isInTouchMode() && !isFocusableInTouchMode()) return; + /** + * Adds any focusable views that are descendants of this view (possibly + * including this view if it is focusable itself) to views. This method + * adds all focusable views regardless if we are in touch mode or + * only views focusable in touch mode if we are in touch mode depending on + * the focusable mode paramater. + * + * @param views Focusable views found so far or null if all we are interested is + * the number of focusables. + * @param direction The direction of the focus. + * @param focusableMode The type of focusables to be added. + * + * @see #FOCUSABLES_ALL + * @see #FOCUSABLES_TOUCH_MODE + */ + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { + if (!isFocusable()) { + return; + } - views.add(this); + if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && + isInTouchMode() && !isFocusableInTouchMode()) { + return; + } + + if (views != null) { + views.add(this); + } } /** @@ -3398,14 +3586,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public void onStartTemporaryDetach() { } - + /** * Called after {@link #onStartTemporaryDetach} when the container is done * changing the view. */ public void onFinishTemporaryDetach() { } - + /** * capture information of this view for later analysis: developement only * check dynamic switch to make sure we only dump view @@ -3790,25 +3978,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * a call on that method would return a non-null InputConnection, and * they are really a first-class editor that the user would normally * start typing on when the go into a window containing your view. - * + * * <p>The default implementation always returns false. This does * <em>not</em> mean that its {@link #onCreateInputConnection(EditorInfo)} * will not be called or the user can not otherwise perform edits on your * view; it is just a hint to the system that this is not the primary * purpose of this view. - * + * * @return Returns true if this view is a text editor, else false. */ public boolean onCheckIsTextEditor() { return false; } - + /** * Create a new InputConnection for an InputMethod to interact * with the view. The default implementation returns null, since it doesn't * support input methods. You can override this to implement such support. * This is only needed for views that take focus and text input. - * + * * <p>When implementing this, you probably also want to implement * {@link #onCheckIsTextEditor()} to indicate you will return a * non-null InputConnection. @@ -3832,7 +4020,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public boolean checkInputConnectionProxy(View view) { return false; } - + /** * Show the context menu for this view. It is not safe to hold on to the * menu after returning from this method. @@ -4563,14 +4751,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * whether an instance is opaque. Opaque Views are treated in a special way by * the View hierarchy, possibly allowing it to perform optimizations during * invalidate/draw passes. - * + * * @return True if this View is guaranteed to be fully opaque, false otherwise. * * @hide Pending API council approval */ @ViewDebug.ExportedProperty public boolean isOpaque() { - return mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE; + return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK; + } + + private void computeOpaqueFlags() { + // Opaque if: + // - Has a background + // - Background is opaque + // - Doesn't have scrollbars or scrollbars are inside overlay + + if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) { + mPrivateFlags |= OPAQUE_BACKGROUND; + } else { + mPrivateFlags &= ~OPAQUE_BACKGROUND; + } + + final int flags = mViewFlags; + if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) || + (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY) { + mPrivateFlags |= OPAQUE_SCROLLBARS; + } else { + mPrivateFlags &= ~OPAQUE_SCROLLBARS; + } + } + + /** + * @hide + */ + protected boolean hasOpaqueScrollbars() { + return (mPrivateFlags & OPAQUE_SCROLLBARS) == OPAQUE_SCROLLBARS; } /** @@ -4897,6 +5113,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) { if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_HORIZONTAL; + computeOpaqueFlags(); recomputePadding(); } } @@ -4926,6 +5143,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_VERTICAL; + computeOpaqueFlags(); recomputePadding(); } } @@ -4954,6 +5172,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setScrollBarStyle(int style) { if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) { mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK); + computeOpaqueFlags(); recomputePadding(); } } @@ -5132,9 +5351,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } } - + /** - * Override this if the vertical scrollbar needs to be hidden in a subclass, like when + * Override this if the vertical scrollbar needs to be hidden in a subclass, like when * FastScroller is visible. * @return whether to temporarily hide the vertical scrollbar * @hide @@ -5570,28 +5789,52 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> + * + * @return A non-scaled bitmap representing this view or null if cache is disabled. + * + * @see #getDrawingCache(boolean) + */ + public Bitmap getDrawingCache() { + return getDrawingCache(false); + } + + /** * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap * is null when caching is disabled. If caching is enabled and the cache is not ready, * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.</p> + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> + * + * @param autoScale Indicates whether the generated bitmap should be scaled based on + * the current density of the screen when the application is in compatibility + * mode. * - * @return a bitmap representing this view or null if cache is disabled - * + * @return A bitmap representing this view or null if cache is disabled. + * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() - * @see #buildDrawingCache() + * @see #buildDrawingCache(boolean) * @see #destroyDrawingCache() */ - public Bitmap getDrawingCache() { + public Bitmap getDrawingCache(boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { - buildDrawingCache(); + buildDrawingCache(autoScale); } - return mDrawingCache == null ? null : mDrawingCache.get(); + return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); } /** @@ -5610,6 +5853,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (bitmap != null) bitmap.recycle(); mDrawingCache = null; } + if (mUnscaledDrawingCache != null) { + final Bitmap bitmap = mUnscaledDrawingCache.get(); + if (bitmap != null) bitmap.recycle(); + mUnscaledDrawingCache = null; + } } /** @@ -5637,18 +5885,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p> + * + * @see #buildDrawingCache(boolean) + */ + public void buildDrawingCache() { + buildDrawingCache(false); + } + + /** * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> * * <p>If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> * * @see #getDrawingCache() * @see #destroyDrawingCache() */ - public void buildDrawingCache() { - if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || - mDrawingCache.get() == null) { + public void buildDrawingCache(boolean autoScale) { + if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? + (mDrawingCache == null || mDrawingCache.get() == null) : + (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -5657,8 +5923,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback { EventLog.writeEvent(60002, hashCode()); } - final int width = mRight - mLeft; - final int height = mBottom - mTop; + int width = mRight - mLeft; + int height = mBottom - mTop; + + final AttachInfo attachInfo = mAttachInfo; + final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; + + if (autoScale && scalingRequired) { + width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); + height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); + } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || @@ -5672,7 +5946,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } boolean clear = true; - Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); + Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { @@ -5701,12 +5976,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback { try { bitmap = Bitmap.createBitmap(width, height, quality); - mDrawingCache = new SoftReference<Bitmap>(bitmap); + if (autoScale) { + mDrawingCache = new SoftReference<Bitmap>(bitmap); + } else { + mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); + } } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy - mDrawingCache = null; + if (autoScale) { + mDrawingCache = null; + } else { + mUnscaledDrawingCache = null; + } return; } @@ -5714,7 +5997,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } Canvas canvas; - final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { @@ -5737,15 +6019,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback { computeScroll(); final int restoreCount = canvas.save(); + + if (autoScale && scalingRequired) { + final float scale = attachInfo.mApplicationScale; + canvas.scale(scale, scale); + } + canvas.translate(-mScrollX, -mScrollY); - mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN; + mPrivateFlags |= DRAWN; // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + mPrivateFlags &= ~DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); @@ -5792,7 +6081,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { canvas = new Canvas(bitmap); } - if ((backgroundColor&0xff000000) != 0) { + if ((backgroundColor & 0xff000000) != 0) { bitmap.eraseColor(backgroundColor); } @@ -5800,6 +6089,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); + // Temporarily remove the dirty mask + int flags = mPrivateFlags; + mPrivateFlags &= ~DIRTY_MASK; + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { dispatchDraw(canvas); @@ -5807,13 +6100,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback { draw(canvas); } + mPrivateFlags = flags; + canvas.restoreToCount(restoreCount); if (attachInfo != null) { // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } - + return bitmap; } @@ -5927,8 +6222,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } - final boolean dirtyOpaque = (mPrivateFlags & DIRTY_MASK) == DIRTY_OPAQUE; - mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN; + final int privateFlags = mPrivateFlags; + final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && + (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); + mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN; /* * Draw traversal performs several drawing steps which must be executed @@ -6306,7 +6603,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { boolean changed = false; if (DBG) { - System.out.println(this + " View.setFrame(" + left + "," + top + "," + Log.d("View", this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")"); } @@ -6709,6 +7006,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { requestLayout = true; } + computeOpaqueFlags(); + if (requestLayout) { requestLayout(); } @@ -6749,7 +7048,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mUserPaddingBottom = bottom; final int viewFlags = mViewFlags; - + // Common case is there are no scroll bars. if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) { // TODO: Deal with RTL languages to adjust left padding instead of right. @@ -6762,7 +7061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ? 0 : getHorizontalScrollbarHeight(); } } - + if (mPaddingLeft != left) { changed = true; mPaddingLeft = left; @@ -6899,7 +7198,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { return v; } } - + View parent = this; while (parent.mParent != null && parent.mParent instanceof View) { @@ -6920,8 +7219,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { getLocationInWindow(location); final AttachInfo info = mAttachInfo; - location[0] += info.mWindowLeft; - location[1] += info.mWindowTop; + if (info != null) { + location[0] += info.mWindowLeft; + location[1] += info.mWindowTop; + } } /** @@ -6947,7 +7248,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { location[1] += view.mTop - view.mScrollY; viewParent = view.mParent; } - + if (viewParent instanceof ViewRoot) { // *cough* final ViewRoot vr = (ViewRoot)viewParent; @@ -7098,7 +7399,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @return the Object stored in this view as a tag * * @see #setTag(int, Object) - * @see #getTag() + * @see #getTag() */ public Object getTag(int key) { SparseArray<Object> tags = null; @@ -7154,7 +7455,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { + "resource id."); } - setTagInternal(this, key, tag); + setTagInternal(this, key, tag); } private static void setTagInternal(View view, int key, Object tag) { @@ -7189,7 +7490,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * Method that subclasses should implement to check their consistency. The type of * consistency check is indicated by the bit field passed as a parameter. - * + * * @param consistency The type of consistency. See ViewDebug for more information. * * @throws IllegalStateException if the view is in an inconsistent state. @@ -7744,7 +8045,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * BZZZTT!!1! - * + * * <p>Provide haptic feedback to the user for this view. * * <p>The framework will provide haptic feedback for some built in actions, @@ -7763,7 +8064,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * BZZZTT!!1! - * + * * <p>Like {@link #performHapticFeedback(int)}, with additional options. * * @param feedbackConstant One of the constants defined in @@ -8158,7 +8459,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * window. */ static class AttachInfo { - interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); @@ -8227,11 +8527,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * The top view of the hierarchy. */ View mRootView; - + IBinder mPanelParentWindowToken; Surface mSurface; /** + * Scale factor used by the compatibility mode + */ + float mApplicationScale; + + /** + * Indicates whether the application is in compatibility mode + */ + boolean mScalingRequired; + + /** * Left position of this view's window */ int mWindowLeft; @@ -8288,6 +8598,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { long mDrawingTime; /** + * Indicates whether or not ignoring the DIRTY_MASK flags. + */ + boolean mIgnoreDirtyState; + + /** * Indicates whether the view's window is currently in touch mode. */ boolean mInTouchMode; @@ -8365,7 +8680,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * calling up the hierarchy. */ final Rect mTmpInvalRect = new Rect(); - + + /** + * Temporary list for use in collecting focusable descendents of a view. + */ + final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24); + /** * Creates a new set of attachment information with the specified * events handler and thread. @@ -8408,18 +8728,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback { // use use a height of 1, and then wack the matrix each time we // actually use it. shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); - + paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } - + public void setFadeColor(int color) { if (color != 0 && color != mLastColor) { mLastColor = color; color |= 0xFF000000; - + shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP); - + paint.setShader(shader); // Restore the default transfer mode (src_over) paint.setXfermode(null); diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 8e1524bd839d..0e36ec296d9c 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -106,6 +106,11 @@ public class ViewConfiguration { * Minimum velocity to initiate a fling, as measured in pixels per second */ private static final int MINIMUM_FLING_VELOCITY = 50; + + /** + * Maximum velocity to initiate a fling, as measured in pixels per second + */ + private static final int MAXIMUM_FLING_VELOCITY = 4000; /** * The maximum size of View's drawing cache, expressed in bytes. This size @@ -122,6 +127,7 @@ public class ViewConfiguration { private final int mEdgeSlop; private final int mFadingEdgeLength; private final int mMinimumFlingVelocity; + private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; private final int mDoubleTapSlop; @@ -139,6 +145,7 @@ public class ViewConfiguration { mEdgeSlop = EDGE_SLOP; mFadingEdgeLength = FADING_EDGE_LENGTH; mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY; + mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY; mScrollbarSize = SCROLL_BAR_SIZE; mTouchSlop = TOUCH_SLOP; mDoubleTapSlop = DOUBLE_TAP_SLOP; @@ -164,6 +171,7 @@ public class ViewConfiguration { mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f); mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f); mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f); + mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f); mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f); mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); @@ -367,6 +375,23 @@ public class ViewConfiguration { } /** + * @return Maximum velocity to initiate a fling, as measured in pixels per second. + * + * @deprecated Use {@link #getScaledMaximumFlingVelocity()} instead. + */ + @Deprecated + public static int getMaximumFlingVelocity() { + return MAXIMUM_FLING_VELOCITY; + } + + /** + * @return Maximum velocity to initiate a fling, as measured in pixels per second. + */ + public int getScaledMaximumFlingVelocity() { + return mMaximumFlingVelocity; + } + + /** * The maximum drawing cache size expressed in bytes. * * @return the maximum size of View's drawing cache expressed in bytes diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 74a248f65834..46aea022912b 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -87,17 +87,17 @@ public class ViewDebug { * check that this value is set to true as not to affect performance. */ public static final boolean TRACE_RECYCLER = false; - + /** * The system property of dynamic switch for capturing view information * when it is set, we dump interested fields and methods for the view on focus - */ + */ static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview"; - + /** * The system property of dynamic switch for capturing event information * when it is set, we log key events, touch/motion and trackball events - */ + */ static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; /** @@ -216,7 +216,7 @@ public class ViewDebug { * <pre> * * A specified String is output when the following is true: - * + * * @return An array of int to String mappings */ FlagToString[] flagMapping() default { }; @@ -228,7 +228,7 @@ public class ViewDebug { * * @return true if the properties of this property should be dumped * - * @see #prefix() + * @see #prefix() */ boolean deepExport() default false; @@ -313,15 +313,15 @@ public class ViewDebug { @Retention(RetentionPolicy.RUNTIME) public @interface CapturedViewProperty { /** - * When retrieveReturn is true, we need to retrieve second level methods + * When retrieveReturn is true, we need to retrieve second level methods * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod() - * we will set retrieveReturn = true on the annotation of + * we will set retrieveReturn = true on the annotation of * myView.getFirstLevelMethod() - * @return true if we need the second level methods + * @return true if we need the second level methods */ - boolean retrieveReturn() default false; + boolean retrieveReturn() default false; } - + private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null; private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null; @@ -401,7 +401,7 @@ public class ViewDebug { */ public static long getViewRootInstanceCount() { return ViewRoot.getInstanceCount(); - } + } /** * Outputs a trace to the currently opened recycler traces. The trace records the type of @@ -624,7 +624,7 @@ public class ViewDebug { * * This method will return immediately if TRACE_HIERARCHY is false. * - * @see #startHierarchyTracing(String, View) + * @see #startHierarchyTracing(String, View) * @see #trace(View, android.view.ViewDebug.HierarchyTraceType) */ public static void stopHierarchyTracing() { @@ -671,7 +671,7 @@ public class ViewDebug { sHierarhcyRoot = null; } - + static void dispatchCommand(View view, String command, String parameters, OutputStream clientStream) throws IOException { @@ -1039,10 +1039,10 @@ public class ViewDebug { final ArrayList<Method> foundMethods = new ArrayList<Method>(); methods = klass.getDeclaredMethods(); - + int count = methods.length; for (int i = 0; i < count; i++) { - final Method method = methods[i]; + final Method method = methods[i]; if (method.getParameterTypes().length == 0 && method.isAnnotationPresent(ExportedProperty.class) && method.getReturnType() != Void.class) { @@ -1075,7 +1075,7 @@ public class ViewDebug { klass = klass.getSuperclass(); } while (klass != Object.class); } - + private static void exportMethods(Context context, Object view, BufferedWriter out, Class<?> klass, String prefix) throws IOException { @@ -1235,10 +1235,11 @@ public class ViewDebug { for (int j = 0; j < count; j++) { final FlagToString flagMapping = mapping[j]; final boolean ifTrue = flagMapping.outputIf(); - final boolean test = (intValue & flagMapping.mask()) == flagMapping.equals(); + final int maskResult = intValue & flagMapping.mask(); + final boolean test = maskResult == flagMapping.equals(); if ((test && ifTrue) || (!test && !ifTrue)) { final String name = flagMapping.name(); - final String value = ifTrue ? "true" : "false"; + final String value = "0x" + Integer.toHexString(maskResult); writeEntry(out, prefix, name, "", value); } } @@ -1259,7 +1260,7 @@ public class ViewDebug { for (int j = 0; j < valuesCount; j++) { String name; - String value; + String value = null; final int intValue = array[j]; @@ -1275,7 +1276,6 @@ public class ViewDebug { } } - value = String.valueOf(intValue); if (hasMapping) { int mappingCount = mapping.length; for (int k = 0; k < mappingCount; k++) { @@ -1288,7 +1288,9 @@ public class ViewDebug { } if (resolveId) { - value = (String) resolveId(context, intValue); + if (value == null) value = (String) resolveId(context, intValue); + } else { + value = String.valueOf(intValue); } writeEntry(out, prefix, name, suffix, value); @@ -1396,10 +1398,10 @@ public class ViewDebug { final ArrayList<Method> foundMethods = new ArrayList<Method>(); methods = klass.getMethods(); - + int count = methods.length; for (int i = 0; i < count; i++) { - final Method method = methods[i]; + final Method method = methods[i]; if (method.getParameterTypes().length == 0 && method.isAnnotationPresent(CapturedViewProperty.class) && method.getReturnType() != Void.class) { @@ -1413,14 +1415,14 @@ public class ViewDebug { return methods; } - - private static String capturedViewExportMethods(Object obj, Class<?> klass, + + private static String capturedViewExportMethods(Object obj, Class<?> klass, String prefix) { if (obj == null) { return "null"; } - + StringBuilder sb = new StringBuilder(); final Method[] methods = capturedViewGetPropertyMethods(klass); @@ -1430,41 +1432,41 @@ public class ViewDebug { try { Object methodValue = method.invoke(obj, (Object[]) null); final Class<?> returnType = method.getReturnType(); - + CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class); if (property.retrieveReturn()) { //we are interested in the second level data only sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#")); - } else { + } else { sb.append(prefix); sb.append(method.getName()); sb.append("()="); - + if (methodValue != null) { - final String value = methodValue.toString().replace("\n", "\\n"); - sb.append(value); + final String value = methodValue.toString().replace("\n", "\\n"); + sb.append(value); } else { sb.append("null"); } sb.append("; "); } } catch (IllegalAccessException e) { - //Exception IllegalAccess, it is OK here + //Exception IllegalAccess, it is OK here //we simply ignore this method } catch (InvocationTargetException e) { - //Exception InvocationTarget, it is OK here + //Exception InvocationTarget, it is OK here //we simply ignore this method - } - } + } + } return sb.toString(); } private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) { - + if (obj == null) { return "null"; } - + StringBuilder sb = new StringBuilder(); final Field[] fields = capturedViewGetPropertyFields(klass); @@ -1486,25 +1488,25 @@ public class ViewDebug { } sb.append(' '); } catch (IllegalAccessException e) { - //Exception IllegalAccess, it is OK here + //Exception IllegalAccess, it is OK here //we simply ignore this field } } return sb.toString(); } - + /** - * Dump view info for id based instrument test generation + * Dump view info for id based instrument test generation * (and possibly further data analysis). The results are dumped - * to the log. + * to the log. * @param tag for log * @param view for dump */ - public static void dumpCapturedView(String tag, Object view) { + public static void dumpCapturedView(String tag, Object view) { Class<?> klass = view.getClass(); StringBuilder sb = new StringBuilder(klass.getName() + ": "); sb.append(capturedViewExportFields(view, klass, "")); - sb.append(capturedViewExportMethods(view, klass, "")); - Log.d(tag, sb.toString()); + sb.append(capturedViewExportMethods(view, klass, "")); + Log.d(tag, sb.toString()); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 26fe776ba350..f7b7f029df82 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -24,15 +24,16 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.Region; import android.graphics.RectF; +import android.graphics.Region; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; +import android.util.Config; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; -import android.util.Config; +import android.view.accessibility.AccessibilityEvent; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; @@ -52,6 +53,15 @@ import java.util.ArrayList; * <p> * Also see {@link LayoutParams} for layout attributes. * </p> + * + * @attr ref android.R.styleable#ViewGroup_clipChildren + * @attr ref android.R.styleable#ViewGroup_clipToPadding + * @attr ref android.R.styleable#ViewGroup_layoutAnimation + * @attr ref android.R.styleable#ViewGroup_animationCache + * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache + * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache + * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren + * @attr ref android.R.styleable#ViewGroup_descendantFocusability */ public abstract class ViewGroup extends View implements ViewParent, ViewManager { private static final boolean DBG = false; @@ -89,7 +99,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Internal flags. - * + * * This field should be made private, so it is hidden from the SDK. * {@hide} */ @@ -142,7 +152,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * to get the index of the child to draw for that iteration. */ protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; - + /** * When set, this ViewGroup supports static transformations on children; this causes * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be @@ -151,7 +161,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * Any subclass overriding * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should * set this flags in {@link #mGroupFlags}. - * + * * {@hide} */ protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; @@ -212,7 +222,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * When set, this ViewGroup should not intercept touch events. */ private static final int FLAG_DISALLOW_INTERCEPT = 0x80000; - + /** * Indicates which types of drawing caches are to be kept in memory. * This field should be made private, so it is hidden from the SDK. @@ -601,6 +611,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void addFocusables(ArrayList<View> views, int direction) { + addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); + } + + /** + * {@inheritDoc} + */ + @Override + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { final int focusableCount = views.size(); final int descendantFocusability = getDescendantFocusability(); @@ -612,7 +630,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addFocusables(views, direction); + child.addFocusables(views, direction, focusableMode); } } } @@ -625,7 +643,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager descendantFocusability != FOCUS_AFTER_DESCENDANTS || // No focusable descendants (focusableCount == views.size())) { - super.addFocusables(views, direction); + super.addFocusables(views, direction, focusableMode); } } @@ -680,7 +698,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ViewParent parent = mParent; if (parent != null) parent.recomputeViewAttributes(this); } - + @Override void dispatchCollectViewAttributes(int visibility) { visibility |= mViewFlags&VISIBILITY_MASK; @@ -812,16 +830,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } } - + boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || - (action == MotionEvent.ACTION_CANCEL); + (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { // Note, we've already copied the previous state to our local // variable, so this takes effect on the next event mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } - + // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one. final View target = mMotionTarget; @@ -868,18 +886,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - + if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { // We're already in this state, assume our ancestors are too return; } - + if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } - + // Pass it up to our parent if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); @@ -1020,6 +1038,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = false; + for (int i = 0, count = getChildCount(); i < count; i++) { + populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); + } + return populated; + } + /** * {@inheritDoc} */ @@ -1139,7 +1166,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } @@ -1181,7 +1208,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bindLayoutAnimation(child); if (cache) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } } @@ -1274,7 +1301,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager post(end); } } - + /** * Returns the index of the child to draw for this iteration. Override this * if you want to change the drawing order of children. By default, it @@ -1282,14 +1309,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * <p> * NOTE: In order for this method to be called, the * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set. - * + * * @param i The current iteration. * @return The index of the child to draw this iteration. */ protected int getChildDrawingOrder(int childCount, int i) { return i; } - + private void notifyAnimationListener() { mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; mGroupFlags |= FLAG_ANIMATION_DONE; @@ -1403,9 +1430,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - // Clear the flag as early as possible to allow draw() implementations + // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations - child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; + child.mPrivateFlags |= DRAWN; if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && (child.mPrivateFlags & DRAW_ANIMATION) == 0) { @@ -1417,10 +1444,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int sx = child.mScrollX; final int sy = child.mScrollY; + boolean scalingRequired = false; Bitmap cache = null; if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { - cache = child.getDrawingCache(); + cache = child.getDrawingCache(true); + if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; } final boolean hasNoCache = cache == null; @@ -1430,6 +1459,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.translate(cl - sx, ct - sy); } else { canvas.translate(cl, ct); + if (scalingRequired) { + // mAttachInfo cannot be null, otherwise scalingRequired == false + final float scale = 1.0f / mAttachInfo.mApplicationScale; + canvas.scale(scale, scale); + } } float alpha = 1.0f; @@ -1472,7 +1506,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (hasNoCache) { canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); } else { - canvas.clipRect(0, 0, cr - cl, cb - ct); + if (!scalingRequired) { + canvas.clipRect(0, 0, cr - cl, cb - ct); + } else { + canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); + } } } @@ -1482,6 +1520,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + child.mPrivateFlags &= ~DIRTY_MASK; child.dispatchDraw(canvas); } else { child.draw(canvas); @@ -1546,7 +1585,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children[i].setSelected(selected); } } - + @Override protected void dispatchSetPressed(boolean pressed) { final View[] children = mChildren; @@ -1577,7 +1616,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * {@inheritDoc} * - * @see #setStaticTransformationsEnabled(boolean) + * @see #setStaticTransformationsEnabled(boolean) */ protected boolean getChildStaticTransformation(View child, Transformation t) { return false; @@ -1844,10 +1883,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } - + AttachInfo ai = mAttachInfo; if (ai != null) { - boolean lastKeepOn = ai.mKeepScreenOn; + boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { @@ -2047,7 +2086,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } needGlobalAttributesUpdate(false); - + removeFromArray(index); if (clearChildFocus) { @@ -2080,7 +2119,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } needGlobalAttributesUpdate(false); - + if (notifyListener) { onHierarchyChangeListener.onChildViewRemoved(this, view); } @@ -2128,7 +2167,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager View clearChildFocus = null; needGlobalAttributesUpdate(false); - + for (int i = count - 1; i >= 0; i--) { final View view = children[i]; @@ -2173,7 +2212,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child == mFocused) { child.clearFocus(); } - + if (animate && child.getAnimation() != null) { addDisappearingView(child); } else if (child.mAttachInfo != null) { @@ -2323,7 +2362,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; // Check whether the child that requests the invalidate is fully opaque - final boolean isOpaque = child.isOpaque(); + final boolean isOpaque = child.isOpaque() && !drawAnimation && + child.getAnimation() != null; // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; @@ -3135,7 +3175,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } } - + @Override protected boolean fitSystemWindows(Rect insets) { @@ -3269,7 +3309,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * laid out. See * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} * for a list of all child view attributes that this class supports. - * + * * <p> * The base LayoutParams class just describes how big the view wants to be * for both width and height. For each dimension, it can specify one of: @@ -3400,7 +3440,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @param output the String to prepend to the internal representation * @return a String with the following format: output + * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" - * + * * @hide */ public String debug(String output) { @@ -3413,7 +3453,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @param size the size to convert * @return a String instance representing the supplied size - * + * * @hide */ protected static String sizeToString(int size) { diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 5090c56b5edc..6f6e22477d25 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -30,14 +30,18 @@ import android.os.Process; import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.Config; +import android.util.DisplayMetrics; import android.util.Log; import android.util.EventLog; import android.util.SparseArray; import android.view.View.MeasureSpec; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import android.content.pm.PackageManager; +import android.content.res.CompatibilityInfo; import android.content.Context; import android.app.ActivityManagerNative; import android.Manifest; @@ -90,18 +94,18 @@ public final class ViewRoot extends Handler implements ViewParent, static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); - private static int sDrawTime; + private static int sDrawTime; long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); final int[] mTmpLocation = new int[2]; - + final InputMethodCallback mInputMethodCallback; final SparseArray<Object> mPendingEvents = new SparseArray<Object>(); int mPendingEventSeq = 0; - + final Thread mThread; final WindowLeaked mLocation; @@ -123,16 +127,13 @@ public final class ViewRoot extends Handler implements ViewParent, int mHeight; Rect mDirty; // will be a graphics.Region soon boolean mIsAnimating; - // TODO: change these to scalar class. - private float mAppScale; - private float mAppScaleInverted; // = 1.0f / mAppScale - private int[] mWindowLayoutParamsBackup = null; + + 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; @@ -168,7 +169,7 @@ public final class ViewRoot extends Handler implements ViewParent, int mScrollY; int mCurScrollY; Scroller mScroller; - + EGL10 mEgl; EGLDisplay mEglDisplay; EGLContext mEglContext; @@ -178,7 +179,7 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mUseGL; boolean mGlWanted; - final ViewConfiguration mViewConfiguration; + final ViewConfiguration mViewConfiguration; /** * see {@link #playSoundEffect(int)} @@ -216,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); @@ -384,29 +384,39 @@ public final class ViewRoot extends Handler implements ViewParent, synchronized (this) { if (mView == null) { mView = view; - mAppScale = mView.getContext().getApplicationScale(); - if (mAppScale != 1.0f) { - mWindowLayoutParamsBackup = new int[4]; - } - mAppScaleInverted = 1.0f / mAppScale; mWindowAttributes.copyFrom(attrs); + + CompatibilityInfo compatibilityInfo = + mView.getContext().getResources().getCompatibilityInfo(); + mTranslator = compatibilityInfo.getTranslator(attrs); + boolean restore = false; + if (attrs != null && mTranslator != null) { + restore = true; + 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 = + mTranslator == null ? false : mTranslator.scalingRequired; + mAttachInfo.mApplicationScale = + mTranslator == null ? 1.0f : mTranslator.applicationScale; if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); } mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; */ - + // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); - try { - res = sWindowSession.add(mWindow, attrs, + res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets); } catch (RemoteException e) { mAdded = false; @@ -414,8 +424,15 @@ 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 (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } - mAttachInfo.mContentInsets.scale(mAppScaleInverted); mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingVisibleInsets.set(0, 0, 0, 0); if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow); @@ -526,18 +543,20 @@ 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 || mAppScale != 1.0f) { + if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); + if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); + dirty = mTempRect; if (mCurScrollY != 0) { - mTempRect.offset(0, -mCurScrollY); + dirty.offset(0, -mCurScrollY); } - if (mAppScale != 1.0f) { - mTempRect.scale(mAppScale); + if (mTranslator != null) { + mTranslator.translateRectInAppWindowToScreen(dirty); + } + if (mAttachInfo.mScalingRequired) { + dirty.inset(-1, -1); } - dirty = mTempRect; } - // TODO: When doing a union with mDirty != empty, we must cancel all the DIRTY_OPAQUE flags mDirty.union(dirty); if (!mWillDrawSoon) { scheduleTraversals(); @@ -553,7 +572,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!"); } @@ -582,7 +601,7 @@ public final class ViewRoot extends Handler implements ViewParent, int getHostVisibility() { return mAppVisible ? mView.getVisibility() : View.GONE; } - + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -614,19 +633,22 @@ public final class ViewRoot extends Handler implements ViewParent, boolean viewVisibilityChanged = mViewVisibility != viewVisibility || mNewSurfaceNeeded; + float appScale = mAttachInfo.mApplicationScale; + WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; params = lp; } - + Rect frame = mWinFrame; if (mFirst) { fullRedrawNeeded = true; mLayoutRequested = true; - Display d = new Display(0); - desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); - desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); + DisplayMetrics packageMetrics = + mView.getContext().getResources().getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; // For the very first time, tell the view hierarchy that it // is attached to the window. Note that at this point the surface @@ -641,12 +663,13 @@ public final class ViewRoot extends Handler implements ViewParent, host.dispatchAttachedToWindow(attachInfo, 0); getRunQueue().executeActions(attachInfo.mHandler); //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; @@ -669,7 +692,7 @@ public final class ViewRoot extends Handler implements ViewParent, } boolean insetsChanged = false; - + if (mLayoutRequested) { if (mFirst) { host.fitSystemWindows(mAttachInfo.mContentInsets); @@ -694,9 +717,10 @@ public final class ViewRoot extends Handler implements ViewParent, || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowResizesToFitContent = true; - Display d = new Display(0); - desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); - desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); + DisplayMetrics packageMetrics = + mView.getContext().getResources().getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; } } @@ -753,7 +777,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) { if (!PixelFormat.formatHasAlpha(params.format)) { params.format = PixelFormat.TRANSLUCENT; @@ -782,7 +806,7 @@ public final class ViewRoot extends Handler implements ViewParent, // computed insets. insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged); - + if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) { if (params == null) { params = mWindowAttributes; @@ -791,7 +815,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - final Rect frame = mWinFrame; boolean initialized = false; boolean contentInsetsChanged = false; boolean visibleInsetsChanged; @@ -818,7 +841,7 @@ public final class ViewRoot extends Handler implements ViewParent, + " content=" + mPendingContentInsets.toShortString() + " visible=" + mPendingVisibleInsets.toShortString() + " surface=" + mSurface); - + contentInsetsChanged = !mPendingContentInsets.equals( mAttachInfo.mContentInsets); visibleInsetsChanged = !mPendingVisibleInsets.equals( @@ -846,7 +869,7 @@ public final class ViewRoot extends Handler implements ViewParent, // all at once. newSurface = true; fullRedrawNeeded = true; - + if (mGlWanted && !mUseGL) { initializeGL(); initialized = mGlCanvas != null; @@ -864,7 +887,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; @@ -876,7 +899,7 @@ public final class ViewRoot extends Handler implements ViewParent, mHeight = frame.height(); if (initialized) { - mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); + mGlCanvas.setViewport((int) (mWidth * appScale), (int) (mHeight * appScale)); } boolean focusChangedDueToTouchMode = ensureTouchModeLocally( @@ -891,7 +914,7 @@ public final class ViewRoot extends Handler implements ViewParent, + " mHeight=" + mHeight + " measuredHeight" + host.mMeasuredHeight + " coveredInsetsChanged=" + contentInsetsChanged); - + // Ask host how big it wants to be host.measure(childWidthMeasureSpec, childHeightMeasureSpec); @@ -939,7 +962,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) { @@ -966,11 +988,10 @@ public final class ViewRoot extends Handler implements ViewParent, mTmpLocation[1] + host.mBottom - host.mTop); host.gatherTransparentRegion(mTransparentRegion); + if (mTranslator != null) { + mTranslator.translateRegionInWindowToScreen(mTransparentRegion); + } - // TODO: scale the region, like: - // Region uses native methods. We probabl should have ScalableRegion class. - - // Region does not have equals method ? if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); // reconfigure window manager @@ -981,7 +1002,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - if (DBG) { System.out.println("======================================"); System.out.println("performTraversals -- after setFrame"); @@ -1001,20 +1021,23 @@ public final class ViewRoot extends Handler implements ViewParent, givenContent.left = givenContent.top = givenContent.right = givenContent.bottom = givenVisible.left = givenVisible.top = givenVisible.right = givenVisible.bottom = 0; - insets.contentInsets.scale(mAppScale); - insets.visibleInsets.scale(mAppScale); - attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); + 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) { } } } - + if (mFirst) { // handle first focus request if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" @@ -1052,7 +1075,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { @@ -1140,10 +1163,9 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mViewScrollChanged = false; mAttachInfo.mTreeObserver.dispatchOnScrollChanged(); } - + int yoff; - final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); + final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); if (scrolling) { yoff = mScroller.getCurrY(); } else { @@ -1153,26 +1175,28 @@ public final class ViewRoot extends Handler implements ViewParent, mCurScrollY = yoff; fullRedrawNeeded = true; } + float appScale = mAttachInfo.mApplicationScale; + boolean scalingRequired = mAttachInfo.mScalingRequired; Rect dirty = mDirty; if (mUseGL) { if (!dirty.isEmpty()) { Canvas canvas = mGlCanvas; - if (mGL!=null && canvas != null) { + if (mGL != null && canvas != null) { mGL.glDisable(GL_SCISSOR_TEST); mGL.glClearColor(0, 0, 0, 0); mGL.glClear(GL_COLOR_BUFFER_BIT); mGL.glEnable(GL_SCISSOR_TEST); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); + mAttachInfo.mIgnoreDirtyState = true; mView.mPrivateFlags |= View.DRAWN; - float scale = mAppScale; int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scale != 1.0f) { - canvas.scale(scale, scale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { @@ -1182,6 +1206,8 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.restoreToCount(saveCount); } + mAttachInfo.mIgnoreDirtyState = false; + mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); checkEglErrors(); @@ -1201,20 +1227,33 @@ public final class ViewRoot extends Handler implements ViewParent, return; } - if (fullRedrawNeeded) - dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); + if (fullRedrawNeeded) { + mAttachInfo.mIgnoreDirtyState = true; + dirty.union(0, 0, (int) (mWidth * appScale), (int) (mHeight * appScale)); + } if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Draw " + mView + "/" + mWindowAttributes.getTitle() + ": dirty={" + dirty.left + "," + dirty.top + "," + dirty.right + "," + dirty.bottom + "} surface=" - + surface + " surface.isValid()=" + surface.isValid()); + + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" + + appScale + ", width=" + mWidth + ", height=" + mHeight); } Canvas canvas; try { + int left = dirty.left; + 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 || + bottom != dirty.bottom) { + mAttachInfo.mIgnoreDirtyState = true; + } + // TODO: Do this in native canvas.setDensityScale(mDensity); } catch (Surface.OutOfResourcesException e) { @@ -1242,12 +1281,11 @@ public final class ViewRoot extends Handler implements ViewParent, // need to clear it before drawing so that the child will // properly re-composite its drawing on a transparent // background. This automatically respects the clip/dirty region - if (!canvas.isOpaque()) { - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); - } else if (yoff != 0) { - // If we are applying an offset, we need to clear the area - // where the offset doesn't appear to avoid having garbage - // left in the blank areas. + // or + // If we are applying an offset, we need to clear the area + // where the offset doesn't appear to avoid having garbage + // left in the blank areas. + if (!canvas.isOpaque() || yoff != 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } @@ -1256,27 +1294,27 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); mView.mPrivateFlags |= View.DRAWN; - float scale = mAppScale; - Context cxt = mView.getContext(); if (DEBUG_DRAW) { - Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale); + Context cxt = mView.getContext(); + Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + + ", metrics=" + mView.getContext().getResources().getDisplayMetrics()); } - int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scale != 1.0f) { - // re-scale this - canvas.scale(scale, scale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); - - if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { - mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); - } } finally { + mAttachInfo.mIgnoreDirtyState = false; canvas.restoreToCount(saveCount); } + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { @@ -1289,7 +1327,7 @@ public final class ViewRoot extends Handler implements ViewParent, EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); } } - + } finally { surface.unlockCanvasAndPost(canvas); } @@ -1297,7 +1335,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (LOCAL_LOGV) { Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost"); } - + if (scrolling) { mFullRedrawNeeded = true; scheduleTraversals(); @@ -1310,7 +1348,7 @@ public final class ViewRoot extends Handler implements ViewParent, final Rect vi = attachInfo.mVisibleInsets; int scrollY = 0; boolean handled = false; - + if (vi.left > ci.left || vi.top > ci.top || vi.right > ci.right || vi.bottom > ci.bottom) { // We'll assume that we aren't going to change the scroll @@ -1397,7 +1435,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); @@ -1411,10 +1449,10 @@ public final class ViewRoot extends Handler implements ViewParent, } mScrollY = scrollY; } - + return handled; } - + public void requestChildFocus(View child, View focused) { checkThread(); if (mFocusedView != focused) { @@ -1494,7 +1532,7 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } } - + /** * Return true if child is an ancestor of parent, (or equal to the parent). */ @@ -1568,10 +1606,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; } - if (event != null) { - event.scale(mAppScaleInverted); + if (event != null && mTranslator != null) { + mTranslator.translateEventInScreenToAppWindow(event); } - try { boolean handled; if (mView != null && mAdded && event != null) { @@ -1657,6 +1694,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)) { @@ -1691,16 +1729,17 @@ public final class ViewRoot extends Handler implements ViewParent, if (mGlWanted && !mUseGL) { initializeGL(); if (mGlCanvas != null) { - mGlCanvas.setViewport((int) (mWidth * mAppScale), - (int) (mHeight * mAppScale)); + float appScale = mAttachInfo.mApplicationScale; + mGlCanvas.setViewport( + (int) (mWidth * appScale), (int) (mHeight * appScale)); } } } } - + mLastWasImTarget = WindowManager.LayoutParams .mayUseInputMethod(mWindowAttributes.flags); - + InputMethodManager imm = InputMethodManager.peekInstance(); if (mView != null) { if (hasWindowFocus && imm != null && mLastWasImTarget) { @@ -1708,7 +1747,7 @@ public final class ViewRoot extends Handler implements ViewParent, } mView.dispatchWindowFocusChanged(hasWindowFocus); } - + // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. if (hasWindowFocus) { @@ -1726,6 +1765,10 @@ public final class ViewRoot extends Handler implements ViewParent, ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; mHasHadWindowFocus = true; } + + if (hasWindowFocus && mView != null) { + sendAccessibilityEvents(); + } } } break; case DIE: @@ -1892,9 +1935,6 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = false; } - if (event != null) { - event.scale(mAppScaleInverted); - } if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); @@ -2120,50 +2160,50 @@ public final class ViewRoot extends Handler implements ViewParent, } /** - * log motion events + * log motion events */ private static void captureMotionLog(String subTag, MotionEvent ev) { - //check dynamic switch + //check dynamic switch if (ev == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { return; - } - - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getX()).append(','); - sb.append(ev.getY()).append(','); - sb.append(ev.getPressure()).append(','); - sb.append(ev.getSize()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getXPrecision()).append(','); - sb.append(ev.getYPrecision()).append(','); - sb.append(ev.getDeviceId()).append(','); + } + + StringBuilder sb = new StringBuilder(subTag + ": "); + sb.append(ev.getDownTime()).append(','); + sb.append(ev.getEventTime()).append(','); + sb.append(ev.getAction()).append(','); + sb.append(ev.getX()).append(','); + sb.append(ev.getY()).append(','); + sb.append(ev.getPressure()).append(','); + sb.append(ev.getSize()).append(','); + sb.append(ev.getMetaState()).append(','); + sb.append(ev.getXPrecision()).append(','); + sb.append(ev.getYPrecision()).append(','); + sb.append(ev.getDeviceId()).append(','); sb.append(ev.getEdgeFlags()); - Log.d(TAG, sb.toString()); + Log.d(TAG, sb.toString()); } /** - * log motion events + * log motion events */ private static void captureKeyLog(String subTag, KeyEvent ev) { - //check dynamic switch - if (ev == null || + //check dynamic switch + if (ev == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { return; } - StringBuilder sb = new StringBuilder(subTag + ": "); + StringBuilder sb = new StringBuilder(subTag + ": "); sb.append(ev.getDownTime()).append(','); sb.append(ev.getEventTime()).append(','); sb.append(ev.getAction()).append(','); - sb.append(ev.getKeyCode()).append(','); + sb.append(ev.getKeyCode()).append(','); sb.append(ev.getRepeatCount()).append(','); sb.append(ev.getMetaState()).append(','); sb.append(ev.getDeviceId()).append(','); sb.append(ev.getScanCode()); - Log.d(TAG, sb.toString()); - } + Log.d(TAG, sb.toString()); + } int enqueuePendingEvent(Object event, boolean sendDone) { int seq = mPendingEventSeq+1; @@ -2181,7 +2221,7 @@ public final class ViewRoot extends Handler implements ViewParent, } return event; } - + private void deliverKeyEvent(KeyEvent event, boolean sendDone) { // If mView is null, we just consume the key event because it doesn't // make sense to do anything else with it. @@ -2238,7 +2278,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) { try { if (mView != null && mAdded) { @@ -2247,8 +2287,8 @@ public final class ViewRoot extends Handler implements ViewParent, if (checkForLeavingTouchModeAndConsume(event)) { return; - } - + } + if (Config.LOGV) { captureKeyLog("captureDispatchKeyEvent", event); } @@ -2324,24 +2364,31 @@ 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; - if (params != null && mAppScale != 1.0f) { + if (params != null && mTranslator != null) { restore = true; - params.scale(mAppScale, mWindowLayoutParamsBackup); + params.backup(); + mTranslator.translateWindowLayout(params); + } + if (params != null) { + if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); } int relayoutResult = sWindowSession.relayout( mWindow, params, - (int) (mView.mMeasuredWidth * mAppScale), - (int) (mView.mMeasuredHeight * mAppScale), + (int) (mView.mMeasuredWidth * appScale), + (int) (mView.mMeasuredHeight * appScale), viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mSurface); if (restore) { - params.restore(mWindowLayoutParamsBackup); + params.restore(); + } + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); + mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); } - - mPendingContentInsets.scale(mAppScaleInverted); - mPendingVisibleInsets.scale(mAppScaleInverted); - mWinFrame.scale(mAppScaleInverted); return relayoutResult; } @@ -2448,11 +2495,14 @@ public final class ViewRoot extends Handler implements ViewParent, + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); - - coveredInsets.scale(mAppScaleInverted); - visibleInsets.scale(mAppScaleInverted); - msg.arg1 = (int) (w * mAppScaleInverted); - msg.arg2 = (int) (h * mAppScaleInverted); + 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); } @@ -2511,6 +2561,21 @@ public final class ViewRoot extends Handler implements ViewParent, sendMessage(msg); } + /** + * The window is getting focus so if there is anything focused/selected + * send an {@link AccessibilityEvent} to announce that. + */ + private void sendAccessibilityEvents() { + if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) { + return; + } + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + View focusedView = mView.findFocus(); + if (focusedView != null && focusedView != mView) { + focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + } + public boolean showContextMenuForChild(View originalView) { return false; } @@ -2540,14 +2605,14 @@ public final class ViewRoot extends Handler implements ViewParent, boolean immediate) { return scrollToRectOrFocus(rectangle, immediate); } - + static class InputMethodCallback extends IInputMethodCallback.Stub { private WeakReference<ViewRoot> mViewRoot; public InputMethodCallback(ViewRoot viewRoot) { mViewRoot = new WeakReference<ViewRoot>(viewRoot); } - + public void finishedEvent(int seq, boolean handled) { final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { @@ -2559,13 +2624,13 @@ public final class ViewRoot extends Handler implements ViewParent, // Stub -- not for use in the client. } } - + static class EventCompletion extends Handler { final IWindow mWindow; final KeyEvent mKeyEvent; final boolean mIsPointer; final MotionEvent mMotionEvent; - + EventCompletion(Looper looper, IWindow window, KeyEvent key, boolean isPointer, MotionEvent motion) { super(looper); @@ -2575,7 +2640,7 @@ public final class ViewRoot extends Handler implements ViewParent, mMotionEvent = motion; sendEmptyMessage(0); } - + @Override public void handleMessage(Message msg) { if (mKeyEvent != null) { @@ -2617,7 +2682,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + static class W extends IWindow.Stub { private final WeakReference<ViewRoot> mViewRoot; private final Looper mMainLooper; @@ -2739,14 +2804,14 @@ public final class ViewRoot extends Handler implements ViewParent, * The maximum amount of acceleration we will apply. */ static final float MAX_ACCELERATION = 20; - + /** * The maximum amount of time (in milliseconds) between events in order * for us to consider the user to be doing fast trackball movements, * and thus apply an acceleration. */ static final long FAST_MOVE_TIME = 150; - + /** * Scaling factor to the time (in milliseconds) between events to how * much to multiple/divide the current acceleration. When movement @@ -2754,7 +2819,7 @@ public final class ViewRoot extends Handler implements ViewParent, * FAST_MOVE_TIME it divides it. */ static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); - + float position; float absPosition; float acceleration = 1; @@ -2806,7 +2871,7 @@ public final class ViewRoot extends Handler implements ViewParent, } else { normTime = 0; } - + // The number of milliseconds between each movement that is // considered "normal" and will not result in any acceleration // or deceleration, scaled by the offset we have here. @@ -2964,7 +3029,7 @@ public final class ViewRoot extends Handler implements ViewParent, sRunQueues.set(rq); return rq; } - + /** * @hide */ diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 428de67ee80e..d7457a030468 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -24,7 +24,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; -import android.util.Log; +import android.view.accessibility.AccessibilityEvent; /** * Abstract base class for a top-level window look and behavior policy. An @@ -153,7 +153,16 @@ public abstract class Window { * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent event); - + + /** + * Called to process population of {@link AccessibilityEvent}s. + * + * @param event The event. + * + * @return boolean Return true if event population was completed. + */ + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); + /** * Instantiate the view to display in the panel for 'featureId'. * You can return null, in which case the default content (typically @@ -367,8 +376,14 @@ public abstract class Window { String title; if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) { title="Media"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) { + title="MediaOvr"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { title="Panel"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) { + title="SubPanel"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) { + title="AtchDlg"; } else { title=Integer.toString(wp.type); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c69c28154c2d..bdb86d703b3b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -18,7 +18,6 @@ package android.view; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -210,6 +209,15 @@ public interface WindowManager extends ViewManager { public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3; /** + * Window type: window for showing overlays on top of media windows. + * These windows are displayed between TYPE_APPLICATION_MEDIA and the + * application window. They should be translucent to be useful. This + * is a big ugly hack so: + * @hide + */ + public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4; + + /** * End of types of sub-windows. */ public static final int LAST_SUB_WINDOW = 1999; @@ -466,6 +474,21 @@ public interface WindowManager extends ViewManager { */ public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000; + /** Window flag: special flag to let windows be shown when the screen + * is locked. This will let application windows take precedence over + * key guard or any other lock screens. Can be used with + * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows + * directly before showing the key guard window + * + * {@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. @@ -787,6 +810,7 @@ public interface WindowManager extends ViewManager { screenOrientation = in.readInt(); } + @SuppressWarnings({"PointlessBitwiseExpression"}) public static final int LAYOUT_CHANGED = 1<<0; public static final int TYPE_CHANGED = 1<<1; public static final int FLAGS_CHANGED = 1<<2; @@ -800,6 +824,9 @@ public interface WindowManager extends ViewManager { public static final int SCREEN_ORIENTATION_CHANGED = 1<<10; public static final int SCREEN_BRIGHTNESS_CHANGED = 1<<11; + // internal buffer to backup/restore parameters under compatibility mode. + private int[] mCompatibilityParamsBackup = null; + public final int copyFrom(LayoutParams o) { int changes = 0; @@ -957,36 +984,45 @@ public interface WindowManager extends ViewManager { /** * Scale the layout params' coordinates and size. - * Returns the original info as a backup so that the caller can - * restore the layout params; - */ - void scale(float scale, int[] backup) { - if (scale != 1.0f) { - backup[0] = x; - backup[1] = y; - x *= scale; - y *= scale; - if (width > 0) { - backup[2] = width; - width *= scale; - } - if (height > 0) { - backup[3] = height; - height *= scale; - } + * @hide + */ + public void scale(float scale) { + x *= scale; + y *= scale; + if (width > 0) { + width *= scale; + } + if (height > 0) { + height *= scale; } } /** - * Restore the layout params' coordinates and size. - */ - void restore(int[] backup) { - x = backup[0]; - y = backup[1]; - if (width > 0) { - width = backup[2]; + * Backup the layout parameters used in compatibility mode. + * @see LayoutParams#restore() + */ + void backup() { + int[] backup = mCompatibilityParamsBackup; + if (backup == null) { + // we backup 4 elements, x, y, width, height + backup = mCompatibilityParamsBackup = new int[4]; } - if (height > 0) { + backup[0] = x; + backup[1] = y; + backup[2] = width; + backup[3] = height; + } + + /** + * Restore the layout params' coordinates, size and gravity + * @see LayoutParams#backup() + */ + void restore() { + int[] backup = mCompatibilityParamsBackup; + if (backup != null) { + x = backup[0]; + y = backup[1]; + width = backup[2]; height = backup[3]; } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 755d7b8cf289..0973599509b1 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -173,7 +173,6 @@ public class WindowManagerImpl implements WindowManager { mRoots[index] = root; mParams[index] = wparams; } - // do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView); } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.aidl b/core/java/android/view/accessibility/AccessibilityEvent.aidl new file mode 100644 index 000000000000..cee360475036 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEvent.aidl @@ -0,0 +1,19 @@ +/** + * 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.view.accessibility; + +parcelable AccessibilityEvent; diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java new file mode 100644 index 000000000000..c22f991183b0 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -0,0 +1,734 @@ +/* + * 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.view.accessibility; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents accessibility events that are sent by the system when + * something notable happens in the user interface. For example, when a + * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. + * <p> + * This class represents various semantically different accessibility event + * types. Each event type has associated a set of related properties. In other + * words, each event type is characterized via a subset of the properties exposed + * by this class. For each event type there is a corresponding constant defined + * in this class. Since some event types are semantically close there are mask + * constants that group them together. Follows a specification of the event + * types and their associated properties: + * <p> + * <b>VIEW TYPES</b> <br> + * <p> + * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View} + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> + * Type:{@link #TYPE_VIEW_CLICKED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + * <p> + * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View} + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> + * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + * <p> + * <b>View selected</b> - represents the event of selecting an item usually in + * the context of an {@link android.widget.AdapterView}. <br> + * Type: {@link #TYPE_VIEW_SELECTED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + * <p> + * <b>View focused</b> - represents the event of focusing a + * {@link android.view.View}. <br> + * Type: {@link #TYPE_VIEW_FOCUSED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + * <p> + * <b>View text changed</b> - represents the event of changing the text of an + * {@link android.widget.EditText}. <br> + * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()}, + * {@link #getFromIndex()}, + * {@link #getAddedCount()}, + * {@link #getRemovedCount()}, + * {@link #getBeforeText()} + * <p> + * <b>TRANSITION TYPES</b> <br> + * <p> + * <b>Window state changed</b> - represents the event of opening/closing a + * {@link android.widget.PopupWindow}, {@link android.view.Menu}, + * {@link android.app.Dialog}, etc. <br> + * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()} + * <p> + * <b>NOTIFICATION TYPES</b> <br> + * <p> + * <b>Notification state changed</b> - represents the event showing/hiding + * {@link android.app.Notification}. + * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()} + * {@link #getParcelableData()} + * <p> + * <b>Security note</b> + * <p> + * Since an event contains the text of its source privacy can be compromised by leaking of + * sensitive information such as passwords. To address this issue any event fired in response + * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. + * + * @see android.view.accessibility.AccessibilityManager + * @see android.accessibilityservice.AccessibilityService + */ +public final class AccessibilityEvent implements Parcelable { + + /** + * Invalid selection/focus position. + * + * @see #getCurrentItemIndex() + */ + public static final int INVALID_POSITION = -1; + + /** + * Maximum length of the text fields. + * + * @see #getBeforeText() + * @see #getText() + */ + public static final int MAX_TEXT_LENGTH = 500; + + /** + * Represents the event of clicking on a {@link android.view.View} like + * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. + */ + public static final int TYPE_VIEW_CLICKED = 0x00000001; + + /** + * Represents the event of long clicking on a {@link android.view.View} like + * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. + */ + public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002; + + /** + * Represents the event of selecting an item usually in the context of an + * {@link android.widget.AdapterView}. + */ + public static final int TYPE_VIEW_SELECTED = 0x00000004; + + /** + * Represents the event of focusing a {@link android.view.View}. + */ + public static final int TYPE_VIEW_FOCUSED = 0x00000008; + + /** + * Represents the event of changing the text of an {@link android.widget.EditText}. + */ + public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; + + /** + * Represents the event of opening/closing a {@link android.widget.PopupWindow}, + * {@link android.view.Menu}, {@link android.app.Dialog}, etc. + */ + public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; + + /** + * Represents the event showing/hiding a {@link android.app.Notification}. + */ + public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; + + /** + * Mask for {@link AccessibilityEvent} all types. + * + * @see #TYPE_VIEW_CLICKED + * @see #TYPE_VIEW_LONG_CLICKED + * @see #TYPE_VIEW_SELECTED + * @see #TYPE_VIEW_FOCUSED + * @see #TYPE_VIEW_TEXT_CHANGED + * @see #TYPE_WINDOW_STATE_CHANGED + * @see #TYPE_NOTIFICATION_STATE_CHANGED + */ + public static final int TYPES_ALL_MASK = 0xFFFFFFFF; + + private static final int MAX_POOL_SIZE = 2; + private static final Object mPoolLock = new Object(); + private static AccessibilityEvent sPool; + private static int sPoolSize; + + private static final int CHECKED = 0x00000001; + private static final int ENABLED = 0x00000002; + private static final int PASSWORD = 0x00000004; + private static final int FULL_SCREEN = 0x00000080; + + private AccessibilityEvent mNext; + + private int mEventType; + private int mBooleanProperties; + private int mCurrentItemIndex; + private int mItemCount; + private int mFromIndex; + private int mAddedCount; + private int mRemovedCount; + + private long mEventTime; + + private CharSequence mClassName; + private CharSequence mPackageName; + private CharSequence mContentDescription; + private CharSequence mBeforeText; + + private Parcelable mParcelableData; + + private final List<CharSequence> mText = new ArrayList<CharSequence>(); + + private boolean mIsInPool; + + /* + * Hide constructor from clients. + */ + private AccessibilityEvent() { + mCurrentItemIndex = INVALID_POSITION; + } + + /** + * Gets if the source is checked. + * + * @return True if the view is checked, false otherwise. + */ + public boolean isChecked() { + return getBooleanProperty(CHECKED); + } + + /** + * Sets if the source is checked. + * + * @param isChecked True if the view is checked, false otherwise. + */ + public void setChecked(boolean isChecked) { + setBooleanProperty(CHECKED, isChecked); + } + + /** + * Gets if the source is enabled. + * + * @return True if the view is enabled, false otherwise. + */ + public boolean isEnabled() { + return getBooleanProperty(ENABLED); + } + + /** + * Sets if the source is enabled. + * + * @param isEnabled True if the view is enabled, false otherwise. + */ + public void setEnabled(boolean isEnabled) { + setBooleanProperty(ENABLED, isEnabled); + } + + /** + * Gets if the source is a password field. + * + * @return True if the view is a password field, false otherwise. + */ + public boolean isPassword() { + return getBooleanProperty(PASSWORD); + } + + /** + * Sets if the source is a password field. + * + * @param isPassword True if the view is a password field, false otherwise. + */ + public void setPassword(boolean isPassword) { + setBooleanProperty(PASSWORD, isPassword); + } + + /** + * Sets if the source is taking the entire screen. + * + * @param isFullScreen True if the source is full screen, false otherwise. + */ + public void setFullScreen(boolean isFullScreen) { + setBooleanProperty(FULL_SCREEN, isFullScreen); + } + + /** + * Gets if the source is taking the entire screen. + * + * @return True if the source is full screen, false otherwise. + */ + public boolean isFullScreen() { + return getBooleanProperty(FULL_SCREEN); + } + + /** + * Gets the event type. + * + * @return The event type. + */ + public int getEventType() { + return mEventType; + } + + /** + * Sets the event type. + * + * @param eventType The event type. + */ + public void setEventType(int eventType) { + mEventType = eventType; + } + + /** + * Gets the number of items that can be visited. + * + * @return The number of items. + */ + public int getItemCount() { + return mItemCount; + } + + /** + * Sets the number of items that can be visited. + * + * @param itemCount The number of items. + */ + public void setItemCount(int itemCount) { + mItemCount = itemCount; + } + + /** + * Gets the index of the source in the list of items the can be visited. + * + * @return The current item index. + */ + public int getCurrentItemIndex() { + return mCurrentItemIndex; + } + + /** + * Sets the index of the source in the list of items that can be visited. + * + * @param currentItemIndex The current item index. + */ + public void setCurrentItemIndex(int currentItemIndex) { + mCurrentItemIndex = currentItemIndex; + } + + /** + * Gets the index of the first character of the changed sequence. + * + * @return The index of the first character. + */ + public int getFromIndex() { + return mFromIndex; + } + + /** + * Sets the index of the first character of the changed sequence. + * + * @param fromIndex The index of the first character. + */ + public void setFromIndex(int fromIndex) { + mFromIndex = fromIndex; + } + + /** + * Gets the number of added characters. + * + * @return The number of added characters. + */ + public int getAddedCount() { + return mAddedCount; + } + + /** + * Sets the number of added characters. + * + * @param addedCount The number of added characters. + */ + public void setAddedCount(int addedCount) { + mAddedCount = addedCount; + } + + /** + * Gets the number of removed characters. + * + * @return The number of removed characters. + */ + public int getRemovedCount() { + return mRemovedCount; + } + + /** + * Sets the number of removed characters. + * + * @param removedCount The number of removed characters. + */ + public void setRemovedCount(int removedCount) { + mRemovedCount = removedCount; + } + + /** + * Gets the time in which this event was sent. + * + * @return The event time. + */ + public long getEventTime() { + return mEventTime; + } + + /** + * Sets the time in which this event was sent. + * + * @param eventTime The event time. + */ + public void setEventTime(long eventTime) { + mEventTime = eventTime; + } + + /** + * Gets the class name of the source. + * + * @return The class name. + */ + public CharSequence getClassName() { + return mClassName; + } + + /** + * Sets the class name of the source. + * + * @param className The lass name. + */ + public void setClassName(CharSequence className) { + mClassName = className; + } + + /** + * Gets the package name of the source. + * + * @return The package name. + */ + public CharSequence getPackageName() { + return mPackageName; + } + + /** + * Sets the package name of the source. + * + * @param packageName The package name. + */ + public void setPackageName(CharSequence packageName) { + mPackageName = packageName; + } + + /** + * Gets the text of the event. The index in the list represents the priority + * of the text. Specifically, the lower the index the higher the priority. + * + * @return The text. + */ + public List<CharSequence> getText() { + return mText; + } + + /** + * Sets the text before a change. + * + * @return The text before the change. + */ + public CharSequence getBeforeText() { + return mBeforeText; + } + + /** + * Sets the text before a change. + * + * @param beforeText The text before the change. + */ + public void setBeforeText(CharSequence beforeText) { + mBeforeText = beforeText; + } + + /** + * Gets the description of the source. + * + * @return The description. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the description of the source. + * + * @param contentDescription The description. + */ + public void setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + } + + /** + * Gets the {@link Parcelable} data. + * + * @return The parcelable data. + */ + public Parcelable getParcelableData() { + return mParcelableData; + } + + /** + * Sets the {@link Parcelable} data of the event. + * + * @param parcelableData The parcelable data. + */ + public void setParcelableData(Parcelable parcelableData) { + mParcelableData = parcelableData; + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated with type property set. + * + * @param eventType The event type. + * @return An instance. + */ + public static AccessibilityEvent obtain(int eventType) { + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(eventType); + return event; + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated. + * + * @return An instance. + */ + public static AccessibilityEvent obtain() { + synchronized (mPoolLock) { + if (sPool != null) { + AccessibilityEvent event = sPool; + sPool = sPool.mNext; + sPoolSize--; + event.mNext = null; + event.mIsInPool = false; + return event; + } + return new AccessibilityEvent(); + } + } + + /** + * Return an instance back to be reused. + * <p> + * <b>Note: You must not touch the object after calling this function.</b> + */ + public void recycle() { + if (mIsInPool) { + return; + } + + clear(); + synchronized (mPoolLock) { + if (sPoolSize <= MAX_POOL_SIZE) { + mNext = sPool; + sPool = this; + mIsInPool = true; + sPoolSize++; + } + } + } + + /** + * Clears the state of this instance. + */ + private void clear() { + mEventType = 0; + mBooleanProperties = 0; + mCurrentItemIndex = INVALID_POSITION; + mItemCount = 0; + mFromIndex = 0; + mAddedCount = 0; + mRemovedCount = 0; + mEventTime = 0; + mClassName = null; + mPackageName = null; + mContentDescription = null; + mBeforeText = null; + mText.clear(); + } + + /** + * Gets the value of a boolean property. + * + * @param property The property. + * @return The value. + */ + private boolean getBooleanProperty(int property) { + return (mBooleanProperties & property) == property; + } + + /** + * Sets a boolean property. + * + * @param property The property. + * @param value The value. + */ + private void setBooleanProperty(int property, boolean value) { + if (value) { + mBooleanProperties |= property; + } else { + mBooleanProperties &= ~property; + } + } + + /** + * Creates a new instance from a {@link Parcel}. + * + * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. + */ + public void initFromParcel(Parcel parcel) { + mEventType = parcel.readInt(); + mBooleanProperties = parcel.readInt(); + mCurrentItemIndex = parcel.readInt(); + mItemCount = parcel.readInt(); + mFromIndex = parcel.readInt(); + mAddedCount = parcel.readInt(); + mRemovedCount = parcel.readInt(); + mEventTime = parcel.readLong(); + mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mParcelableData = parcel.readParcelable(null); + parcel.readList(mText, null); + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mEventType); + parcel.writeInt(mBooleanProperties); + parcel.writeInt(mCurrentItemIndex); + parcel.writeInt(mItemCount); + parcel.writeInt(mFromIndex); + parcel.writeInt(mAddedCount); + parcel.writeInt(mRemovedCount); + parcel.writeLong(mEventTime); + TextUtils.writeToParcel(mClassName, parcel, 0); + TextUtils.writeToParcel(mPackageName, parcel, 0); + TextUtils.writeToParcel(mContentDescription, parcel, 0); + TextUtils.writeToParcel(mBeforeText, parcel, 0); + parcel.writeParcelable(mParcelableData, flags); + parcel.writeList(mText); + } + + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(super.toString()); + builder.append("; EventType: " + mEventType); + builder.append("; EventTime: " + mEventTime); + builder.append("; ClassName: " + mClassName); + builder.append("; PackageName: " + mPackageName); + builder.append("; Text: " + mText); + builder.append("; ContentDescription: " + mContentDescription); + builder.append("; ItemCount: " + mItemCount); + builder.append("; CurrentItemIndex: " + mCurrentItemIndex); + builder.append("; IsEnabled: " + isEnabled()); + builder.append("; IsPassword: " + isPassword()); + builder.append("; IsChecked: " + isChecked()); + builder.append("; IsFullScreen: " + isFullScreen()); + builder.append("; BeforeText: " + mBeforeText); + builder.append("; FromIndex: " + mFromIndex); + builder.append("; AddedCount: " + mAddedCount); + builder.append("; RemovedCount: " + mRemovedCount); + builder.append("; ParcelableData: " + mParcelableData); + return builder.toString(); + } + + /** + * @see Parcelable.Creator + */ + public static final Parcelable.Creator<AccessibilityEvent> CREATOR = + new Parcelable.Creator<AccessibilityEvent>() { + public AccessibilityEvent createFromParcel(Parcel parcel) { + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.initFromParcel(parcel); + return event; + } + + public AccessibilityEvent[] newArray(int size) { + return new AccessibilityEvent[size]; + } + }; +} diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java new file mode 100644 index 000000000000..3d70959b1ef9 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEventSource.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +/** + * This interface is implemented by classes source of {@link AccessibilityEvent}s. + */ +public interface AccessibilityEventSource { + + /** + * Handles the request for sending an {@link AccessibilityEvent} given + * the event type. The method must first check if accessibility is on + * via calling {@link AccessibilityManager#isEnabled()}, obtain + * an {@link AccessibilityEvent} from the event pool through calling + * {@link AccessibilityEvent#obtain(int)}, populate the event, and + * send it for dispatch via calling + * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}. + * + * @see AccessibilityEvent + * @see AccessibilityManager + * + * @param eventType The event type. + */ + public void sendAccessibilityEvent(int eventType); + + /** + * Handles the request for sending an {@link AccessibilityEvent}. The + * method does not guarantee to check if accessibility is on before + * sending the event for dispatch. It is responsibility of the caller + * to do the check via calling {@link AccessibilityManager#isEnabled()}. + * + * @see AccessibilityEvent + * @see AccessibilityManager + * + * @param event The event. + */ + public void sendAccessibilityEventUnchecked(AccessibilityEvent event); +} diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java new file mode 100644 index 000000000000..01862700097d --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -0,0 +1,198 @@ +/* + * 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.view.accessibility; + +import static android.util.Config.LOGV; + +import android.content.Context; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + +/** + * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. + * Such events are generated when something notable happens in the user interface, + * for example an {@link android.app.Activity} starts, the focus or selection of a + * {@link android.view.View} changes etc. Parties interested in handling accessibility + * events implement and register an accessibility service which extends + * {@link android.accessibilityservice.AccessibilityService}. + * + * @see AccessibilityEvent + * @see android.accessibilityservice.AccessibilityService + * @see android.content.Context#getSystemService + */ +public final class AccessibilityManager { + private static final String LOG_TAG = "AccessibilityManager"; + + static final Object sInstanceSync = new Object(); + + private static AccessibilityManager sInstance; + + private static final int DO_SET_ENABLED = 10; + + final IAccessibilityManager mService; + + final Handler mHandler; + + boolean mIsEnabled; + + final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { + public void setEnabled(boolean enabled) { + mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); + } + }; + + class MyHandler extends Handler { + + MyHandler(Looper mainLooper) { + super(mainLooper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case DO_SET_ENABLED : + synchronized (mHandler) { + mIsEnabled = (message.arg1 == 1); + } + return; + default : + Log.w(LOG_TAG, "Unknown message type: " + message.what); + } + } + } + + /** + * Get an AccessibilityManager instance (create one if necessary). + * + * @hide + */ + public static AccessibilityManager getInstance(Context context) { + synchronized (sInstanceSync) { + if (sInstance == null) { + sInstance = new AccessibilityManager(context); + } + } + return sInstance; + } + + /** + * Create an instance. + * + * @param context A {@link Context}. + */ + private AccessibilityManager(Context context) { + mHandler = new MyHandler(context.getMainLooper()); + IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); + mService = IAccessibilityManager.Stub.asInterface(iBinder); + try { + mService.addClient(mClient); + } catch (RemoteException re) { + Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); + } + } + + /** + * Returns if the {@link AccessibilityManager} is enabled. + * + * @return True if this {@link AccessibilityManager} is enabled, false otherwise. + */ + public boolean isEnabled() { + synchronized (mHandler) { + return mIsEnabled; + } + } + + /** + * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not + * enabled the call is a NOOP. + * + * @param event The {@link AccessibilityEvent}. + * + * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} + * while accessibility is not enabled. + */ + public void sendAccessibilityEvent(AccessibilityEvent event) { + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + boolean doRecycle = false; + try { + event.setEventTime(SystemClock.uptimeMillis()); + // it is possible that this manager is in the same process as the service but + // client using it is called through Binder from another process. Example: MMS + // app adds a SMS notification and the NotificationManagerService calls this method + long identityToken = Binder.clearCallingIdentity(); + doRecycle = mService.sendAccessibilityEvent(event); + Binder.restoreCallingIdentity(identityToken); + if (LOGV) { + Log.i(LOG_TAG, event + " sent"); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error during sending " + event + " ", re); + } finally { + if (doRecycle) { + event.recycle(); + } + } + } + + /** + * Requests interruption of the accessibility feedback from all accessibility services. + */ + public void interrupt() { + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + try { + mService.interrupt(); + if (LOGV) { + Log.i(LOG_TAG, "Requested interrupt from all services"); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); + } + } + + /** + * Returns the {@link ServiceInfo}s of the installed accessibility services. + * + * @return An unmodifiable list with {@link ServiceInfo}s. + */ + public List<ServiceInfo> getAccessibilityServiceList() { + List<ServiceInfo> services = null; + try { + services = mService.getAccessibilityServiceList(); + if (LOGV) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); + } + return Collections.unmodifiableList(services); + } +} diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl new file mode 100644 index 000000000000..32788be63670 --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -0,0 +1,39 @@ +/* //device/java/android/android/app/INotificationManager.aidl +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view.accessibility; + +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.IAccessibilityManagerClient; +import android.content.pm.ServiceInfo; + +/** + * Interface implemented by the AccessibilityManagerService called by + * the AccessibilityMasngers. + * + * @hide + */ +interface IAccessibilityManager { + + void addClient(IAccessibilityManagerClient client); + + boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent); + + List<ServiceInfo> getAccessibilityServiceList(); + + void interrupt(); +} diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl new file mode 100644 index 000000000000..1eb60fc61b48 --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +/** + * Interface a client of the IAccessibilityManager implements to + * receive information about changes in the manager state. + * + * @hide + */ +oneway interface IAccessibilityManagerClient { + + void setEnabled(boolean enabled); + +} diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 11de3e2ed433..739373792047 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -297,6 +297,10 @@ public class BaseInputConnection implements InputConnection { b = tmp; } + if (a <= 0) { + return ""; + } + if (length > a) { length = a; } @@ -336,10 +340,19 @@ public class BaseInputConnection implements InputConnection { } /** - * The default implementation does nothing. + * The default implementation turns this into the enter key. */ public boolean performEditorAction(int actionCode) { - return false; + long eventTime = SystemClock.uptimeMillis(); + sendKeyEvent(new KeyEvent(eventTime, eventTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + return true; } /** @@ -488,12 +501,12 @@ public class BaseInputConnection implements InputConnection { } else { a = Selection.getSelectionStart(content); b = Selection.getSelectionEnd(content); - if (a >=0 && b>= 0 && a != b) { - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } + if (a < 0) a = 0; + if (b < 0) b = 0; + if (b < a) { + int tmp = a; + a = b; + b = tmp; } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index ba3f78cf22ec..dbd268291aa2 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -143,6 +143,17 @@ class BrowserFrame extends Handler { } /** + * Load a url with "POST" method from the network into the main frame. + * @param url The url to load. + * @param data The data for POST request. + */ + public void postUrl(String url, byte[] data) { + mLoadInitFromJava = true; + nativePostUrl(url, data); + mLoadInitFromJava = false; + } + + /** * Load the content as if it was loaded by the provided base URL. The * failUrl is used as the history entry for the load data. If null or * an empty string is passed for the failUrl, then no history entry is @@ -752,6 +763,8 @@ class BrowserFrame extends Handler { */ private native void nativeLoadUrl(String url); + private native void nativePostUrl(String url, byte[] postData); + private native void nativeLoadData(String baseUrl, String data, String mimeType, String encoding, String failUrl); diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java index 806b458fa8c0..145411cf7a6a 100644 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ b/core/java/android/webkit/ByteArrayBuilder.java @@ -17,6 +17,7 @@ package android.webkit; import java.util.LinkedList; +import java.util.ListIterator; /** Utility class optimized for accumulating bytes, and then spitting them back out. It does not optimize for returning the result in a @@ -94,6 +95,20 @@ class ByteArrayBuilder { return mChunks.isEmpty(); } + public int size() { + return mChunks.size(); + } + + public int getByteSize() { + int total = 0; + ListIterator<Chunk> it = mChunks.listIterator(0); + while (it.hasNext()) { + Chunk c = it.next(); + total += c.mLength; + } + return total; + } + public synchronized void clear() { Chunk c = getFirstChunk(); while (c != null) { diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 6f1b16047950..66ab0213543c 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -364,7 +364,7 @@ class FrameLoader { String cookie = CookieManager.getInstance().getCookie( mListener.getWebAddress()); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } } } diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 2a84683cdc86..1dbd007886f1 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -18,6 +18,7 @@ package android.webkit; import android.os.Handler; import android.os.Message; +import android.security.CertTool; import android.util.Log; final class JWebCoreJavaBridge extends Handler { @@ -186,6 +187,15 @@ final class JWebCoreJavaBridge extends Handler { mHasInstantTimer = false; } + private String[] getKeyStrengthList() { + return CertTool.getInstance().getSupportedKeyStrenghs(); + } + + private String getSignedPublicKey(int index, String challenge, String url) { + // generateKeyPair expects organizations which we don't have. Ignore url. + return CertTool.getInstance().generateKeyPair(index, challenge, null); + } + private native void nativeConstructor(); private native void nativeFinalize(); private native void sharedTimerFired(); diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index d583eb18a043..39360cd72a23 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -25,16 +25,16 @@ import android.net.http.HttpAuthHeader; import android.net.http.RequestHandle; import android.net.http.SslCertificate; import android.net.http.SslError; -import android.net.http.SslCertificate; import android.os.Handler; import android.os.Message; +import android.security.CertTool; import android.util.Log; import android.webkit.CacheManager.CacheResult; +import android.widget.Toast; import com.android.internal.R; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -72,6 +72,8 @@ class LoadListener extends Handler implements EventHandler { private static final int HTTP_NOT_FOUND = 404; private static final int HTTP_PROXY_AUTH = 407; + private static final String CERT_MIMETYPE = "application/x-x509-ca-cert"; + private static int sNativeLoaderCount; private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(8192); @@ -934,6 +936,12 @@ class LoadListener extends Handler implements EventHandler { // This commits the headers without checking the response status code. private void commitHeaders() { + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore in commitLoad. Do not call webcore. + return; + } + // Commit the headers to WebCore int nativeResponse = createNativeResponse(); // The native code deletes the native response object. @@ -974,6 +982,30 @@ class LoadListener extends Handler implements EventHandler { private void commitLoad() { if (mCancelled) return; + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore and stop the current loading so that it will not + // generate a new history page + byte[] cert = new byte[mDataBuilder.getByteSize()]; + int position = 0; + ByteArrayBuilder.Chunk c; + while (true) { + c = mDataBuilder.getFirstChunk(); + if (c == null) break; + + if (c.mLength != 0) { + System.arraycopy(c.mArray, 0, cert, position, c.mLength); + position += c.mLength; + } + mDataBuilder.releaseChunk(c); + } + CertTool.getInstance().addCertificate(cert, mContext); + Toast.makeText(mContext, R.string.certificateSaved, + Toast.LENGTH_SHORT).show(); + mBrowserFrame.stopLoading(); + return; + } + // Give the data to WebKit now PerfChecker checker = new PerfChecker(); ByteArrayBuilder.Chunk c; diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index 9de97c92267b..99de56d7db5d 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -538,7 +538,8 @@ import java.util.ArrayList; * removing the password input type. */ public void setSingleLine(boolean single) { - int inputType = EditorInfo.TYPE_CLASS_TEXT; + int inputType = EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; if (!single) { inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 105eacdca754..ec671d5dac52 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. @@ -123,7 +142,7 @@ public class WebSettings { private String mSerifFontFamily = "serif"; private String mCursiveFontFamily = "cursive"; private String mFantasyFontFamily = "fantasy"; - private String mDefaultTextEncoding = "Latin-1"; + private String mDefaultTextEncoding; private String mUserAgent; private boolean mUseDefaultUserAgent; private String mAcceptLanguage; @@ -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,12 @@ 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); if (sLockForLocaleSettings == null) { sLockForLocaleSettings = new Object(); @@ -445,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 563d819d301f..fcf946f7d12a 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -237,6 +237,7 @@ public class WebView extends AbsoluteLayout * Helper class to get velocity for fling */ VelocityTracker mVelocityTracker; + private int mMaximumFling; /** * Touch mode @@ -395,22 +396,27 @@ public class WebView extends AbsoluteLayout // width which view is considered to be fully zoomed out static final int ZOOM_OUT_WIDTH = 1008; - private static final float DEFAULT_MAX_ZOOM_SCALE = 4.0f; - private static final float DEFAULT_MIN_ZOOM_SCALE = 0.25f; + // default scale limit. Depending on the display density + private static float DEFAULT_MAX_ZOOM_SCALE; + private static float DEFAULT_MIN_ZOOM_SCALE; // scale limit, which can be set through viewport meta tag in the web page - private float mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; - private float mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; + private float mMaxZoomScale; + private float mMinZoomScale; private boolean mMinZoomScaleFixed = false; // initial scale in percent. 0 means using default. private int mInitialScale = 0; + // default scale. Depending on the display density. + static int DEFAULT_SCALE_PERCENT; + private float mDefaultScale; + // set to true temporarily while the zoom control is being dragged private boolean mPreviewZoomOnly = false; // computed scale and inverse, from mZoomWidth. - private float mActualScale = 1; - private float mInvActualScale = 1; + private float mActualScale; + private float mInvActualScale; // if this is non-zero, it is used on drawing rather than mActualScale private float mZoomScale; private float mInvInitialZoomScale; @@ -635,7 +641,7 @@ public class WebView extends AbsoluteLayout mZoomFitPageButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { - zoomWithPreview(1f); + zoomWithPreview(mDefaultScale); updateZoomButtonsEnabled(); } }); @@ -658,7 +664,7 @@ public class WebView extends AbsoluteLayout // or out. mZoomButtonsController.setZoomInEnabled(canZoomIn); mZoomButtonsController.setZoomOutEnabled(canZoomOut); - mZoomFitPageButton.setEnabled(mActualScale != 1); + mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale); } mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE: View.GONE); @@ -671,13 +677,41 @@ public class WebView extends AbsoluteLayout setClickable(true); setLongClickable(true); - final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + final int slop = configuration.getScaledTouchSlop(); mTouchSlopSquare = slop * slop; mMinLockSnapReverseDistance = slop; + final float density = getContext().getResources().getDisplayMetrics().density; // use one line height, 16 based on our current default font, for how // far we allow a touch be away from the edge of a link - mNavSlop = (int) (16 * getContext().getResources() - .getDisplayMetrics().density); + mNavSlop = (int) (16 * density); + // density adjusted scale factors + DEFAULT_SCALE_PERCENT = (int) (100 * density); + mDefaultScale = density; + mActualScale = density; + mInvActualScale = 1 / density; + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; + mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; + mMaximumFling = configuration.getScaledMaximumFlingVelocity(); + } + + /* 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, @@ -1118,6 +1152,29 @@ public class WebView extends AbsoluteLayout } /** + * Load the url with postData using "POST" method into the WebView. If url + * is not a network url, it will be loaded with {link + * {@link #loadUrl(String)} instead. + * + * @param url The url of the resource to load. + * @param postData The data will be passed to "POST" request. + * + * @hide pending API solidification + */ + public void postUrl(String url, byte[] postData) { + if (URLUtil.isNetworkUrl(url)) { + switchOutDrawHistory(); + HashMap arg = new HashMap(); + arg.put("url", url); + arg.put("data", postData); + mWebViewCore.sendMessage(EventHub.POST_URL, arg); + clearTextEntry(); + } else { + loadUrl(url); + } + } + + /** * Load the given data into the WebView. This will load the data into * WebView using the data: scheme. Content loaded through this mechanism * does not have the ability to load content from the network. @@ -4103,7 +4160,7 @@ public class WebView extends AbsoluteLayout int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0); int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0); - mVelocityTracker.computeCurrentVelocity(1000); + mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling); int vx = (int) mVelocityTracker.getXVelocity(); int vy = (int) mVelocityTracker.getYVelocity(); @@ -4134,9 +4191,9 @@ public class WebView extends AbsoluteLayout private boolean zoomWithPreview(float scale) { float oldScale = mActualScale; - // snap to 100% if it is close - if (scale > 0.95f && scale < 1.05f) { - scale = 1.0f; + // snap to DEFAULT_SCALE if it is close + if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) { + scale = mDefaultScale; } setNewZoomScale(scale, false); @@ -4517,9 +4574,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: @@ -4651,8 +4710,8 @@ public class WebView extends AbsoluteLayout } int initialScale = msg.arg1; int viewportWidth = msg.arg2; - // by default starting a new page with 100% zoom scale. - float scale = 1.0f; + // start a new page with DEFAULT_SCALE zoom 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 e9df453b78f1..a5fa41e8ed18 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -97,7 +97,7 @@ final class WebViewCore { private boolean mViewportUserScalable = true; - private int mRestoredScale = 100; + private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT; private int mRestoredX = 0; private int mRestoredY = 0; @@ -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(); @@ -544,6 +544,8 @@ final class WebViewCore { "WEBKIT_DRAW", // = 130; "SYNC_SCROLL", // = 131; "REFRESH_PLUGINS", // = 132; + // this will replace REFRESH_PLUGINS in the next release + "POST_URL", // = 142; "SPLIT_PICTURE_SET", // = 133; "CLEAR_CONTENT", // = 134; "SET_FINAL_FOCUS", // = 135; @@ -589,6 +591,8 @@ final class WebViewCore { static final int WEBKIT_DRAW = 130; static final int SYNC_SCROLL = 131; static final int REFRESH_PLUGINS = 132; + // this will replace REFRESH_PLUGINS in the next release + static final int POST_URL = 142; static final int SPLIT_PICTURE_SET = 133; static final int CLEAR_CONTENT = 134; @@ -672,6 +676,13 @@ final class WebViewCore { loadUrl((String) msg.obj); break; + case POST_URL: { + HashMap param = (HashMap) msg.obj; + String url = (String) param.get("url"); + byte[] data = (byte[]) param.get("data"); + mBrowserFrame.postUrl(url, data); + break; + } case LOAD_DATA: HashMap loadParams = (HashMap) msg.obj; String baseUrl = (String) loadParams.get("baseUrl"); @@ -1549,19 +1560,33 @@ 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) { - mViewportInitialScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale == 0) { - mViewportMinimumScale = 100; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; } } if (mViewportUserScalable == false) { - mViewportInitialScale = 100; - mViewportMinimumScale = 100; - mViewportMaximumScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale > mViewportInitialScale) { if (mViewportInitialScale == 0) { @@ -1575,9 +1600,10 @@ final class WebViewCore { mViewportMaximumScale = mViewportInitialScale; } else if (mViewportInitialScale == 0) { mViewportInitialScale = mViewportMaximumScale; - } + } } - if (mViewportWidth < 0 && mViewportInitialScale == 100) { + if (mViewportWidth < 0 + && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { mViewportWidth = 0; } diff --git a/core/java/android/webkit/gears/AndroidRadioDataProvider.java b/core/java/android/webkit/gears/AndroidRadioDataProvider.java index 2d431a8d6162..13840428a8b7 100644 --- a/core/java/android/webkit/gears/AndroidRadioDataProvider.java +++ b/core/java/android/webkit/gears/AndroidRadioDataProvider.java @@ -28,6 +28,7 @@ package android.webkit.gears; import android.content.Context; import android.telephony.CellLocation; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.gsm.GsmCellLocation; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; @@ -54,6 +55,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { public static final class RadioData { public int cellId = -1; public int locationAreaCode = -1; + // TODO: use new SignalStrength instead of asu public int signalStrength = -1; public int mobileCountryCode = -1; public int mobileNetworkCode = -1; @@ -179,6 +181,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { private CellLocation cellLocation = null; /** The last known signal strength */ + // TODO: use new SignalStrength instead of asu private int signalStrength = -1; /** The last known serviceState */ @@ -207,7 +210,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { // Register for cell id, signal strength and service state changed // notifications. telephonyManager.listen(this, PhoneStateListener.LISTEN_CELL_LOCATION - | PhoneStateListener.LISTEN_SIGNAL_STRENGTH + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE); } @@ -226,8 +229,9 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { } @Override - public void onSignalStrengthChanged(int asu) { - signalStrength = asu; + public void onSignalStrengthsChanged(SignalStrength ss) { + int gsmSignalStrength = ss.getGsmSignalStrength(); + signalStrength = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); notifyListeners(); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 1ca59b2430c7..f9ca8cb7a43b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -54,7 +54,9 @@ import java.util.ArrayList; import java.util.List; /** - * Common code shared between ListView and GridView + * Base class that can be used to implement virtualized lists of items. A list does + * not have a spatial definition here. For instance, subclases of this class can + * display the content of the list in a grid, in a carousel, as stack, etc. * * @attr ref android.R.styleable#AbsListView_listSelector * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop @@ -86,7 +88,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public static final int TRANSCRIPT_MODE_NORMAL = 1; /** * The list will automatically scroll to the bottom, no matter what items - * are currently visible. + * are currently visible. * * @see #setTranscriptMode(int) */ @@ -123,7 +125,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * Indicates the view is in the process of being flung */ static final int TOUCH_MODE_FLING = 4; - + /** * Indicates that the user is currently dragging the fast scroll thumb */ @@ -316,7 +318,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * bitmap cache after scrolling. */ boolean mScrollingCacheEnabled; - + /** * Whether or not to enable the fast scroll feature on this list */ @@ -389,7 +391,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * The last CheckForTap runnable we posted, if any */ private Runnable mPendingCheckForTap; - + /** * The last CheckForKeyLongPress runnable we posted, if any */ @@ -427,14 +429,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ private FastScroller mFastScroller; - private int mTouchSlop; + private boolean mGlobalLayoutListenerAddedFilter; + private int mTouchSlop; private float mDensityScale; private InputConnection mDefInputConnection; private InputConnectionWrapper mPublicInputConnection; private Runnable mClearScrollingCache; + private int mMinimumVelocity; + private int mMaximumVelocity; /** * Interface definition for a callback to be invoked when the list or grid @@ -529,21 +534,35 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0); setCacheColorHint(color); - + boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false); setFastScrollEnabled(enableFastScroll); boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true); setSmoothScrollbarEnabled(smoothScrollbar); - + a.recycle(); } + private void initAbsListView() { + // Setting focusable in touch mode will set the focusable property to true + setFocusableInTouchMode(true); + setWillNotDraw(false); + setAlwaysDrawnWithCacheEnabled(false); + setScrollingCacheEnabled(true); + + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mDensityScale = getContext().getResources().getDisplayMetrics().density; + } + /** - * Enables fast scrolling by letting the user quickly scroll through lists by - * dragging the fast scroll thumb. The adapter attached to the list may want + * Enables fast scrolling by letting the user quickly scroll through lists by + * dragging the fast scroll thumb. The adapter attached to the list may want * to implement {@link SectionIndexer} if it wishes to display alphabet preview and - * jump between sections of the list. + * jump between sections of the list. * @see SectionIndexer * @see #isFastScrollEnabled() * @param enabled whether or not to enable fast scrolling @@ -561,7 +580,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - + /** * Returns the current state of the fast scroll feature. * @see #setFastScrollEnabled(boolean) @@ -571,10 +590,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public boolean isFastScrollEnabled() { return mFastScrollEnabled; } - + /** * If fast scroll is visible, then don't draw the vertical scrollbar. - * @hide + * @hide */ @Override protected boolean isVerticalScrollBarHidden() { @@ -592,11 +611,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * When smooth scrollbar is disabled, the position and size of the scrollbar thumb * is based solely on the number of items in the adapter and the position of the * visible items inside the adapter. This provides a stable scrollbar as the user - * navigates through a list of items with varying heights. + * navigates through a list of items with varying heights. * * @param enabled Whether or not to enable smooth scrollbar. * - * @see #setSmoothScrollbarEnabled(boolean) + * @see #setSmoothScrollbarEnabled(boolean) * @attr ref android.R.styleable#AbsListView_smoothScrollbar */ public void setSmoothScrollbarEnabled(boolean enabled) { @@ -712,17 +731,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - private void initAbsListView() { - // Setting focusable in touch mode will set the focusable property to true - setFocusableInTouchMode(true); - setWillNotDraw(false); - setAlwaysDrawnWithCacheEnabled(false); - setScrollingCacheEnabled(true); - - mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - mDensityScale = getContext().getResources().getDisplayMetrics().density; - } - private void useDefaultSelector() { setSelector(getResources().getDrawable( com.android.internal.R.drawable.list_selector_background)); @@ -828,7 +836,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public Parcelable onSaveInstanceState() { /* * This doesn't really make sense as the place to dismiss the - * popup, but there don't seem to be any other useful hooks + * popups, but there don't seem to be any other useful hooks * that happen early enough to keep from getting complaints * about having leaked the window. */ @@ -908,17 +916,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private boolean acceptFilter() { - if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) || - ((Filterable) getAdapter()).getFilter() == null) { - return false; - } - return true; + return mTextFilterEnabled && getAdapter() instanceof Filterable && + ((Filterable) getAdapter()).getFilter() != null; } /** * Sets the initial value for the text filter. * @param filterText The text to use for the filter. - * + * * @see #setTextFilterEnabled */ public void setFilterText(String filterText) { @@ -944,7 +949,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** - * Returns the list's text filter, if available. + * Returns the list's text filter, if available. * @return the list's text filter or null if filtering isn't enabled */ public CharSequence getTextFilter() { @@ -953,7 +958,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } return null; } - + @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); @@ -1096,6 +1101,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te listPadding.bottom = mSelectionBottomPadding + mPaddingBottom; } + /** + * Subclasses should NOT override this method but + * {@link #layoutChildren()} instead. + */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); @@ -1111,17 +1120,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te protected boolean setFrame(int left, int top, int right, int bottom) { final boolean changed = super.setFrame(left, top, right, bottom); - // Reposition the popup when the frame has changed. This includes - // translating the widget, not just changing its dimension. The - // filter popup needs to follow the widget. - if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null && - mPopup.isShowing()) { - positionPopup(); + if (changed) { + // Reposition the popup when the frame has changed. This includes + // translating the widget, not just changing its dimension. The + // filter popup needs to follow the widget. + final boolean visible = getWindowVisibility() == View.VISIBLE; + if (mFiltered && visible && mPopup != null && mPopup.isShowing()) { + positionPopup(); + } } return changed; } + /** + * Subclasses must override this method to layout their children. + */ protected void layoutChildren() { } @@ -1324,6 +1338,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mDataChanged = true; rememberSyncState(); } + if (mFastScroller != null) { mFastScroller.onSizeChanged(w, h, oldw, oldh); } @@ -1494,7 +1509,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te System.arraycopy(state, enabledPos + 1, state, enabledPos, state.length - enabledPos - 1); } - + return state; } @@ -1510,6 +1525,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final ViewTreeObserver treeObserver = getViewTreeObserver(); if (treeObserver != null) { treeObserver.addOnTouchModeChangeListener(this); + if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) { + treeObserver.addOnGlobalLayoutListener(this); + } } } @@ -1520,6 +1538,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final ViewTreeObserver treeObserver = getViewTreeObserver(); if (treeObserver != null) { treeObserver.removeOnTouchModeChangeListener(this); + if (mTextFilterEnabled && mPopup != null) { + treeObserver.removeGlobalOnLayoutListener(this); + mGlobalLayoutListenerAddedFilter = false; + } } } @@ -1586,16 +1608,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ private class WindowRunnnable { private int mOriginalAttachCount; - + public void rememberWindowAttachCount() { mOriginalAttachCount = getWindowAttachCount(); } - + public boolean sameWindow() { return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount; } } - + private class PerformClick extends WindowRunnnable implements Runnable { View mChild; int mClickMotionPosition; @@ -1622,7 +1644,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final long longPressId = mAdapter.getItemId(mMotionPosition); boolean handled = false; - if (sameWindow() && !mDataChanged) { + if (sameWindow() && !mDataChanged) { handled = performLongPress(child, longPressPosition, longPressId); } if (handled) { @@ -1636,7 +1658,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - + private class CheckForKeyLongPress extends WindowRunnnable implements Runnable { public void run() { if (isPressed() && mSelectedPosition >= 0) { @@ -1812,7 +1834,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_DONE_WAITING; } } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; + mTouchMode = TOUCH_MODE_DONE_WAITING; } } } @@ -1867,13 +1889,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public boolean onTouchEvent(MotionEvent ev) { - if (mFastScroller != null) { boolean intercepted = mFastScroller.onTouchEvent(ev); if (intercepted) { return true; - } + } } + final int action = ev.getAction(); final int x = (int) ev.getX(); final int y = (int) ev.getY(); @@ -2041,12 +2063,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; case TOUCH_MODE_SCROLL: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); - int initialVelocity = (int)velocityTracker.getYVelocity(); - - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - (getChildCount() > 0)) { + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int initialVelocity = (int) velocityTracker.getYVelocity(); + if (Math.abs(initialVelocity) > mMinimumVelocity && (getChildCount() > 0)) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } @@ -2059,10 +2078,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } setPressed(false); - + // Need to redraw since we probably aren't drawing the selector anymore invalidate(); - + final Handler handler = getHandler(); if (handler != null) { handler.removeCallbacks(mPendingCheckForLongPress); @@ -2106,7 +2125,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return true; } - + @Override public void draw(Canvas canvas) { super.draw(canvas); @@ -2121,14 +2140,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te int x = (int) ev.getX(); int y = (int) ev.getY(); View v; - + if (mFastScroller != null) { boolean intercepted = mFastScroller.onInterceptTouchEvent(ev); if (intercepted) { return true; } } - + switch (action) { case MotionEvent.ACTION_DOWN: { int motionPosition = findMotionRow(y); @@ -2775,7 +2794,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Removes the filter window */ - void dismissPopup() { + private void dismissPopup() { if (mPopup != null) { mPopup.dismiss(); } @@ -2978,7 +2997,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } return null; } - + /** * For filtering we proxy an input connection to an internal text editor, * and this allows the proxying to happen. @@ -2987,7 +3006,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public boolean checkInputConnectionProxy(View view) { return view == mTextFilter; } - + /** * Creates the window for the text filter and populates it with an EditText field; * @@ -3017,6 +3036,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te p.setBackgroundDrawable(null); mPopup = p; getViewTreeObserver().addOnGlobalLayoutListener(this); + mGlobalLayoutListenerAddedFilter = true; } if (animateEntrance) { mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilter); @@ -3379,7 +3399,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; } - + public boolean shouldRecycleViewType(int viewType) { return viewType >= 0; } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 173e80f54f75..7d2fcbc0fa41 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -24,11 +24,12 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; +import android.view.SoundEffectConstants; import android.view.View; -import android.view.ViewGroup; import android.view.ViewDebug; -import android.view.SoundEffectConstants; +import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; +import android.view.accessibility.AccessibilityEvent; /** @@ -618,7 +619,9 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } /** - * Sets the currently selected item + * Sets the currently selected item. To support accessibility subclasses that + * override this method must invoke the overriden super method first. + * * @param position Index (starting at 0) of the data item to be selected. */ public abstract void setSelection(int position); @@ -844,6 +847,11 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { fireOnSelected(); } } + + // we fire selection events here not in View + if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + } } private void fireOnSelected() { @@ -861,6 +869,35 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = false; + // This is an exceptional case which occurs when a window gets the + // focus and sends a focus event via its focused child to announce + // current focus/selection. AdapterView fires selection but not focus + // events so we change the event type here. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + } + + // we send selection events only from AdapterView to avoid + // generation of such event for each child + View selectedView = getSelectedView(); + if (selectedView != null) { + populated = selectedView.dispatchPopulateAccessibilityEvent(event); + } + + if (!populated) { + if (selectedView != null) { + event.setEnabled(selectedView.isEnabled()); + } + event.setItemCount(getCount()); + event.setCurrentItemIndex(getSelectedItemPosition()); + } + + return populated; + } + + @Override protected boolean canAnimate() { return super.canAnimate() && mItemCount > 0; } diff --git a/core/java/android/widget/AlphabetIndexer.java b/core/java/android/widget/AlphabetIndexer.java index 4e466a09de7e..f50676ab3914 100644 --- a/core/java/android/widget/AlphabetIndexer.java +++ b/core/java/android/widget/AlphabetIndexer.java @@ -248,8 +248,8 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer { public int getSectionForPosition(int position) { int savedCursorPos = mDataCursor.getPosition(); mDataCursor.moveToPosition(position); - mDataCursor.moveToPosition(savedCursorPos); String curName = mDataCursor.getString(mColumnIndex); + mDataCursor.moveToPosition(savedCursorPos); // Linear search, as there are only a few items in the section index // Could speed this up later if it actually gets used. for (int i = 0; i < mAlphabetLength; i++) { diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 5fa00e7d443a..c4b5ef891558 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -124,25 +124,25 @@ public class AppSecurityPermissions implements View.OnClickListener { if(pkg == null) { return; } - // Extract shared user permissions if any + // Get requested permissions + if (pkg.requestedPermissions != null) { + ArrayList<String> strList = pkg.requestedPermissions; + int size = strList.size(); + if (size > 0) { + extractPerms(strList.toArray(new String[size]), permSet); + } + } + // Get permissions related to shared user if any if(pkg.mSharedUserId != null) { int sharedUid; try { sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); + getAllUsedPermissions(sharedUid, permSet); } catch (NameNotFoundException e) { Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); - return; } - getAllUsedPermissions(sharedUid, permSet); - } else { - ArrayList<String> strList = pkg.requestedPermissions; - int size; - if((strList == null) || ((size = strList.size()) == 0)) { - return; - } - // Extract permissions defined in current package - extractPerms(strList.toArray(new String[size]), permSet); } + // Retrieve list of permissions for(PermissionInfo tmpInfo : permSet) { mPermsList.add(tmpInfo); } @@ -176,14 +176,9 @@ public class AppSecurityPermissions implements View.OnClickListener { Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName); return; } - if(pkgInfo == null) { - return; - } - String strList[] = pkgInfo.requestedPermissions; - if(strList == null) { - return; + if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) { + extractPerms(pkgInfo.requestedPermissions, permSet); } - extractPerms(strList, permSet); } private void extractPerms(String strList[], Set<PermissionInfo> permSet) { diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index c28210df6864..32e55048ed18 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -348,7 +348,12 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { "ArrayAdapter requires the resource ID to be a TextView", e); } - text.setText(getItem(position).toString()); + T item = getItem(position); + if (item instanceof CharSequence) { + text.setText((CharSequence)item); + } else { + text.setText(item.toString()); + } return view; } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index a1d16eadc0d8..675aba23ae81 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; @@ -122,10 +124,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private boolean mBlockCompletion; private AutoCompleteTextView.ListSelectorHider mHideSelector; - - // Indicates whether this AutoCompleteTextView is attached to a window or not - // The widget is attached to a window when mAttachCount > 0 - private int mAttachCount; + private Runnable mShowDropDownRunnable; private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener; @@ -170,6 +169,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); @@ -258,6 +259,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> @@ -589,7 +618,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe if (isPopupShowing()) { // special case for the back key, we do not even try to send it // to the drop down list but instead, consume it immediately - if (keyCode == KeyEvent.KEYCODE_BACK) { + if (keyCode == KeyEvent.KEYCODE_BACK && !mDropDownAlwaysVisible) { dismissDropDown(); return true; } @@ -637,15 +666,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.getAdapter().getCount() - 1)) { // When the selection is at the top, we block the key // event to prevent focus from moving. - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + clearListSelection(); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); - mPopup.update(); + showDropDown(); return true; + } else { + // WARNING: Please read the comment where mListSelectionHidden + // is declared + mDropDownList.mListSelectionHidden = false; } + consumed = mDropDownList.onKeyDown(keyCode, event); - if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" - + consumed); + if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed); + if (consumed) { // If it handled the key event, then the user is // navigating in the list, so we should put it in front. @@ -655,7 +688,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 @@ -755,7 +788,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } else { // drop down is automatically dismissed when enough characters // are deleted from the text view - dismissDropDown(); + if (!mDropDownAlwaysVisible) dismissDropDown(); if (mFilter != null) { mFilter.filter(null); } @@ -788,9 +821,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * it back. */ public void clearListSelection() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + final DropDownListView list = mDropDownList; + if (list != null) { + // WARNING: Please read the comment where mListSelectionHidden is declared + list.mListSelectionHidden = true; + list.hideSelector(); + list.requestLayout(); } } @@ -801,6 +837,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe */ public void setListSelection(int position) { if (mPopup.isShowing() && (mDropDownList != null)) { + mDropDownList.mListSelectionHidden = false; mDropDownList.setSelection(position); // ListView.setSelection() will call requestLayout() } @@ -893,7 +930,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } - if (mDropDownDismissedOnCompletion) { + if (mDropDownDismissedOnCompletion && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -950,6 +987,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * @param text the selected suggestion in the drop down list */ protected void replaceText(CharSequence text) { + clearComposingText(); + setText(text); // make sure we keep the caret at the end of the text view Editable spannable = getText(); @@ -958,7 +997,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** {@inheritDoc} */ public void onFilterComplete(int count) { - if (mAttachCount <= 0) return; + // Not attached to window, don't update drop-down + if (getWindowVisibility() == View.GONE) return; /* * This checks enoughToFilter() again because filtering requests @@ -971,7 +1011,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe if (hasFocus() && hasWindowFocus()) { showDropDown(); } - } else { + } else if (!mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -980,7 +1020,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); performValidation(); - if (!hasWindowFocus) { + if (!hasWindowFocus && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -989,7 +1029,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); performValidation(); - if (!focused) { + if (!focused && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -997,13 +1037,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mAttachCount++; } @Override protected void onDetachedFromWindow() { dismissDropDown(); - mAttachCount--; super.onDetachedFromWindow(); } @@ -1044,12 +1082,26 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } /** + * Issues a runnable to show the dropdown as soon as possible. + * + * @hide internal used only by Search Dialog + */ + public void showDropDownAfterLayout() { + post(mShowDropDownRunnable); + } + + /** * <p>Displays the drop down on screen.</p> */ 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. @@ -1059,20 +1111,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 @@ -1082,8 +1165,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.showAsDropDown(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset); mDropDownList.setSelection(ListView.INVALID_POSITION); - mDropDownList.hideSelector(); - mDropDownList.requestFocus(); + clearListSelection(); post(mHideSelector); } } @@ -1119,6 +1201,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mHideSelector = new ListSelectorHider(); + /** + * This Runnable exists for the sole purpose of checking if the view layout has got + * completed and if so call showDropDown to display the drop down. This is used to show + * the drop down as soon as possible after user opens up the search dialog, without + * waiting for the normal UI pipeline to do it's job which is slower than this method. + */ + mShowDropDownRunnable = new Runnable() { + public void run() { + // View layout should be all done before displaying the drop down. + View view = getDropDownAnchorView(); + if (view != null && view.getWindowToken() != null) { + showDropDown(); + } + } + }; + mDropDownList = new DropDownListView(context); mDropDownList.setSelector(mDropDownListHighlight); mDropDownList.setAdapter(mAdapter); @@ -1126,6 +1224,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.setOnItemClickListener(mDropDownItemClickListener); mDropDownList.setFocusable(true); mDropDownList.setFocusableInTouchMode(true); + mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, + int position, long id) { + + if (position != -1) { + DropDownListView dropDownList = mDropDownList; + + if (dropDownList != null) { + dropDownList.mListSelectionHidden = false; + } + } + } + + public void onNothingSelected(AdapterView<?> parent) { + } + }); if (mItemSelectedListener != null) { mDropDownList.setOnItemSelectedListener(mItemSelectedListener); @@ -1180,10 +1294,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) { @@ -1249,10 +1365,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private class ListSelectorHider implements Runnable { public void run() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); - } + clearListSelection(); } } @@ -1279,6 +1392,36 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * passed to the drop down; the list only looks focused.</p> */ private static class DropDownListView extends ListView { + /* + * WARNING: This is a workaround for a touch mode issue. + * + * Touch mode is propagated lazily to windows. This causes problems in + * the following scenario: + * - Type something in the AutoCompleteTextView and get some results + * - Move down with the d-pad to select an item in the list + * - Move up with the d-pad until the selection disappears + * - Type more text in the AutoCompleteTextView *using the soft keyboard* + * and get new results; you are now in touch mode + * - The selection comes back on the first item in the list, even though + * the list is supposed to be in touch mode + * + * Using the soft keyboard triggers the touch mode change but that change + * is propagated to our window only after the first list layout, therefore + * after the list attempts to resurrect the selection. + * + * The trick to work around this issue is to pretend the list is in touch + * mode when we know that the selection should not appear, that is when + * we know the user moved the selection away from the list. + * + * This boolean is set to true whenever we explicitely hide the list's + * selection and reset to false whenver we know the user moved the + * selection back to the list. + * + * When this boolean is true, isInTouchMode() returns true, otherwise it + * returns super.isInTouchMode(). + */ + private boolean mListSelectionHidden; + /** * <p>Creates a new list view wrapper.</p> * @@ -1324,6 +1467,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return mSelectionBottomPadding; } + @Override + public boolean isInTouchMode() { + // WARNING: Please read the comment where mListSelectionHidden is declared + return mListSelectionHidden || super.isInTouchMode(); + } + /** * <p>Returns the focus state in the drop down.</p> * diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index abcc715c77e5..fd590ed16a8b 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -16,14 +16,15 @@ package android.widget; +import com.android.internal.R; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; - -import com.android.internal.R; +import android.view.accessibility.AccessibilityEvent; /** @@ -194,5 +195,13 @@ public class CheckedTextView extends TextView implements Checkable { invalidate(); } } - + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + if (!populated) { + event.setChecked(mChecked); + } + return populated; + } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index d4482dc5988e..98b0976adb8d 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -26,7 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; - +import android.view.accessibility.AccessibilityEvent; /** * <p> @@ -124,6 +124,7 @@ public abstract class CompoundButton extends Button implements Checkable { if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } + mBroadcasting = false; } } @@ -205,6 +206,25 @@ public abstract class CompoundButton extends Button implements Checkable { } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + if (!populated) { + int resourceId = 0; + if (mChecked) { + resourceId = R.string.accessibility_compound_button_selected; + } else { + resourceId = R.string.accessibility_compound_button_unselected; + } + String state = getResources().getString(resourceId); + event.getText().add(state); + event.setChecked(mChecked); + } + + return populated; + } + + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index 0fc8f495d8c4..536062168e61 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -1083,6 +1083,11 @@ public class ExpandableListView extends ListView { @Override public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 336847795025..cd965fc62e62 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -134,7 +134,7 @@ class FastScroller { mScrollCompleted = true; - getSections(); + getSectionsFromIndexer(); mOverlayPos = new RectF(); mScrollFade = new ScrollFade(); @@ -250,7 +250,18 @@ class FastScroller { } } - private void getSections() { + SectionIndexer getSectionIndexer() { + return mSectionIndexer; + } + + Object[] getSections() { + if (mListAdapter == null && mList != null) { + getSectionsFromIndexer(); + } + return mSections; + } + + private void getSectionsFromIndexer() { Adapter adapter = mList.getAdapter(); mSectionIndexer = null; if (adapter instanceof HeaderViewListAdapter) { @@ -391,8 +402,7 @@ class FastScroller { boolean onInterceptTouchEvent(MotionEvent ev) { if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) { - if (ev.getX() > mList.getWidth() - mThumbW && ev.getY() >= mThumbY && - ev.getY() <= mThumbY + mThumbH) { + if (isPointInside(ev.getX(), ev.getY())) { setState(STATE_DRAGGING); return true; } @@ -404,20 +414,20 @@ class FastScroller { if (mState == STATE_NONE) { return false; } - if (me.getAction() == MotionEvent.ACTION_DOWN) { - if (me.getX() > mList.getWidth() - mThumbW - && me.getY() >= mThumbY - && me.getY() <= mThumbY + mThumbH) { - + + final int action = me.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + if (isPointInside(me.getX(), me.getY())) { setState(STATE_DRAGGING); if (mListAdapter == null && mList != null) { - getSections(); + getSectionsFromIndexer(); } cancelFling(); return true; } - } else if (me.getAction() == MotionEvent.ACTION_UP) { + } else if (action == MotionEvent.ACTION_UP) { if (mState == STATE_DRAGGING) { setState(STATE_VISIBLE); final Handler handler = mHandler; @@ -425,7 +435,7 @@ class FastScroller { handler.postDelayed(mScrollFade, 1000); return true; } - } else if (me.getAction() == MotionEvent.ACTION_MOVE) { + } else if (action == MotionEvent.ACTION_MOVE) { if (mState == STATE_DRAGGING) { final int viewHeight = mList.getHeight(); // Jitter @@ -448,7 +458,11 @@ class FastScroller { } return false; } - + + boolean isPointInside(float x, float y) { + return x > mList.getWidth() - mThumbW && y >= mThumbY && y <= mThumbY + mThumbH; + } + public class ScrollFade implements Runnable { long mStartTime; diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 80fbf9eddecf..3afd5d42f857 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -353,25 +353,24 @@ public class FrameLayout extends ViewGroup { if (mForeground != null) { final Drawable foreground = mForeground; + if (mForegroundBoundsChanged) { mForegroundBoundsChanged = false; - if (foreground != null) { - final Rect selfBounds = mSelfBounds; - final Rect overlayBounds = mOverlayBounds; - - final int w = mRight-mLeft; - final int h = mBottom-mTop; - - if (mForegroundInPadding) { - selfBounds.set(0, 0, w, h); - } else { - selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); - } + final Rect selfBounds = mSelfBounds; + final Rect overlayBounds = mOverlayBounds; - Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), - foreground.getIntrinsicHeight(), selfBounds, overlayBounds); - foreground.setBounds(overlayBounds); + final int w = mRight-mLeft; + final int h = mBottom-mTop; + + if (mForegroundInPadding) { + selfBounds.set(0, 0, w, h); + } else { + selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); } + + Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), + foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.setBounds(overlayBounds); } foreground.draw(canvas); diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 02fc7c6b8816..f86b37cf8b3a 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -114,6 +114,8 @@ public class HorizontalScrollView extends FrameLayout { private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; + private int mMinimumVelocity; + private int mMaximumVelocity; public HorizontalScrollView(Context context) { this(context, null); @@ -179,7 +181,10 @@ public class HorizontalScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override @@ -477,12 +482,10 @@ public class HorizontalScrollView extends FrameLayout { break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getXVelocity(); - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity) && getChildCount() > 0) { fling(-initialVelocity); } diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java index 4c1cbf6a2f91..d417e40bf293 100644 --- a/core/java/android/widget/ImageButton.java +++ b/core/java/android/widget/ImageButton.java @@ -27,9 +27,35 @@ import java.util.Map; /** * <p> - * An image button displays an image that can be pressed, or clicked, by the - * user. - * </p> + * Displays a button with an image (instead of text) that can be pressed + * or clicked by the user. By default, an ImageButton looks like a regular + * {@link android.widget.Button}, with the standard button background + * that changes color during different button states. The image on the surface + * of the button is defined either by the {@code android:src} attribute in the + * {@code <ImageButton>} XML element or by the + * {@link #setImageResource(int)} method.</p> + * + * <p>To remove the standard button background image, define your own + * background image or set the background color to be transparent.</p> + * <p>To indicate the different button states (focused, selected, etc.), you can + * define a different image for each state. E.g., a blue image by default, an + * orange one for when focused, and a yellow one for when pressed. An easy way to + * do this is with an XML drawable "selector." For example:</p> + * <pre> + * <?xml version="1.0" encoding="utf-8"?> + * <selector xmlns:android="http://schemas.android.com/apk/res/android"> + * <item android:drawable="@drawable/button_normal" /> <!-- default --> + * <item android:state_pressed="true" + * android:drawable="@drawable/button_pressed" /> <!-- pressed --> + * <item android:state_focused="true" + * android:drawable="@drawable/button_focused" /> <!-- focused --> + * </selector></pre> + * + * <p>Save the XML file in your project {@code res/drawable/} folder and then + * reference it as a drawable for the source of your ImageButton (in the + * {@code android:src} attribute). Android will automatically change the image + * based on the state of the button and the corresponding images + * defined in the XML.</p> * * <p><strong>XML attributes</strong></p> * <p> diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 480b0b8a135a..27967742a135 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,6 +32,8 @@ import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews.RemoteView; @@ -848,7 +850,7 @@ public class ImageView extends View { public int getBaseline() { return mBaselineAligned ? getMeasuredHeight() : -1; } - + /** * Set a tinting option for the image. * @@ -878,7 +880,7 @@ public class ImageView extends View { invalidate(); } } - + public void setAlpha(int alpha) { alpha &= 0xFF; // keep it legal if (mAlpha != alpha) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 5472d6859c0a..f8a6f89a9880 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.PixelFormat; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; import android.os.Parcel; @@ -35,6 +36,7 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; import android.view.SoundEffectConstants; +import android.view.accessibility.AccessibilityEvent; import com.google.android.collect.Lists; import com.android.internal.R; @@ -132,6 +134,7 @@ public class ListView extends AbsListView { // used for temporary calculations. private final Rect mTempRect = new Rect(); + private Paint mDividerPaint; // the single allocated result per list view; kinda cheesey but avoids // allocating these thingies too often. @@ -171,6 +174,8 @@ public class ListView extends AbsListView { setDividerHeight(dividerHeight); } + setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE)); + mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true); mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true); @@ -1845,6 +1850,39 @@ public class ListView extends AbsListView { } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + // If the item count is less than 15 then subtract disabled items from the count and + // position. Otherwise ignore disabled items. + if (!populated) { + int itemCount = 0; + int currentItemIndex = getSelectedItemPosition(); + + ListAdapter adapter = getAdapter(); + if (adapter != null) { + final int count = adapter.getCount(); + if (count < 15) { + for (int i = 0; i < count; i++) { + if (adapter.isEnabled(i)) { + itemCount++; + } else if (i <= currentItemIndex) { + currentItemIndex--; + } + } + } else { + itemCount = count; + } + } + + event.setItemCount(itemCount); + event.setCurrentItemIndex(currentItemIndex); + } + + return populated; + } + /** * setSelectionAfterHeaderView set the selection to be the first list item * after the header views. @@ -2786,12 +2824,20 @@ public class ListView extends AbsListView { */ @Override public boolean isOpaque() { - return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque) || super.isOpaque(); + return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque && + hasOpaqueScrollbars()) || super.isOpaque(); } @Override public void setCacheColorHint(int color) { - mIsCacheColorOpaque = (color >>> 24) == 0xFF; + final boolean opaque = (color >>> 24) == 0xFF; + mIsCacheColorOpaque = opaque; + if (opaque) { + if (mDividerPaint == null) { + mDividerPaint = new Paint(); + } + mDividerPaint.setColor(color); + } super.setCacheColorHint(color); } @@ -2814,6 +2860,17 @@ public class ListView extends AbsListView { final int first = mFirstPosition; final boolean areAllItemsSelectable = mAreAllItemsSelectable; final ListAdapter adapter = mAdapter; + // If the list is opaque *and* the background is not, we want to + // fill a rect where the dividers would be for non-selectable items + // If the list is opaque and the background is also opaque, we don't + // need to draw anything since the background will do it for us + final boolean fillForMissingDividers = isOpaque() && !super.isOpaque(); + + if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) { + mDividerPaint = new Paint(); + mDividerPaint.setColor(getCacheColorHint()); + } + final Paint paint = mDividerPaint; if (!mStackFromBottom) { int bottom; @@ -2825,12 +2882,18 @@ public class ListView extends AbsListView { View child = getChildAt(i); bottom = child.getBottom(); // Don't draw dividers next to items that are not enabled - if (bottom < listBottom && (areAllItemsSelectable || - (adapter.isEnabled(first + i) && (i == count - 1 || - adapter.isEnabled(first + i + 1))))) { - bounds.top = bottom; - bounds.bottom = bottom + dividerHeight; - drawDivider(canvas, bounds, i); + if (bottom < listBottom) { + if ((areAllItemsSelectable || + (adapter.isEnabled(first + i) && (i == count - 1 || + adapter.isEnabled(first + i + 1))))) { + bounds.top = bottom; + bounds.bottom = bottom + dividerHeight; + drawDivider(canvas, bounds, i); + } else if (fillForMissingDividers) { + bounds.top = bottom; + bounds.bottom = bottom + dividerHeight; + canvas.drawRect(bounds, paint); + } } } } @@ -2844,16 +2907,22 @@ public class ListView extends AbsListView { View child = getChildAt(i); top = child.getTop(); // Don't draw dividers next to items that are not enabled - if (top > listTop && (areAllItemsSelectable || - (adapter.isEnabled(first + i) && (i == count - 1 || - adapter.isEnabled(first + i + 1))))) { - bounds.top = top - dividerHeight; - bounds.bottom = top; - // Give the method the child ABOVE the divider, so we - // subtract one from our child - // position. Give -1 when there is no child above the - // divider. - drawDivider(canvas, bounds, i - 1); + if (top > listTop) { + if ((areAllItemsSelectable || + (adapter.isEnabled(first + i) && (i == count - 1 || + adapter.isEnabled(first + i + 1))))) { + bounds.top = top - dividerHeight; + bounds.bottom = top; + // Give the method the child ABOVE the divider, so we + // subtract one from our child + // position. Give -1 when there is no child above the + // divider. + drawDivider(canvas, bounds, i - 1); + } else if (fillForMissingDividers) { + bounds.top = top - dividerHeight; + bounds.bottom = top; + canvas.drawRect(bounds, paint); + } } } } @@ -3195,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/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java index 05abc2661eb2..ae8027784bc3 100644 --- a/core/java/android/widget/MultiAutoCompleteTextView.java +++ b/core/java/android/widget/MultiAutoCompleteTextView.java @@ -195,6 +195,8 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView { */ @Override protected void replaceText(CharSequence text) { + clearComposingText(); + int end = getSelectionEnd(); int start = mTokenizer.findTokenStart(getText(), end); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 78c7bd8e54c4..0c2cd55d2ee3 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -18,6 +18,8 @@ package android.widget; import com.android.internal.R; +import android.content.Context; +import android.content.res.TypedArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -33,8 +35,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.IBinder; -import android.content.Context; -import android.content.res.TypedArray; import android.util.AttributeSet; import java.lang.ref.WeakReference; @@ -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. @@ -823,6 +822,7 @@ public class PopupWindow { p.flags = computeFlags(p.flags); p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; p.token = token; + p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); return p; @@ -990,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; @@ -1017,6 +1017,7 @@ public class PopupWindow { unregisterForScrollChanged(); mWindowManager.removeView(mPopupView); + if (mPopupView != mContentView && mPopupView instanceof ViewGroup) { ((ViewGroup) mPopupView).removeView(mContentView); } @@ -1071,6 +1072,20 @@ public class PopupWindow { mWindowManager.updateViewLayout(mPopupView, p); } } + + /** + * <p>Updates the dimension of the popup window. Calling this function + * also updates the window with the current popup state as described + * for {@link #update()}.</p> + * + * @param width the new width + * @param height the new height + */ + public void update(int width, int height) { + WindowManager.LayoutParams p = (WindowManager.LayoutParams) + mPopupView.getLayoutParams(); + update(p.x, p.y, width, height, false); + } /** * <p>Updates the position and the dimension of the popup window. Width and @@ -1115,8 +1130,7 @@ public class PopupWindow { return; } - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); + WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); boolean update = force; @@ -1203,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) { @@ -1316,7 +1329,16 @@ public class PopupWindow { return super.onTouchEvent(event); } } - + + @Override + public void sendAccessibilityEvent(int eventType) { + // clinets are interested in the content not the container, make it event source + if (mContentView != null) { + mContentView.sendAccessibilityEvent(eventType); + } else { + super.sendAccessibilityEvent(eventType); + } + } } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 441414a20de4..2c9e71e02f08 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.Animatable; import android.graphics.drawable.shapes.RoundRectShape; import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; @@ -683,7 +684,7 @@ public class ProgressBar extends View { return; } - if (mIndeterminateDrawable instanceof AnimationDrawable) { + if (mIndeterminateDrawable instanceof Animatable) { mShouldStartAnimationDrawable = true; mAnimation = null; } else { @@ -708,8 +709,8 @@ public class ProgressBar extends View { void stopAnimation() { mAnimation = null; mTransformation = null; - if (mIndeterminateDrawable instanceof AnimationDrawable) { - ((AnimationDrawable) mIndeterminateDrawable).stop(); + if (mIndeterminateDrawable instanceof Animatable) { + ((Animatable) mIndeterminateDrawable).stop(); mShouldStartAnimationDrawable = false; } } @@ -818,8 +819,8 @@ public class ProgressBar extends View { } d.draw(canvas); canvas.restore(); - if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) { - ((AnimationDrawable) d).start(); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { + ((Animatable) d).start(); mShouldStartAnimationDrawable = false; } } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index edbb3db255ab..e62dda58ba42 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,40 +16,59 @@ package android.widget; +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.view.View; -import android.view.ViewGroup; +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; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; -import android.graphics.Rect; -import com.android.internal.R; +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 * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if * view Y is dependent on the position of view X, make sure the view X comes first in the layout. - * + * * <p> * Note that you cannot have a circular dependency between the size of the RelativeLayout and the * position of its children. For example, you cannot have a RelativeLayout whose height is set to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to * {@link #ALIGN_PARENT_BOTTOM}. * </p> - * + * * <p> * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for * layout attributes * </p> - * + * * @attr ref android.R.styleable#RelativeLayout_gravity * @attr ref android.R.styleable#RelativeLayout_ignoreGravity */ @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; /** @@ -137,6 +156,13 @@ public class RelativeLayout extends ViewGroup { private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; + 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); } @@ -225,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; @@ -254,7 +327,6 @@ public class RelativeLayout extends ViewGroup { height = myHeight; } - int len = this.getChildCount(); mHasBaselineAlignedChild = false; View ignore = null; @@ -268,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); } @@ -300,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) { @@ -319,8 +419,8 @@ public class RelativeLayout extends ViewGroup { } } - if (widthMode != MeasureSpec.EXACTLY) { - // Width already has left padding in it since it was calculated by looking at + if (isWrapContentWidth) { + // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; @@ -330,9 +430,23 @@ 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) { - // Height already has top padding in it since it was calculated by looking at + + if (isWrapContentHeight) { + // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; @@ -342,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) { @@ -355,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(); @@ -409,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, @@ -425,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(myHeight, MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); + } + 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 @@ -504,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) { @@ -527,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 @@ -543,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; @@ -622,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; @@ -684,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; @@ -782,6 +925,57 @@ public class RelativeLayout extends ViewGroup { return new LayoutParams(p); } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (mTopToBottomLeftToRightSet == null) { + mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator()); + } + + // sort children top-to-bottom and left-to-right + for (int i = 0, count = getChildCount(); i < count; i++) { + mTopToBottomLeftToRightSet.add(getChildAt(i)); + } + + for (View view : mTopToBottomLeftToRightSet) { + if (view.dispatchPopulateAccessibilityEvent(event)) { + mTopToBottomLeftToRightSet.clear(); + return true; + } + } + + mTopToBottomLeftToRightSet.clear(); + return false; + } + + /** + * Compares two views in left-to-right and top-to-bottom fashion. + */ + private class TopToBottomLeftToRightComparator implements Comparator<View> { + public int compare(View first, View second) { + // top - bottom + int topDifference = first.getTop() - second.getTop(); + if (topDifference != 0) { + return topDifference; + } + // left - right + int leftDifference = first.getLeft() - second.getLeft(); + if (leftDifference != 0) { + return leftDifference; + } + // break tie by height + int heightDiference = first.getHeight() - second.getHeight(); + if (heightDiference != 0) { + return heightDiference; + } + // break tie by width + int widthDiference = first.getWidth() - second.getWidth(); + if (widthDiference != 0) { + return widthDiference; + } + return 0; + } + } + /** * Per-child layout information associated with RelativeLayout. * @@ -823,7 +1017,7 @@ public class RelativeLayout extends ViewGroup { @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf") }, mapping = { @ViewDebug.IntToString(from = TRUE, to = "true"), - @ViewDebug.IntToString(from = 0, to = "FALSE/NO_ID") + @ViewDebug.IntToString(from = 0, to = "false/NO_ID") }) private int[] mRules = new int[VERB_COUNT]; @@ -975,4 +1169,284 @@ 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 + */ + // The pool is static, so all nodes instances are shared across + // activities, that's why we give it a rather high limit + private static final int POOL_LIMIT = 100; + 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/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7936f6535d9f..2dac65211213 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -20,10 +20,8 @@ import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Parcel; @@ -36,15 +34,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater.Filter; import android.view.View.OnClickListener; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; import java.lang.Class; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -69,13 +64,7 @@ public class RemoteViews implements Parcelable, Filter { * The resource ID of the layout file. (Added to the parcel) */ private int mLayoutId; - - /** - * The Context object used to inflate the layout file. Also may - * be used by actions if they need access to the senders resources. - */ - private Context mContext; - + /** * An array of actions to perform on the view tree once it has been * inflated @@ -85,7 +74,7 @@ public class RemoteViews implements Parcelable, Filter { /** * This annotation indicates that a subclass of View is alllowed to be used - * with the {@link android.widget.RemoteViews} mechanism. + * with the {@link RemoteViews} mechanism. */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -116,7 +105,7 @@ public class RemoteViews implements Parcelable, Filter { public int describeContents() { return 0; } - }; + } /** * Equivalent to calling @@ -232,15 +221,17 @@ public class RemoteViews implements Parcelable, Filter { targetDrawable = imageView.getDrawable(); } - // Perform modifications only if values are set correctly - if (alpha != -1) { - targetDrawable.setAlpha(alpha); - } - if (colorFilter != -1 && filterMode != null) { - targetDrawable.setColorFilter(colorFilter, filterMode); - } - if (level != -1) { - targetDrawable.setLevel(level); + if (targetDrawable != null) { + // Perform modifications only if values are set correctly + if (alpha != -1) { + targetDrawable.setAlpha(alpha); + } + if (colorFilter != -1 && filterMode != null) { + targetDrawable.setColorFilter(colorFilter, filterMode); + } + if (level != -1) { + targetDrawable.setLevel(level); + } } } @@ -289,6 +280,7 @@ public class RemoteViews implements Parcelable, Filter { this.viewId = in.readInt(); this.methodName = in.readString(); this.type = in.readInt(); + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); @@ -340,31 +332,32 @@ public class RemoteViews implements Parcelable, Filter { out.writeInt(this.viewId); out.writeString(this.methodName); out.writeInt(this.type); + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } switch (this.type) { case BOOLEAN: - out.writeInt(((Boolean)this.value).booleanValue() ? 1 : 0); + out.writeInt((Boolean) this.value ? 1 : 0); break; case BYTE: - out.writeByte(((Byte)this.value).byteValue()); + out.writeByte((Byte) this.value); break; case SHORT: - out.writeInt(((Short)this.value).shortValue()); + out.writeInt((Short) this.value); break; case INT: - out.writeInt(((Integer)this.value).intValue()); + out.writeInt((Integer) this.value); break; case LONG: - out.writeLong(((Long)this.value).longValue()); + out.writeLong((Long) this.value); break; case FLOAT: - out.writeFloat(((Float)this.value).floatValue()); + out.writeFloat((Float) this.value); break; case DOUBLE: - out.writeDouble(((Double)this.value).doubleValue()); + out.writeDouble((Double) this.value); break; case CHAR: out.writeInt((int)((Character)this.value).charValue()); @@ -430,7 +423,7 @@ public class RemoteViews implements Parcelable, Filter { } Class klass = view.getClass(); - Method method = null; + Method method; try { method = klass.getMethod(this.methodName, getParameterType()); } @@ -446,6 +439,7 @@ public class RemoteViews implements Parcelable, Filter { } try { + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " + this.methodName + "(" + param.getName() + ") with " @@ -816,13 +810,12 @@ public class RemoteViews implements Parcelable, Filter { * @return The inflated view hierarchy */ public View apply(Context context, ViewGroup parent) { - View result = null; + View result; Context c = prepareContext(context); - Resources r = c.getResources(); - LayoutInflater inflater = (LayoutInflater) c - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + LayoutInflater inflater = (LayoutInflater) + c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(c); inflater.setFilter(this); @@ -858,12 +851,12 @@ public class RemoteViews implements Parcelable, Filter { } private Context prepareContext(Context context) { - Context c = null; + Context c; String packageName = mPackage; if (packageName != null) { try { - c = context.createPackageContext(packageName, 0); + c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + packageName + " not found"); c = context; @@ -872,8 +865,6 @@ public class RemoteViews implements Parcelable, Filter { c = context; } - mContext = c; - return c; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index c9b3751764eb..90e1242396f3 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -115,6 +115,8 @@ public class ScrollView extends FrameLayout { private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; + private int mMinimumVelocity; + private int mMaximumVelocity; public ScrollView(Context context) { this(context, null); @@ -180,7 +182,10 @@ public class ScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override @@ -478,12 +483,10 @@ public class ScrollView extends FrameLayout { break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity) && getChildCount() > 0) { fling(-initialVelocity); } diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index 92561edc1cd1..f70674430135 100644 --- a/core/java/android/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -16,21 +16,22 @@ package android.widget; -import android.view.ViewGroup; -import android.view.View; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.SoundEffectConstants; +import android.R; import android.content.Context; import android.content.res.TypedArray; -import android.util.AttributeSet; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.Bitmap; -import android.os.SystemClock; import android.os.Handler; import android.os.Message; -import android.R; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; /** * SlidingDrawer hides content out of the screen and allows the user to drag a handle @@ -746,6 +747,8 @@ public class SlidingDrawer extends ViewGroup { openDrawer(); invalidate(); requestLayout(); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } /** @@ -777,6 +780,7 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft()); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -798,6 +802,9 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft()); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -827,6 +834,7 @@ public class SlidingDrawer extends ViewGroup { } mExpanded = true; + if (mOnDrawerOpenListener != null) { mOnDrawerOpenListener.onDrawerOpened(); } diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index dc2c70de92e4..103d44db52a0 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -87,8 +87,9 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode /** - * <p>Call setup() before adding tabs if loading TabHost using findViewById(). <i><b>However</i></b>: You do - * not need to call setup() after getTabHost() in {@link android.app.TabActivity TabActivity}. + * <p>Call setup() before adding tabs if loading TabHost using findViewById(). + * <i><b>However</i></b>: You do not need to call setup() after getTabHost() + * in {@link android.app.TabActivity TabActivity}. * Example:</p> <pre>mTabHost = (TabHost)findViewById(R.id.tabhost); mTabHost.setup(); @@ -176,7 +177,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); // leaving touch mode.. if nothing has focus, let's give it to // the indicator of the current tab if (!mCurrentView.hasFocus() || mCurrentView.isFocused()) { - mTabWidget.getChildAt(mCurrentTab).requestFocus(); + mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus(); } } } @@ -196,6 +197,12 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); } View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView(); tabIndicator.setOnKeyListener(mTabKeyListener); + + // If this is a custom view, then do not draw the bottom strips for + // the tab indicators. + if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) { + mTabWidget.setDrawBottomStrips(false); + } mTabWidget.addView(tabIndicator); mTabSpecs.add(tabSpec); @@ -234,7 +241,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); public View getCurrentTabView() { if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) { - return mTabWidget.getChildAt(mCurrentTab); + return mTabWidget.getChildTabViewAt(mCurrentTab); } return null; } @@ -272,7 +279,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); && (mCurrentView.isRootNamespace()) && (mCurrentView.hasFocus()) && (mCurrentView.findFocus().focusSearch(View.FOCUS_UP) == null)) { - mTabWidget.getChildAt(mCurrentTab).requestFocus(); + mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus(); playSoundEffect(SoundEffectConstants.NAVIGATION_UP); return true; } @@ -363,14 +370,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); * * @param tag * Which tab was selected. - * @return The view to distplay the contents of the selected tab. + * @return The view to display the contents of the selected tab. */ View createTabContent(String tag); } /** - * A tab has a tab indictor, content, and a tag that is used to keep + * A tab has a tab indicator, content, and a tag that is used to keep * track of it. This builder helps choose among these options. * * For the tab indicator, your choices are: @@ -410,6 +417,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); } /** + * Specify a view as the tab indicator. + */ + public TabSpec setIndicator(View view) { + mIndicatorStrategy = new ViewIndicatorStrategy(view); + return this; + } + + /** * Specify the id of the view that should be used as the content * of the tab. */ @@ -436,7 +451,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); } - String getTag() { + public String getTag() { return mTag; } } @@ -525,6 +540,22 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); } /** + * How to create a tab indicator by specifying a view. + */ + private class ViewIndicatorStrategy implements IndicatorStrategy { + + private final View mView; + + private ViewIndicatorStrategy(View view) { + mView = view; + } + + public View createIndicatorView() { + return mView; + } + } + + /** * How to create the tab content via a view id. */ private class ViewIdContentStrategy implements ContentStrategy { @@ -607,7 +638,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); } mLaunchedView = wd; - // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activies for now so they can get + // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get // focus if none of their children have it. They need focus to be able to // display menu items. // diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 20cddcb3bfb2..a26bfa23eff8 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -49,6 +49,8 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { private Drawable mBottomLeftStrip; private Drawable mBottomRightStrip; private boolean mStripMoved; + private Drawable mDividerDrawable; + private boolean mDrawBottomStrips = true; public TabWidget(Context context) { this(context, null); @@ -87,9 +89,68 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { setOnFocusChangeListener(this); } + /** + * Returns the tab indicator view at the given index. + * + * @param index the zero-based index of the tab indicator view to return + * @return the tab indicator view at the given index + */ + public View getChildTabViewAt(int index) { + // If we are using dividers, then instead of tab views at 0, 1, 2, ... + // we have tab views at 0, 2, 4, ... + if (mDividerDrawable != null) { + index *= 2; + } + return getChildAt(index); + } + + /** + * Returns the number of tab indicator views. + * @return the number of tab indicator views. + */ + public int getTabCount() { + int children = getChildCount(); + + // If we have dividers, then we will always have an odd number of + // children: 1, 3, 5, ... and we want to convert that sequence to + // this: 1, 2, 3, ... + if (mDividerDrawable != null) { + children = (children + 1) / 2; + } + return children; + } + + /** + * Sets the drawable to use as a divider between the tab indicators. + * @param drawable the divider drawable + */ + public void setDividerDrawable(Drawable drawable) { + mDividerDrawable = drawable; + } + + /** + * Sets the drawable to use as a divider between the tab indicators. + * @param resId the resource identifier of the drawable to use as a + * divider. + */ + public void setDividerDrawable(int resId) { + mDividerDrawable = mContext.getResources().getDrawable(resId); + } + + /** + * Controls whether the bottom strips on the tab indicators are drawn or + * not. The default is to draw them. If the user specifies a custom + * view for the tab indicators, then the TabHost class calls this method + * to disable drawing of the bottom strips. + * @param drawBottomStrips true if the bottom strips should be drawn. + */ + void setDrawBottomStrips(boolean drawBottomStrips) { + mDrawBottomStrips = drawBottomStrips; + } + @Override public void childDrawableStateChanged(View child) { - if (child == getChildAt(mSelectedTab)) { + if (child == getChildTabViewAt(mSelectedTab)) { // To make sure that the bottom strip is redrawn invalidate(); } @@ -100,7 +161,14 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { public void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - View selectedChild = getChildAt(mSelectedTab); + // If the user specified a custom view for the tab indicators, then + // do not draw the bottom strips. + if (!mDrawBottomStrips) { + // Skip drawing the bottom strips. + return; + } + + View selectedChild = getChildTabViewAt(mSelectedTab); mBottomLeftStrip.setState(selectedChild.getDrawableState()); mBottomRightStrip.setState(selectedChild.getDrawableState()); @@ -157,13 +225,13 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { * @see #focusCurrentTab */ public void setCurrentTab(int index) { - if (index < 0 || index >= getChildCount()) { + if (index < 0 || index >= getTabCount()) { return; } - getChildAt(mSelectedTab).setSelected(false); + getChildTabViewAt(mSelectedTab).setSelected(false); mSelectedTab = index; - getChildAt(mSelectedTab).setSelected(true); + getChildTabViewAt(mSelectedTab).setSelected(true); mStripMoved = true; } @@ -189,17 +257,17 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { // change the focus if applicable. if (oldTab != index) { - getChildAt(index).requestFocus(); + getChildTabViewAt(index).requestFocus(); } } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); - int count = getChildCount(); + int count = getTabCount(); - for (int i=0; i<count; i++) { - View child = getChildAt(i); + for (int i = 0; i < count; i++) { + View child = getChildTabViewAt(i); child.setEnabled(enabled); } } @@ -218,17 +286,26 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { child.setFocusable(true); child.setClickable(true); + // If we have dividers between the tabs and we already have at least one + // tab, then add a divider before adding the next tab. + if (mDividerDrawable != null && getTabCount() > 0) { + View divider = new View(mContext); + final LinearLayout.LayoutParams lp = new LayoutParams( + mDividerDrawable.getIntrinsicWidth(), + mDividerDrawable.getIntrinsicHeight()); + lp.setMargins(0, 0, 0, 0); + divider.setLayoutParams(lp); + divider.setBackgroundDrawable(mDividerDrawable); + super.addView(divider); + } super.addView(child); // TODO: detect this via geometry with a tabwidget listener rather // than potentially interfere with the view's listener - child.setOnClickListener(new TabClickListener(getChildCount() - 1)); + child.setOnClickListener(new TabClickListener(getTabCount() - 1)); child.setOnFocusChangeListener(this); } - - - /** * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator. */ @@ -238,14 +315,15 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { public void onFocusChange(View v, boolean hasFocus) { if (v == this && hasFocus) { - getChildAt(mSelectedTab).requestFocus(); + getChildTabViewAt(mSelectedTab).requestFocus(); return; } if (hasFocus) { int i = 0; - while (i < getChildCount()) { - if (getChildAt(i) == v) { + int numTabs = getTabCount(); + while (i < numTabs) { + if (getChildTabViewAt(i) == v) { setCurrentTab(i); mSelectionChangedListener.onTabSelectionChanged(i, false); break; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index adfc74fbfae2..d8ed4f0e4e86 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,11 @@ package android.widget; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; @@ -31,17 +36,17 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; +import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.ResultReceiver; import android.os.SystemClock; -import android.os.Message; import android.text.BoringLayout; +import android.text.ClipboardManager; import android.text.DynamicLayout; import android.text.Editable; import android.text.GetChars; import android.text.GraphicsOperations; -import android.text.ClipboardManager; import android.text.InputFilter; import android.text.InputType; import android.text.Layout; @@ -49,9 +54,9 @@ import android.text.ParcelableSpan; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; +import android.text.SpannableString; import android.text.Spanned; import android.text.SpannedString; -import android.text.SpannableString; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; @@ -64,19 +69,18 @@ import android.text.method.KeyListener; import android.text.method.LinkMovementMethod; import android.text.method.MetaKeyKeyListener; import android.text.method.MovementMethod; -import android.text.method.TimeKeyListener; - import android.text.method.PasswordTransformationMethod; import android.text.method.SingleLineTransformationMethod; import android.text.method.TextKeyListener; +import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.style.ParagraphStyle; import android.text.style.URLSpan; import android.text.style.UpdateAppearance; import android.text.util.Linkify; import android.util.AttributeSet; -import android.util.Log; import android.util.FloatMath; +import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; import android.view.Gravity; @@ -89,25 +93,22 @@ import android.view.ViewDebug; import android.view.ViewRoot; import android.view.ViewTreeObserver; import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AnimationUtils; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; import android.widget.RemoteViews.RemoteView; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - /** * Displays text to the user and optionally allows them to edit it. A TextView * is a complete text editor, however the basic class is configured to not @@ -126,6 +127,8 @@ import org.xmlpull.v1.XmlPullParserException; * @attr ref android.R.styleable#TextView_textColor * @attr ref android.R.styleable#TextView_textColorHighlight * @attr ref android.R.styleable#TextView_textColorHint + * @attr ref android.R.styleable#TextView_textAppearance + * @attr ref android.R.styleable#TextView_textColorLink * @attr ref android.R.styleable#TextView_textSize * @attr ref android.R.styleable#TextView_textScaleX * @attr ref android.R.styleable#TextView_typeface @@ -163,13 +166,22 @@ import org.xmlpull.v1.XmlPullParserException; * @attr ref android.R.styleable#TextView_capitalize * @attr ref android.R.styleable#TextView_autoText * @attr ref android.R.styleable#TextView_editable + * @attr ref android.R.styleable#TextView_freezesText + * @attr ref android.R.styleable#TextView_ellipsize * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableBottom * @attr ref android.R.styleable#TextView_drawableRight * @attr ref android.R.styleable#TextView_drawableLeft + * @attr ref android.R.styleable#TextView_drawablePadding * @attr ref android.R.styleable#TextView_lineSpacingExtra * @attr ref android.R.styleable#TextView_lineSpacingMultiplier * @attr ref android.R.styleable#TextView_marqueeRepeatLimit + * @attr ref android.R.styleable#TextView_inputType + * @attr ref android.R.styleable#TextView_imeOptions + * @attr ref android.R.styleable#TextView_privateImeOptions + * @attr ref android.R.styleable#TextView_imeActionLabel + * @attr ref android.R.styleable#TextView_imeActionId + * @attr ref android.R.styleable#TextView_editorExtras */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -404,6 +416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean singleLine = false; int maxlength = -1; CharSequence text = ""; + CharSequence hint = null; int shadowcolor = 0; float dx = 0, dy = 0, r = 0; boolean password = false; @@ -531,7 +544,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_hint: - setHint(a.getText(attr)); + hint = a.getText(attr); break; case com.android.internal.R.styleable.TextView_text: @@ -861,6 +874,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setText(text, bufferType); + if (hint != null) setHint(hint); /* * Views are not normally focusable unless specified to be. @@ -1328,9 +1342,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { // We need to retain the last set padding, so just clear // out all of the fields in the existing structure. + if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null); dr.mDrawableLeft = null; + if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null); dr.mDrawableTop = null; + if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null); dr.mDrawableRight = null; + if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null); dr.mDrawableBottom = null; dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0; dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0; @@ -1343,19 +1361,32 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mDrawables = dr = new Drawables(); } + if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) { + dr.mDrawableLeft.setCallback(null); + } dr.mDrawableLeft = left; + if (dr.mDrawableTop != left && dr.mDrawableTop != null) { + dr.mDrawableTop.setCallback(null); + } dr.mDrawableTop = top; + if (dr.mDrawableRight != left && dr.mDrawableRight != null) { + dr.mDrawableRight.setCallback(null); + } dr.mDrawableRight = right; + if (dr.mDrawableBottom != left && dr.mDrawableBottom != null) { + dr.mDrawableBottom.setCallback(null); + } dr.mDrawableBottom = bottom; final Rect compoundRect = dr.mCompoundRect; - int[] state = null; + int[] state; state = getDrawableState(); if (left != null) { left.setState(state); left.copyBounds(compoundRect); + left.setCallback(this); dr.mDrawableSizeLeft = compoundRect.width(); dr.mDrawableHeightLeft = compoundRect.height(); } else { @@ -1365,6 +1396,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (right != null) { right.setState(state); right.copyBounds(compoundRect); + right.setCallback(this); dr.mDrawableSizeRight = compoundRect.width(); dr.mDrawableHeightRight = compoundRect.height(); } else { @@ -1374,6 +1406,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (top != null) { top.setState(state); top.copyBounds(compoundRect); + top.setCallback(this); dr.mDrawableSizeTop = compoundRect.height(); dr.mDrawableWidthTop = compoundRect.width(); } else { @@ -1383,6 +1416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (bottom != null) { bottom.setState(state); bottom.copyBounds(compoundRect); + bottom.setCallback(this); dr.mDrawableSizeBottom = compoundRect.height(); dr.mDrawableWidthBottom = compoundRect.width(); } else { @@ -2785,8 +2819,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener checkForRelayout(); } - if (mText.length() == 0) + if (mText.length() == 0) { invalidate(); + } } /** @@ -3646,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 @@ -3666,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 @@ -3680,6 +3717,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public void invalidateDrawable(Drawable drawable) { + if (verifyDrawable(drawable)) { + final Rect dirty = drawable.getBounds(); + int scrollX = mScrollX; + int scrollY = mScrollY; + + // IMPORTANT: The coordinates below are based on the coordinates computed + // for each compound drawable in onDraw(). Make sure to update each section + // accordingly. + final TextView.Drawables drawables = mDrawables; + if (drawables != null) { + if (drawable == drawables.mDrawableLeft) { + final int compoundPaddingTop = getCompoundPaddingTop(); + final int compoundPaddingBottom = getCompoundPaddingBottom(); + final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; + + scrollX += mPaddingLeft; + scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2; + } else if (drawable == drawables.mDrawableRight) { + final int compoundPaddingTop = getCompoundPaddingTop(); + final int compoundPaddingBottom = getCompoundPaddingBottom(); + final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; + + scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight); + scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2; + } else if (drawable == drawables.mDrawableTop) { + final int compoundPaddingLeft = getCompoundPaddingLeft(); + final int compoundPaddingRight = getCompoundPaddingRight(); + final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; + + scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2; + scrollY += mPaddingTop; + } else if (drawable == drawables.mDrawableBottom) { + final int compoundPaddingLeft = getCompoundPaddingLeft(); + final int compoundPaddingRight = getCompoundPaddingRight(); + final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; + + scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2; + scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom); + } + } + + invalidate(dirty.left + scrollX, dirty.top + scrollY, + dirty.right + scrollX, dirty.bottom + scrollY); + } + } + + @Override protected void onDraw(Canvas canvas) { restartMarqueeIfNeeded(); @@ -3707,6 +3792,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop; int hspace = right - left - compoundPaddingRight - compoundPaddingLeft; + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableLeft != null) { canvas.save(); canvas.translate(scrollX + mPaddingLeft, @@ -3716,6 +3803,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener canvas.restore(); } + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableRight != null) { canvas.save(); canvas.translate(scrollX + right - left - mPaddingRight - dr.mDrawableSizeRight, @@ -3724,6 +3813,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener canvas.restore(); } + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableTop != null) { canvas.save(); canvas.translate(scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop) / 2, @@ -3732,6 +3823,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener canvas.restore(); } + // IMPORTANT: The coordinates computed are also used in invalidateDrawable() + // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableBottom != null) { canvas.save(); canvas.translate(scrollX + compoundPaddingLeft + @@ -4714,10 +4807,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener alignment = Layout.Alignment.ALIGN_NORMAL; } + boolean shouldEllipsize = mEllipsize != null && mInput == null; + if (mText instanceof Spannable) { mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w, alignment, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, + mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -4744,7 +4839,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Log.e("aaa", "Boring: " + mTransformed); mSavedLayout = (BoringLayout) mLayout; - } else if (mEllipsize != null && boring.width <= w) { + } else if (shouldEllipsize && boring.width <= w) { if (mSavedLayout != null) { mLayout = mSavedLayout. replaceOrMake(mTransformed, mTextPaint, @@ -4757,7 +4852,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boring, mIncludePad, mEllipsize, ellipsisWidth); } - } else if (mEllipsize != null) { + } else if (shouldEllipsize) { mLayout = new StaticLayout(mTransformed, 0, mTransformed.length(), mTextPaint, w, alignment, mSpacingMult, @@ -4769,7 +4864,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mIncludePad); // Log.e("aaa", "Boring but wide: " + mTransformed); } - } else if (mEllipsize != null) { + } else if (shouldEllipsize) { mLayout = new StaticLayout(mTransformed, 0, mTransformed.length(), mTextPaint, w, alignment, mSpacingMult, @@ -4782,9 +4877,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + shouldEllipsize = mEllipsize != null; mHintLayout = null; if (mHint != null) { + if (shouldEllipsize) hintWidth = w; + if (hintBoring == UNKNOWN_BORING) { hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring); @@ -4794,24 +4892,50 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (hintBoring != null) { - if (hintBoring.width <= hintWidth) { + if (hintBoring.width <= hintWidth && + (!shouldEllipsize || hintBoring.width <= ellipsisWidth)) { if (mSavedHintLayout != null) { mHintLayout = mSavedHintLayout. replaceOrMake(mHint, mTextPaint, - hintWidth, alignment, mSpacingMult, - mSpacingAdd, hintBoring, mIncludePad); + hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintBoring, mIncludePad); } else { mHintLayout = BoringLayout.make(mHint, mTextPaint, - hintWidth, alignment, mSpacingMult, - mSpacingAdd, hintBoring, mIncludePad); + hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintBoring, mIncludePad); } mSavedHintLayout = (BoringLayout) mHintLayout; + } else if (shouldEllipsize && hintBoring.width <= hintWidth) { + if (mSavedHintLayout != null) { + mHintLayout = mSavedHintLayout. + replaceOrMake(mHint, mTextPaint, + hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintBoring, mIncludePad, mEllipsize, + ellipsisWidth); + } else { + mHintLayout = BoringLayout.make(mHint, mTextPaint, + hintWidth, alignment, mSpacingMult, mSpacingAdd, + hintBoring, mIncludePad, mEllipsize, + ellipsisWidth); + } + } else if (shouldEllipsize) { + mHintLayout = new StaticLayout(mHint, + 0, mHint.length(), + mTextPaint, hintWidth, alignment, mSpacingMult, + mSpacingAdd, mIncludePad, mEllipsize, + ellipsisWidth); } else { mHintLayout = new StaticLayout(mHint, mTextPaint, hintWidth, alignment, mSpacingMult, mSpacingAdd, mIncludePad); } + } else if (shouldEllipsize) { + mHintLayout = new StaticLayout(mHint, + 0, mHint.length(), + mTextPaint, hintWidth, alignment, mSpacingMult, + mSpacingAdd, mIncludePad, mEllipsize, + ellipsisWidth); } else { mHintLayout = new StaticLayout(mHint, mTextPaint, hintWidth, alignment, mSpacingMult, mSpacingAdd, @@ -4895,8 +5019,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private static final BoringLayout.Metrics UNKNOWN_BORING = - new BoringLayout.Metrics(); + private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics(); @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -4923,8 +5046,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (des < 0) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, - mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring); if (boring != null) { mBoring = boring; } @@ -4934,8 +5056,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (boring == null || boring == UNKNOWN_BORING) { if (des < 0) { - des = (int) FloatMath.ceil(Layout. - getDesiredWidth(mTransformed, mTextPaint)); + des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint)); } width = des; @@ -4953,13 +5074,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int hintDes = -1; int hintWidth; - if (mHintLayout != null) { + if (mHintLayout != null && mEllipsize == null) { hintDes = desired(mHintLayout); } if (hintDes < 0) { - hintBoring = BoringLayout.isBoring(mHint, mTextPaint, - mHintBoring); + hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring); if (hintBoring != null) { mHintBoring = hintBoring; } @@ -4967,8 +5087,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (hintBoring == null || hintBoring == UNKNOWN_BORING) { if (hintDes < 0) { - hintDes = (int) FloatMath.ceil(Layout. - getDesiredWidth(mHint, mTextPaint)); + hintDes = (int) FloatMath.ceil( + Layout.getDesiredWidth(mHint, mTextPaint)); } hintWidth = hintDes; @@ -5014,20 +5134,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mLayout == null) { makeNewLayout(want, hintWant, boring, hintBoring, - width - getCompoundPaddingLeft() - getCompoundPaddingRight(), - false); + width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); } else if ((mLayout.getWidth() != want) || (hintWidth != hintWant) || (mLayout.getEllipsizedWidth() != width - getCompoundPaddingLeft() - getCompoundPaddingRight())) { if (mHint == null && mEllipsize == null && want > mLayout.getWidth() && (mLayout instanceof BoringLayout || - (fromexisting && des >= 0 && des <= want))) { + (fromexisting && des >= 0 && des <= want))) { mLayout.increaseWidthTo(want); } else { makeNewLayout(want, hintWant, boring, hintBoring, - width - getCompoundPaddingLeft() - getCompoundPaddingRight(), - false); + width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); } } else { // Width has not changed. @@ -5048,11 +5166,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - int unpaddedHeight = height - getCompoundPaddingTop() - - getCompoundPaddingBottom(); + int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom(); if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) { - unpaddedHeight = Math.min(unpaddedHeight, - mLayout.getLineTop(mMaximum)); + unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum)); } /* @@ -5071,8 +5187,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private int getDesiredHeight() { - return Math.max(getDesiredHeight(mLayout, true), - getDesiredHeight(mHintLayout, false)); + return Math.max( + getDesiredHeight(mLayout, true), + getDesiredHeight(mHintLayout, mEllipsize != null)); } private int getDesiredHeight(Layout layout, boolean cap) { @@ -5715,6 +5832,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void startMarquee() { + // Do not ellipsize EditText + if (mInput != null) return; + if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) { return; } @@ -6129,10 +6249,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private class ChangeWatcher implements TextWatcher, SpanWatcher { + + private CharSequence mBeforeText; + public void beforeTextChanged(CharSequence buffer, int start, int before, int after) { if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start + " before=" + before + " after=" + after + ": " + buffer); + + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + mBeforeText = buffer.toString(); + } + TextView.this.sendBeforeTextChanged(buffer, start, before, after); } @@ -6141,6 +6269,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start + " before=" + before + " after=" + after + ": " + buffer); TextView.this.handleTextChanged(buffer, start, before, after); + + if (AccessibilityManager.getInstance(mContext).isEnabled() && + (isFocused() || isSelected() && + isShown())) { + sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); + mBeforeText = null; + } } public void afterTextChanged(Editable buffer) { @@ -6336,6 +6471,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode != InputMethodManager.RESULT_SHOWN) { + final int len = mText.length(); + if (mNewStart > len) { + mNewStart = len; + } + if (mNewEnd > len) { + mNewEnd = len; + } Selection.setSelection((Spannable)mText, mNewStart, mNewEnd); } } @@ -6525,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: @@ -6776,6 +6919,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean isPassword = + (mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) == + (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + + if (!isPassword) { + CharSequence text = getText(); + if (TextUtils.isEmpty(text)) { + text = getHint(); + } + if (!TextUtils.isEmpty(text)) { + if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) { + text = text.subSequence(0, AccessibilityEvent.MAX_TEXT_LENGTH + 1); + } + event.getText().add(text); + } + } else { + event.setPassword(isPassword); + } + return false; + } + + void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, + int fromIndex, int removedCount, int addedCount) { + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + event.setFromIndex(fromIndex); + event.setRemovedCount(removedCount); + event.setAddedCount(addedCount); + event.setBeforeText(beforeText); + sendAccessibilityEventUnchecked(event); + } + + @Override protected void onCreateContextMenu(ContextMenu menu) { super.onCreateContextMenu(menu); boolean added = false; diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index ff747878fba0..670692f552df 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -21,8 +21,8 @@ import android.app.ITransientNotification; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; -import android.os.RemoteException; import android.os.Handler; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.view.Gravity; @@ -278,7 +278,7 @@ public class Toast { } tv.setText(s); } - + // ======================================================================================= // All the gunk below is the interaction with the Notification Service, which handles // the proper ordering of these system-wide. @@ -373,6 +373,7 @@ public class Toast { TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } + mView = null; } } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 6d3a2d3602c0..20dd8a6bed91 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -55,6 +55,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { private SurfaceHolder mSurfaceHolder = null; private MediaPlayer mMediaPlayer = null; private boolean mIsPrepared; + private boolean mIsPlaybackCompleted; private int mVideoWidth; private int mVideoHeight; private int mSurfaceWidth; @@ -260,7 +261,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mSeekWhenPrepared = 0; } if (mStartWhenPrepared) { - mMediaPlayer.start(); + start(); mStartWhenPrepared = false; if (mMediaController != null) { mMediaController.show(); @@ -281,7 +282,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mSeekWhenPrepared = 0; } if (mStartWhenPrepared) { - mMediaPlayer.start(); + start(); mStartWhenPrepared = false; } } @@ -291,6 +292,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { private MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) { + mIsPlaybackCompleted = true; if (mMediaController != null) { mMediaController.hide(); } @@ -405,7 +407,9 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mMediaPlayer.seekTo(mSeekWhenPrepared); mSeekWhenPrepared = 0; } - mMediaPlayer.start(); + if (!mIsPlaybackCompleted) { + start(); + } if (mMediaController != null) { mMediaController.show(); } @@ -490,6 +494,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { } public void start() { + mIsPlaybackCompleted = false; if (mMediaPlayer != null && mIsPrepared) { mMediaPlayer.start(); mStartWhenPrepared = false; diff --git a/core/java/android/widget/ViewSwitcher.java b/core/java/android/widget/ViewSwitcher.java index f4f23a8f655b..0dcaf954279e 100644 --- a/core/java/android/widget/ViewSwitcher.java +++ b/core/java/android/widget/ViewSwitcher.java @@ -16,8 +16,6 @@ package android.widget; -import java.util.Map; - import android.content.Context; import android.util.AttributeSet; import android.view.View; diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index d9fb78b81d38..bae4dad5c01f 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -81,27 +81,27 @@ public class ZoomButtonsController implements View.OnTouchListener { private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20; private int mTouchPaddingScaledSq; - private Context mContext; - private WindowManager mWindowManager; + private final Context mContext; + private final WindowManager mWindowManager; private boolean mAutoDismissControls = true; /** * The view that is being zoomed by this zoom controller. */ - private View mOwnerView; + private final View mOwnerView; /** * The location of the owner view on the screen. This is recalculated * each time the zoom controller is shown. */ - private int[] mOwnerViewRawLocation = new int[2]; + private final int[] mOwnerViewRawLocation = new int[2]; /** * The container that is added as a window. */ - private FrameLayout mContainer; + private final FrameLayout mContainer; private LayoutParams mContainerLayoutParams; - private int[] mContainerRawLocation = new int[2]; + private final int[] mContainerRawLocation = new int[2]; private ZoomControls mControls; @@ -113,7 +113,7 @@ public class ZoomButtonsController implements View.OnTouchListener { /** * The {@link #mTouchTargetView}'s location in window, set on touch down. */ - private int[] mTouchTargetWindowLocation = new int[2]; + private final int[] mTouchTargetWindowLocation = new int[2]; /** * If the zoom controller is dismissed but the user is still in a touch @@ -128,8 +128,8 @@ public class ZoomButtonsController implements View.OnTouchListener { /** Whether the container has been added to the window manager. */ private boolean mIsVisible; - private Rect mTempRect = new Rect(); - private int[] mTempIntArray = new int[2]; + private final Rect mTempRect = new Rect(); + private final int[] mTempIntArray = new int[2]; private OnZoomListener mCallback; @@ -141,13 +141,13 @@ public class ZoomButtonsController implements View.OnTouchListener { */ private Runnable mPostedVisibleInitializer; - private IntentFilter mConfigurationChangedFilter = + private final IntentFilter mConfigurationChangedFilter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); /** * Needed to reposition the zoom controls after configuration changes. */ - private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!mIsVisible) return; @@ -167,7 +167,7 @@ public class ZoomButtonsController implements View.OnTouchListener { */ private static final int MSG_POST_SET_VISIBLE = 4; - private Handler mHandler = new Handler() { + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -444,6 +444,9 @@ public class ZoomButtonsController implements View.OnTouchListener { } private void refreshPositioningVariables() { + // if the mOwnerView is detached from window then skip. + if (mOwnerView.getWindowToken() == null) return; + // Position the zoom controls on the bottom of the owner view. int ownerHeight = mOwnerView.getHeight(); int ownerWidth = mOwnerView.getWidth(); diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index e1ff2a52062f..4bac5933d7ed 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -18,6 +18,8 @@ package com.android.internal.app; import com.android.internal.os.BatteryStatsImpl; +import android.telephony.SignalStrength; + interface IBatteryStats { byte[] getStatistics(); void noteStartWakelock(int uid, String name, int type); @@ -33,8 +35,9 @@ interface IBatteryStats { void noteUserActivity(int uid, int event); void notePhoneOn(); void notePhoneOff(); - void notePhoneSignalStrength(int asu); + 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 ce39768137e2..af06965ae123 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -16,6 +16,8 @@ package com.android.internal.backup; +import android.backup.RestoreSet; +import android.content.pm.PackageInfo; import android.os.ParcelFileDescriptor; /** {@hide} */ @@ -25,7 +27,7 @@ interface IBackupTransport { 1. set up the connection to the destination - set up encryption - for Google cloud, log in using the user's gaia credential or whatever - - for sd, spin off the backup transport and establish communication with it + - for adb, just set up the all-in-one destination file 2. send each app's backup transaction - parse the data file for key/value pointers etc - send key/blobsize set to the Google cloud, get back quota ok/rejected response @@ -36,34 +38,112 @@ interface IBackupTransport { - sd target streams raw data into encryption envelope then to sd? 3. shut down connection to destination - cloud: tear down connection etc - - sd: close the file and shut down the writer proxy + - adb: close the file */ /** - * 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. + * 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 Zero on success; a nonzero error code on failure. + * @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. */ - int startSession(); + String transportDirName(); /** - * Send one application's data to the backup destination. + * 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. * - * @param packageName The identity of the application whose data is being backed up. + * <p>If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + */ + long requestBackupTime(); + + /** + * 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. + * BackupService.doBackup() method. This may be a pipe rather than a file on + * persistent media, so it may not be seekable. + * @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 performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); + + /** + * Erase the give application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. + * + * @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 clearBackupData(in PackageInfo packageInfo); + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup} or {@link clearBackupData} to ensure that + * all data is sent. Only when this method returns true can a backup be assumed + * to have succeeded. + * + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK. */ - int performBackup(String packageName, in ParcelFileDescriptor data); + boolean finishBackup(); /** - * Terminate the backup session, closing files, freeing memory, and cleaning up whatever - * other state the transport required. + * Get the set of backups currently available over this transport. * - * @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. + * @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(); + + /** + * 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}. + * @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}). + */ + boolean startRestore(long token, in PackageInfo[] packages); + + /** + * 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). + */ + String nextRestorePackage(); + + /** + * 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 new file mode 100644 index 000000000000..2facce2ed190 --- /dev/null +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -0,0 +1,200 @@ +package com.android.internal.backup; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.RestoreSet; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import org.bouncycastle.util.encoders.Base64; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Backup transport for stashing stuff into a known location on disk, and + * later restoring from there. For testing only. + */ + +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 PackageInfo[] mRestorePackages = null; + private int mRestorePackage = -1; // Index into mRestorePackages + + + public LocalTransport(Context context) { + if (DEBUG) Log.v(TAG, "Transport constructed"); + mContext = context; + mPackageManager = context.getPackageManager(); + } + + + public String transportDirName() throws RemoteException { + return TRANSPORT_DIR_NAME; + } + + public long requestBackupTime() throws RemoteException { + // any time is a good time for local backup + return 0; + } + + public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) + throws RemoteException { + if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); + + File packageDir = new File(mDataDir, packageInfo.packageName); + packageDir.mkdirs(); + + // Each 'record' in the restore set is kept in its own file, named by + // the record key. Wind through the data file, extracting individual + // record operations and building a set of all the updates to apply + // in this update. + BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); + try { + int bufSize = 512; + byte[] buf = new byte[bufSize]; + while (changeSet.readNextHeader()) { + String key = changeSet.getKey(); + String base64Key = new String(Base64.encode(key.getBytes())); + File entityFile = new File(packageDir, base64Key); + + int dataSize = changeSet.getDataSize(); + + if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize + + " key64=" + base64Key); + + if (dataSize >= 0) { + FileOutputStream entity = new FileOutputStream(entityFile); + + if (dataSize > bufSize) { + bufSize = dataSize; + buf = new byte[bufSize]; + } + changeSet.readEntityData(buf, 0, dataSize); + if (DEBUG) Log.v(TAG, " data size " + dataSize); + + try { + entity.write(buf, 0, dataSize); + } catch (IOException e) { + Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); + return false; + } finally { + entity.close(); + } + } else { + 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); + return false; + } + } + + public boolean clearBackupData(PackageInfo packageInfo) { + if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); + + File packageDir = new File(mDataDir, packageInfo.packageName); + for (File f : packageDir.listFiles()) { + f.delete(); + } + packageDir.delete(); + return true; + } + + public boolean finishBackup() throws RemoteException { + if (DEBUG) Log.v(TAG, "finishBackup()"); + return true; + } + + // Restore handling + public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + // one hardcoded restore set + RestoreSet set = new RestoreSet("Local disk image", "flash", 0); + RestoreSet[] array = { set }; + return array; + } + + public boolean startRestore(long token, PackageInfo[] packages) { + if (DEBUG) Log.v(TAG, "start restore " + token); + mRestorePackages = packages; + mRestorePackage = -1; + return true; + } + + 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; + } + } + + if (DEBUG) Log.v(TAG, " no more packages to restore"); + return ""; + } + + 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 (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(); + } + } + return true; + } catch (IOException e) { + Log.e(TAG, "Unable to read backup records", e); + return false; + } + } + + 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 000000000000..6b396d777a66 --- /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 e8356a2c6415..a03802dff012 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -23,23 +23,24 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.Set; /** * All information we are collecting about things that can happen that impact @@ -54,7 +55,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 34; + private static final int VERSION = 39; private final File mFile; private final File mBackupFile; @@ -95,7 +96,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mScreenOn; StopwatchTimer mScreenOnTimer; - + int mScreenBrightnessBin = -1; final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -104,6 +105,12 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; + boolean mAudioOn; + StopwatchTimer mAudioOnTimer; + + boolean mVideoOn; + StopwatchTimer mVideoOnTimer; + int mPhoneSignalStrengthBin = -1; final StopwatchTimer[] mPhoneSignalStrengthsTimer = new StopwatchTimer[NUM_SIGNAL_STRENGTH_BINS]; @@ -132,18 +139,27 @@ public final class BatteryStatsImpl extends BatteryStats { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; - + long mUnpluggedBatteryUptime; long mUnpluggedBatteryRealtime; - + /* * These keep track of battery levels (1-100) at the last plug event and the last unplug event. */ int mDischargeStartLevel; int mDischargeCurrentLevel; - + long mLastWriteTime = 0; // Milliseconds - + + // Mobile data transferred while on battery + private long[] mMobileDataTx = new long[4]; + private long[] mMobileDataRx = new long[4]; + private long[] mTotalDataTx = new long[4]; + private long[] mTotalDataRx = new long[4]; + + private long mRadioDataUptime; + private long mRadioDataStart; + /* * Holds a SamplingTimer associated with each kernel wakelock name being tracked. */ @@ -175,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; @@ -319,6 +337,13 @@ public final class BatteryStatsImpl extends BatteryStats { */ long mUnpluggedTime; + /** + * Constructs from a parcel. + * @param type + * @param unpluggables + * @param powerType + * @param in + */ Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) { mType = type; @@ -631,7 +656,6 @@ public final class BatteryStatsImpl extends BatteryStats { * was actually held for an interesting duration. */ long mAcquireTime; - StopwatchTimer(int type, ArrayList<StopwatchTimer> timerPool, ArrayList<Unpluggable> unpluggables, Parcel in) { @@ -692,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) { @@ -882,7 +910,40 @@ public final class BatteryStatsImpl extends BatteryStats { } return kwlt; } - + + private void doDataPlug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED]; + dataTransfer[STATS_UNPLUGGED] = -1; + } + + private void doDataUnplug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_UNPLUGGED] = currentBytes; + } + + private long getCurrentRadioDataUptimeMs() { + try { + File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms"); + if (!awakeTimeFile.exists()) return 0; + BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile)); + String line = br.readLine(); + br.close(); + return Long.parseLong(line); + } catch (NumberFormatException nfe) { + // Nothing + } catch (IOException ioe) { + // Nothing + } + return 0; + } + + public long getRadioDataUptimeMs() { + if (mRadioDataStart == -1) { + return mRadioDataUptime; + } else { + return getCurrentRadioDataUptimeMs() - mRadioDataStart; + } + } + public void doUnplug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -894,8 +955,16 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime); } + // Track total mobile data + doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataStart = getCurrentRadioDataUptimeMs(); + mRadioDataUptime = 0; } - + public void doPlug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -911,16 +980,23 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).plug(batteryUptime, batteryRealtime); } + doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataUptime = getRadioDataUptimeMs(); + mRadioDataStart = -1; } - + 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() { if (!mScreenOn) { mScreenOn = true; @@ -962,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() { @@ -981,15 +1054,43 @@ public final class BatteryStatsImpl extends BatteryStats { mPhoneOnTimer.stopRunningLocked(this); } } - - public void notePhoneSignalStrengthLocked(int asu) { + + 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; - if (asu < 0 || asu >= 99) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - else if (asu >= 16) bin = SIGNAL_STRENGTH_GREAT; - else if (asu >= 8) bin = SIGNAL_STRENGTH_GOOD; - else if (asu >= 4) bin = SIGNAL_STRENGTH_MODERATE; - else bin = SIGNAL_STRENGTH_POOR; + + if (!signalStrength.isGsm()) { + int dBm = signalStrength.getCdmaDbm(); + if (dBm >= -75) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (dBm >= -85) bin = SIGNAL_STRENGTH_GREAT; + else if (dBm >= -95) bin = SIGNAL_STRENGTH_GOOD; + else if (dBm >= -100) bin = SIGNAL_STRENGTH_MODERATE; + else bin = SIGNAL_STRENGTH_POOR; + } else { + int asu = signalStrength.getGsmSignalStrength(); + if (asu < 0 || asu >= 99) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (asu >= 16) bin = SIGNAL_STRENGTH_GREAT; + else if (asu >= 8) bin = SIGNAL_STRENGTH_GOOD; + else if (asu >= 4) bin = SIGNAL_STRENGTH_MODERATE; + else bin = SIGNAL_STRENGTH_POOR; + } if (mPhoneSignalStrengthBin != bin) { if (mPhoneSignalStrengthBin >= 0) { mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this); @@ -1017,6 +1118,7 @@ public final class BatteryStatsImpl extends BatteryStats { break; } } + if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData); if (mPhoneDataConnectionType != bin) { if (mPhoneDataConnectionType >= 0) { mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this); @@ -1033,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(); } } @@ -1052,14 +1148,43 @@ 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; } } + + public void noteAudioOnLocked(int uid) { + if (!mAudioOn) { + mAudioOn = true; + mAudioOnTimer.startRunningLocked(this); + } + getUidStatsLocked(uid).noteAudioTurnedOnLocked(); + } + public void noteAudioOffLocked(int uid) { + if (mAudioOn) { + mAudioOn = false; + mAudioOnTimer.stopRunningLocked(this); + } + getUidStatsLocked(uid).noteAudioTurnedOffLocked(); + } + + public void noteVideoOnLocked(int uid) { + if (!mVideoOn) { + mVideoOn = true; + mVideoOnTimer.startRunningLocked(this); + } + getUidStatsLocked(uid).noteVideoTurnedOnLocked(); + } + + public void noteVideoOffLocked(int uid) { + if (mVideoOn) { + mVideoOn = false; + mVideoOnTimer.stopRunningLocked(this); + } + getUidStatsLocked(uid).noteVideoTurnedOffLocked(); + } + public void noteWifiRunningLocked() { if (!mWifiRunning) { mWifiRunning = true; @@ -1089,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) { @@ -1139,7 +1246,7 @@ public final class BatteryStatsImpl extends BatteryStats { return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked( batteryRealtime, which); } - + @Override public int getInputEventCount(int which) { return mInputEventCounter.getCountLocked(which); } @@ -1147,7 +1254,7 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long getPhoneOnTime(long batteryRealtime, int which) { return mPhoneOnTimer.getTotalTimeLocked(batteryRealtime, which); } - + @Override public long getPhoneSignalStrengthTime(int strengthBin, long batteryRealtime, int which) { return mPhoneSignalStrengthsTimer[strengthBin].getTotalTimeLocked( @@ -1214,9 +1321,15 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mScanWifiLockOut; StopwatchTimer mScanWifiLockTimer; - + boolean mWifiMulticastEnabled; StopwatchTimer mWifiMulticastTimer; + + boolean mAudioTurnedOn; + StopwatchTimer mAudioTurnedOnTimer; + + boolean mVideoTurnedOn; + StopwatchTimer mVideoTurnedOnTimer; Counter[] mUserActivityCounters; @@ -1247,6 +1360,8 @@ public final class BatteryStatsImpl extends BatteryStats { mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables); mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED, null, mUnpluggables); + mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables); + mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables); } @Override @@ -1268,11 +1383,13 @@ public final class BatteryStatsImpl extends BatteryStats { public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() { return mPackageStats; } - + + @Override public int getUid() { return mUid; } - + + @Override public long getTcpBytesReceived(int which) { if (which == STATS_LAST) { return mLoadedTcpBytesReceived; @@ -1291,7 +1408,8 @@ public final class BatteryStatsImpl extends BatteryStats { return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0 ? (NetStat.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0); } - + + @Override public long getTcpBytesSent(int which) { if (which == STATS_LAST) { return mLoadedTcpBytesSent; @@ -1331,6 +1449,38 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public void noteVideoTurnedOnLocked() { + if (!mVideoTurnedOn) { + mVideoTurnedOn = true; + mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteVideoTurnedOffLocked() { + if (mVideoTurnedOn) { + mVideoTurnedOn = false; + mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteAudioTurnedOnLocked() { + if (!mAudioTurnedOn) { + mAudioTurnedOn = true; + mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteAudioTurnedOffLocked() { + if (mAudioTurnedOn) { + mAudioTurnedOn = false; + mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + + @Override public void noteFullWifiLockReleasedLocked() { if (mFullWifiLockOut) { mFullWifiLockOut = false; @@ -1374,7 +1524,17 @@ public final class BatteryStatsImpl extends BatteryStats { public long getWifiTurnedOnTime(long batteryRealtime, int which) { return mWifiTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); } - + + @Override + public long getAudioTurnedOnTime(long batteryRealtime, int which) { + return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); + } + + @Override + public long getVideoTurnedOnTime(long batteryRealtime, int which) { + return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); + } + @Override public long getFullWifiLockTime(long batteryRealtime, int which) { return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which); @@ -1425,7 +1585,7 @@ public final class BatteryStatsImpl extends BatteryStats { return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0 ? (NetStat.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0); } - + void writeToParcelLocked(Parcel out, long batteryRealtime) { out.writeInt(mWakelockStats.size()); for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) { @@ -1463,6 +1623,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mTcpBytesSentAtLastUnplug); mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime); mFullWifiLockTimer.writeToParcel(out, batteryRealtime); + mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime); + mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime); mScanWifiLockTimer.writeToParcel(out, batteryRealtime); mWifiMulticastTimer.writeToParcel(out, batteryRealtime); if (mUserActivityCounters == null) { @@ -1522,6 +1684,10 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables, in); mFullWifiLockOut = false; mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables, in); + mAudioTurnedOn = false; + mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables, in); + mVideoTurnedOn = false; + mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables, in); mScanWifiLockOut = false; mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables, in); mWifiMulticastEnabled = false; @@ -1632,7 +1798,8 @@ public final class BatteryStatsImpl extends BatteryStats { public Timer getSensorTime() { return mTimer; } - + + @Override public int getHandle() { return mHandle; } @@ -1658,6 +1825,11 @@ public final class BatteryStatsImpl extends BatteryStats { int mStarts; /** + * Amount of time the process was running in the foreground. + */ + long mForegroundTime; + + /** * The amount of user time loaded from a previous save. */ long mLoadedUserTime; @@ -1673,6 +1845,11 @@ public final class BatteryStatsImpl extends BatteryStats { int mLoadedStarts; /** + * The amount of foreground time loaded from a previous save. + */ + long mLoadedForegroundTime; + + /** * The amount of user time loaded from the previous run. */ long mLastUserTime; @@ -1688,6 +1865,11 @@ public final class BatteryStatsImpl extends BatteryStats { int mLastStarts; /** + * The amount of foreground time loaded from the previous run + */ + long mLastForegroundTime; + + /** * The amount of user time when last unplugged. */ long mUnpluggedUserTime; @@ -1702,6 +1884,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mUnpluggedStarts; + /** + * The amount of foreground time since unplugged. + */ + long mUnpluggedForegroundTime; + Proc() { mUnpluggables.add(this); } @@ -1710,6 +1897,7 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedUserTime = mUserTime; mUnpluggedSystemTime = mSystemTime; mUnpluggedStarts = mStarts; + mUnpluggedForegroundTime = mForegroundTime; } public void plug(long batteryUptime, long batteryRealtime) { @@ -1721,30 +1909,38 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mUserTime); out.writeLong(mSystemTime); + out.writeLong(mForegroundTime); out.writeInt(mStarts); out.writeLong(mLoadedUserTime); out.writeLong(mLoadedSystemTime); + out.writeLong(mLoadedForegroundTime); out.writeInt(mLoadedStarts); out.writeLong(mLastUserTime); out.writeLong(mLastSystemTime); + out.writeLong(mLastForegroundTime); out.writeInt(mLastStarts); out.writeLong(mUnpluggedUserTime); out.writeLong(mUnpluggedSystemTime); + out.writeLong(mUnpluggedForegroundTime); out.writeInt(mUnpluggedStarts); } void readFromParcelLocked(Parcel in) { mUserTime = in.readLong(); mSystemTime = in.readLong(); + mForegroundTime = in.readLong(); mStarts = in.readInt(); mLoadedUserTime = in.readLong(); mLoadedSystemTime = in.readLong(); + mLoadedForegroundTime = in.readLong(); mLoadedStarts = in.readInt(); mLastUserTime = in.readLong(); mLastSystemTime = in.readLong(); + mLastForegroundTime = in.readLong(); mLastStarts = in.readInt(); mUnpluggedUserTime = in.readLong(); mUnpluggedSystemTime = in.readLong(); + mUnpluggedForegroundTime = in.readLong(); mUnpluggedStarts = in.readInt(); } @@ -1757,6 +1953,10 @@ public final class BatteryStatsImpl extends BatteryStats { mSystemTime += stime; } + public void addForegroundTimeLocked(long ttime) { + mForegroundTime += ttime; + } + public void incStartsLocked() { mStarts++; } @@ -1794,6 +1994,22 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public long getForegroundTime(int which) { + long val; + if (which == STATS_LAST) { + val = mLastForegroundTime; + } else { + val = mForegroundTime; + if (which == STATS_CURRENT) { + val -= mLoadedForegroundTime; + } else if (which == STATS_UNPLUGGED) { + val -= mUnpluggedForegroundTime; + } + } + return val; + } + + @Override public int getStarts(int which) { int val; if (which == STATS_LAST) { @@ -2315,7 +2531,7 @@ public final class BatteryStatsImpl extends BatteryStats { StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, false); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); - } + } } public BatteryStatsImpl getBatteryStats() { @@ -2526,7 +2742,44 @@ public final class BatteryStatsImpl extends BatteryStats { public long getBatteryRealtime(long curTime) { return getBatteryRealtimeLocked(curTime); } - + + private long getTcpBytes(long current, long[] dataBytes, int which) { + if (which == STATS_LAST) { + return dataBytes[STATS_LAST]; + } else { + if (which == STATS_UNPLUGGED) { + if (dataBytes[STATS_UNPLUGGED] < 0) { + return dataBytes[STATS_LAST]; + } else { + return current - dataBytes[STATS_UNPLUGGED]; + } + } else if (which == STATS_TOTAL) { + return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL]; + } + return current - dataBytes[STATS_CURRENT]; + } + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesSent(int which) { + return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesSent(int which) { + return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which); + } + @Override public int getDischargeStartLevel() { synchronized(this) { @@ -2567,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. @@ -2578,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. */ @@ -2752,6 +3023,10 @@ public final class BatteryStatsImpl extends BatteryStats { u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in); u.mFullWifiLockOut = false; u.mFullWifiLockTimer.readSummaryFromParcelLocked(in); + u.mAudioTurnedOn = false; + u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in); + u.mVideoTurnedOn = false; + u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in); u.mScanWifiLockOut = false; u.mScanWifiLockTimer.readSummaryFromParcelLocked(in); u.mWifiMulticastEnabled = false; @@ -2888,6 +3163,8 @@ public final class BatteryStatsImpl extends BatteryStats { u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL); + u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); + u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL); @@ -3046,11 +3323,24 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeCurrentLevel = in.readInt(); mLastWriteTime = in.readLong(); + mMobileDataRx[STATS_LAST] = in.readLong(); + mMobileDataRx[STATS_UNPLUGGED] = -1; + mMobileDataTx[STATS_LAST] = in.readLong(); + mMobileDataTx[STATS_UNPLUGGED] = -1; + mTotalDataRx[STATS_LAST] = in.readLong(); + mTotalDataRx[STATS_UNPLUGGED] = -1; + mTotalDataTx[STATS_LAST] = in.readLong(); + mTotalDataTx[STATS_UNPLUGGED] = -1; + + mRadioDataUptime = in.readLong(); + mRadioDataStart = -1; + mKernelWakelockStats.clear(); int NKW = in.readInt(); for (int ikw = 0; ikw < NKW; ikw++) { if (in.readInt() != 0) { String wakelockName = in.readString(); + in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel SamplingTimer kwlt = new SamplingTimer(mUnpluggables, mOnBattery, in); mKernelWakelockStats.put(wakelockName, kwlt); } @@ -3119,6 +3409,14 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargeCurrentLevel); out.writeLong(mLastWriteTime); + out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED)); + + // Write radio uptime for data + out.writeLong(getRadioDataUptimeMs()); + out.writeInt(mKernelWakelockStats.size()); for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { SamplingTimer kwlt = ent.getValue(); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java new file mode 100644 index 000000000000..4a8d8b182f90 --- /dev/null +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -0,0 +1,238 @@ +/* + * 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.os; + + +import android.content.Context; +import android.content.res.XmlResourceParser; + +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Reports power consumption values for various device activities. Reads values from an XML file. + * Customize the XML file for different devices. + * [hidden] + */ +public class PowerProfile { + + /** + * No power consumption, or accounted for elsewhere. + */ + public static final String POWER_NONE = "none"; + + /** + * Power consumption when CPU is in power collapse mode. + */ + public static final String POWER_CPU_IDLE = "cpu.idle"; + + /** + * Power consumption when CPU is running at normal speed. + */ + public static final String POWER_CPU_NORMAL = "cpu.normal"; + + /** + * Power consumption when CPU is running at full speed. + */ + public static final String POWER_CPU_FULL = "cpu.full"; + + /** + * Power consumption when WiFi driver is scanning for networks. + */ + public static final String POWER_WIFI_SCAN = "wifi.scan"; + + /** + * Power consumption when WiFi driver is on. + */ + public static final String POWER_WIFI_ON = "wifi.on"; + + /** + * Power consumption when WiFi driver is transmitting/receiving. + */ + public static final String POWER_WIFI_ACTIVE = "wifi.active"; + + /** + * Power consumption when GPS is on. + */ + public static final String POWER_GPS_ON = "gps.on"; + + /** + * Power consumption when Bluetooth driver is on. + */ + public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; + + /** + * Power consumption when Bluetooth driver is transmitting/receiving. + */ + public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; + + /** + * Power consumption when screen is on, not including the backlight power. + */ + public static final String POWER_SCREEN_ON = "screen.on"; + + /** + * Power consumption when cell radio is on but not on a call. + */ + public static final String POWER_RADIO_ON = "radio.on"; + + /** + * Power consumption when talking on the phone. + */ + public static final String POWER_RADIO_ACTIVE = "radio.active"; + + /** + * Power consumption at full backlight brightness. If the backlight is at + * 50% brightness, then this should be multiplied by 0.5 + */ + public static final String POWER_SCREEN_FULL = "screen.full"; + + /** + * Power consumed by the audio hardware when playing back audio content. This is in addition + * to the CPU power, probably due to a DSP and / or amplifier. + */ + public static final String POWER_AUDIO = "dsp.audio"; + + /** + * Power consumed by any media hardware when playing back video content. This is in addition + * to the CPU power, probably due to a DSP. + */ + public static final String POWER_VIDEO = "dsp.video"; + + static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>(); + + private static final String TAG_DEVICE = "device"; + private static final String TAG_ITEM = "item"; + private static final String TAG_ARRAY = "array"; + private static final String TAG_ARRAYITEM = "value"; + private static final String ATTR_NAME = "name"; + + public PowerProfile(Context context) { + // Read the XML file for the given profile (normally only one per + // device) + if (sPowerMap.size() == 0) { + readPowerValuesFromXml(context); + } + } + + private void readPowerValuesFromXml(Context context) { + int id = com.android.internal.R.xml.power_profile; + XmlResourceParser parser = context.getResources().getXml(id); + boolean parsingArray = false; + ArrayList<Double> array = new ArrayList<Double>(); + String arrayName = null; + + try { + XmlUtils.beginDocument(parser, TAG_DEVICE); + + while (true) { + XmlUtils.nextElement(parser); + + String element = parser.getName(); + if (element == null) break; + + if (parsingArray && !element.equals(TAG_ARRAYITEM)) { + // Finish array + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + parsingArray = false; + } + if (element.equals(TAG_ARRAY)) { + parsingArray = true; + array.clear(); + arrayName = parser.getAttributeValue(null, ATTR_NAME); + } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { + String name = null; + if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); + if (parser.next() == XmlPullParser.TEXT) { + String power = parser.getText(); + double value = 0; + try { + value = Double.valueOf(power); + } catch (NumberFormatException nfe) { + } + if (element.equals(TAG_ITEM)) { + sPowerMap.put(name, value); + } else if (parsingArray) { + array.add(value); + } + } + } + } + if (parsingArray) { + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + } + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + parser.close(); + } + } + + /** + * Returns the average current in mA consumed by the subsystem + * @param type the subsystem type + * @return the average current in milliAmps. + */ + public double getAveragePower(String type) { + if (sPowerMap.containsKey(type)) { + Object data = sPowerMap.get(type); + if (data instanceof Double[]) { + return ((Double[])data)[0]; + } else { + return (Double) sPowerMap.get(type); + } + } else { + return 0; + } + } + + /** + * Returns the average current in mA consumed by the subsystem for the given level. + * @param type the subsystem type + * @param level the level of power at which the subsystem is running. For instance, the + * signal strength of the cell network between 0 and 4 (if there are 4 bars max.). + * If there is no data for multiple levels, the level is ignored. + * @return the average current in milliAmps. + */ + 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 && level >= 0) { + return values[level]; + } else if (level < 0) { + return 0; + } else { + return values[values.length - 1]; + } + } else { + return (Double) data; + } + } else { + return 0; + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ac8b58934168..f67a235b0736 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -467,7 +467,7 @@ public class ZygoteInit { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", - "--capabilities=121715744,121715744", + "--capabilities=130104352,130104352", "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer", diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java index 4757919f23f5..86f74f302518 100644 --- a/core/java/com/android/internal/util/BitwiseInputStream.java +++ b/core/java/com/android/internal/util/BitwiseInputStream.java @@ -65,30 +65,31 @@ public class BitwiseInputStream { /** * Read some data and increment the current position. * - * @param bits the amount of data to read (gte 0, lte 8) + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. * + * @param bits the amount of data to read (gte 0, lte 8) * @return byte of read data (possibly partially filled, from lsb) */ - public byte read(int bits) throws AccessException { + public int read(int bits) throws AccessException { int index = mPos >>> 3; int offset = 16 - (mPos & 0x07) - bits; // &7==%8 if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) { throw new AccessException("illegal read " + "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")"); } - int data = (mBuf[index] & 0x00FF) << 8; - if (offset < 8) data |= (mBuf[index + 1] & 0xFF); + int data = (mBuf[index] & 0xFF) << 8; + if (offset < 8) data |= mBuf[index + 1] & 0xFF; data >>>= offset; data &= (-1 >>> (32 - bits)); mPos += bits; - return (byte)data; + return data; } /** * Read data in bulk into a byte array and increment the current position. * * @param bits the amount of data to read - * * @return newly allocated byte array of read data */ public byte[] readByteArray(int bits) throws AccessException { diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java index 1b974ce4c9ee..70c0be81a56a 100644 --- a/core/java/com/android/internal/util/BitwiseOutputStream.java +++ b/core/java/com/android/internal/util/BitwiseOutputStream.java @@ -82,6 +82,9 @@ public class BitwiseOutputStream { /** * Write some data and increment the current position. * + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. + * * @param bits the amount of data to write (gte 0, lte 8) * @param data to write, will be masked to expose only bits param from lsb */ @@ -95,8 +98,8 @@ public class BitwiseOutputStream { int offset = 16 - (mPos & 0x07) - bits; // &7==%8 data <<= offset; mPos += bits; - mBuf[index] |= (data >>> 8); - if (offset < 8) mBuf[index + 1] |= (data & 0x00FF); + mBuf[index] |= data >>> 8; + if (offset < 8) mBuf[index + 1] |= data & 0xFF; } /** diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java index 871c925478e4..922f5bef8a49 100644 --- a/core/java/com/google/android/net/GoogleHttpClient.java +++ b/core/java/com/google/android/net/GoogleHttpClient.java @@ -37,6 +37,10 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.LayeredSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.impl.client.EntityEnclosingRequestWrapper; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.params.HttpParams; @@ -44,6 +48,8 @@ import org.apache.http.protocol.HttpContext; import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; @@ -66,25 +72,22 @@ public class GoogleHttpClient implements HttpClient { private final AndroidHttpClient mClient; private final ContentResolver mResolver; - private final String mUserAgent; + private final String mAppName, mUserAgent; + private final ThreadLocal<Boolean> mConnectionAllocated = new ThreadLocal<Boolean>(); /** - * Create an HTTP client. Normally one client is shared throughout an app. - * @param resolver to use for accessing URL rewriting rules. - * @param userAgent to report in your HTTP requests. - * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)} + * Create an HTTP client without SSL session persistence. + * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)} */ public GoogleHttpClient(ContentResolver resolver, String userAgent) { mClient = AndroidHttpClient.newInstance(userAgent); mResolver = resolver; - mUserAgent = userAgent; + mUserAgent = mAppName = userAgent; } /** - * GoogleHttpClient(Context, String, boolean) - without SSL session - * persistence. - * - * @deprecated use Context instead of ContentResolver. + * Create an HTTP client without SSL session persistence. + * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)} */ public GoogleHttpClient(ContentResolver resolver, String appAndVersion, boolean gzipCapable) { @@ -111,21 +114,72 @@ public class GoogleHttpClient implements HttpClient { * headers. Needed because Google servers require gzip in the User-Agent * in order to return gzip'd content. */ - public GoogleHttpClient(Context context, String appAndVersion, - boolean gzipCapable) { - this(context.getContentResolver(), SSLClientSessionCacheFactory.getCache(context), + public GoogleHttpClient(Context context, String appAndVersion, boolean gzipCapable) { + this(context.getContentResolver(), + SSLClientSessionCacheFactory.getCache(context), appAndVersion, gzipCapable); } - private GoogleHttpClient(ContentResolver resolver, SSLClientSessionCache cache, + private GoogleHttpClient(ContentResolver resolver, + SSLClientSessionCache cache, String appAndVersion, boolean gzipCapable) { String userAgent = appAndVersion + " (" + Build.DEVICE + " " + Build.ID + ")"; if (gzipCapable) { userAgent = userAgent + "; gzip"; } + mClient = AndroidHttpClient.newInstance(userAgent, cache); mResolver = resolver; + mAppName = appAndVersion; mUserAgent = userAgent; + + // Wrap all the socket factories with the appropriate wrapper. (Apache + // HTTP, curse its black and stupid heart, inspects the SocketFactory to + // see if it's a LayeredSocketFactory, so we need two wrapper classes.) + SchemeRegistry registry = getConnectionManager().getSchemeRegistry(); + for (String name : registry.getSchemeNames()) { + Scheme scheme = registry.unregister(name); + SocketFactory sf = scheme.getSocketFactory(); + if (sf instanceof LayeredSocketFactory) { + sf = new WrappedLayeredSocketFactory((LayeredSocketFactory) sf); + } else { + sf = new WrappedSocketFactory(sf); + } + registry.register(new Scheme(name, sf, scheme.getDefaultPort())); + } + } + + /** + * Delegating wrapper for SocketFactory records when sockets are connected. + * We use this to know whether a connection was created vs reused, to + * gather per-app statistics about connection reuse rates. + * (Note, we record only *connection*, not *creation* of sockets -- + * what we care about is the network overhead of an actual TCP connect.) + */ + private class WrappedSocketFactory implements SocketFactory { + private SocketFactory mDelegate; + private WrappedSocketFactory(SocketFactory delegate) { mDelegate = delegate; } + public final Socket createSocket() throws IOException { return mDelegate.createSocket(); } + public final boolean isSecure(Socket s) { return mDelegate.isSecure(s); } + + public final Socket connectSocket( + Socket s, String h, int p, + InetAddress la, int lp, HttpParams params) throws IOException { + mConnectionAllocated.set(Boolean.TRUE); + return mDelegate.connectSocket(s, h, p, la, lp, params); + } + } + + /** Like WrappedSocketFactory, but for the LayeredSocketFactory subclass. */ + private class WrappedLayeredSocketFactory + extends WrappedSocketFactory implements LayeredSocketFactory { + private LayeredSocketFactory mDelegate; + private WrappedLayeredSocketFactory(LayeredSocketFactory sf) { super(sf); mDelegate = sf; } + + public final Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + return mDelegate.createSocket(s, host, port, autoClose); + } } /** @@ -140,24 +194,21 @@ public class GoogleHttpClient implements HttpClient { public HttpResponse executeWithoutRewriting( HttpUriRequest request, HttpContext context) throws IOException { - String code = "Error"; + int code = -1; long start = SystemClock.elapsedRealtime(); try { HttpResponse response; - // TODO: if we're logging network stats, and if the apache library is configured - // to follow redirects, count each redirect as an additional round trip. + mConnectionAllocated.set(null); - // see if we're logging network stats. - boolean logNetworkStats = NetworkStatsEntity.shouldLogNetworkStats(); + if (NetworkStatsEntity.shouldLogNetworkStats()) { + // TODO: if we're logging network stats, and if the apache library is configured + // to follow redirects, count each redirect as an additional round trip. - if (logNetworkStats) { int uid = android.os.Process.myUid(); long startTx = NetStat.getUidTxBytes(uid); long startRx = NetStat.getUidRxBytes(uid); response = mClient.execute(request, context); - code = Integer.toString(response.getStatusLine().getStatusCode()); - HttpEntity origEntity = response == null ? null : response.getEntity(); if (origEntity != null) { // yeah, we compute the same thing below. we do need to compute this here @@ -165,30 +216,39 @@ public class GoogleHttpClient implements HttpClient { long now = SystemClock.elapsedRealtime(); long elapsed = now - start; NetworkStatsEntity entity = new NetworkStatsEntity(origEntity, - mUserAgent, uid, startTx, startRx, + mAppName, uid, startTx, startRx, elapsed /* response latency */, now /* processing start time */); response.setEntity(entity); } } else { response = mClient.execute(request, context); - code = Integer.toString(response.getStatusLine().getStatusCode()); } + code = response.getStatusLine().getStatusCode(); return response; - } catch (IOException e) { - code = "IOException"; - throw e; } finally { // Record some statistics to the checkin service about the outcome. // Note that this is only describing execute(), not body download. + // We assume the database writes are much faster than network I/O, + // and not worth running in a background thread or anything. try { long elapsed = SystemClock.elapsedRealtime() - start; ContentValues values = new ContentValues(); - values.put(Checkin.Stats.TAG, - Checkin.Stats.Tag.HTTP_STATUS + ":" + - mUserAgent + ":" + code); values.put(Checkin.Stats.COUNT, 1); values.put(Checkin.Stats.SUM, elapsed / 1000.0); + + values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REQUEST + ":" + mAppName); + mResolver.insert(Checkin.Stats.CONTENT_URI, values); + + // No sockets and no exceptions means we successfully reused a connection + if (mConnectionAllocated.get() == null && code >= 0) { + values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REUSED + ":" + mAppName); + mResolver.insert(Checkin.Stats.CONTENT_URI, values); + } + + String status = code < 0 ? "IOException" : Integer.toString(code); + values.put(Checkin.Stats.TAG, + Checkin.Stats.Tag.HTTP_STATUS + ":" + mAppName + ":" + status); mResolver.insert(Checkin.Stats.CONTENT_URI, values); } catch (Exception e) { Log.e(TAG, "Error recording stats", e); diff --git a/core/java/com/google/android/util/GoogleWebContentHelper.java b/core/java/com/google/android/util/GoogleWebContentHelper.java index 291142008934..8291e2964dc1 100644 --- a/core/java/com/google/android/util/GoogleWebContentHelper.java +++ b/core/java/com/google/android/util/GoogleWebContentHelper.java @@ -130,7 +130,18 @@ public class GoogleWebContentHelper { mWebView.loadUrl(mSecureUrl); return this; } - + + /** + * Loads data into the webview and also provides a failback url + * @return This {@link GoogleWebContentHelper} so methods can be chained. + */ + public GoogleWebContentHelper loadDataWithFailUrl(String base, String data, + String mimeType, String encoding, String failUrl) { + ensureViews(); + mWebView.loadDataWithBaseURL(base, data, mimeType, encoding, failUrl); + return this; + } + /** * Helper to handle the back key. Returns true if the back key was handled, * otherwise returns false. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4839b6f1d991..888cb1163997 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 \ @@ -117,7 +118,10 @@ LOCAL_SRC_FILES:= \ android_location_GpsLocationProvider.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ - android_backup_FileBackupHelper.cpp + android_backup_BackupDataInput.cpp \ + android_backup_BackupDataOutput.cpp \ + android_backup_FileBackupHelperBase.cpp \ + android_backup_BackupHelperDispatcher.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -164,8 +168,7 @@ LOCAL_SHARED_LIBRARIES := \ libicui18n \ libicudata \ libmedia \ - libwpa_client \ - libemoji + libwpa_client ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index aa6450d153c2..c81530122995 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -155,7 +155,10 @@ extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); extern int register_android_util_Base64(JNIEnv* env); extern int register_android_location_GpsLocationProvider(JNIEnv* env); -extern int register_android_backup_FileBackupHelper(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; @@ -1126,7 +1129,10 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_util_Base64), REG_JNI(register_android_location_GpsLocationProvider), - REG_JNI(register_android_backup_FileBackupHelper), + 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 b5539135facd..f3037f383039 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 b19e02126b75..b19e02126b75 100644 --- a/include/utils/TimeUtils.h +++ b/core/jni/TimeUtils.h diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 65f44d504257..af8ecf5cccc3 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -28,7 +28,7 @@ typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, static void FromColor_D32(void* dst, const SkColor src[], int width,
int, int) {
SkPMColor* d = (SkPMColor*)dst;
-
+
for (int i = 0; i < width; i++) {
*d++ = SkPreMultiplyColor(*src++);
}
@@ -37,7 +37,7 @@ static void FromColor_D32(void* dst, const SkColor src[], int width, static void FromColor_D565(void* dst, const SkColor src[], int width,
int x, int y) {
uint16_t* d = (uint16_t*)dst;
-
+
DITHER_565_SCAN(y);
for (int stop = x + width; x < stop; x++) {
SkColor c = *src++;
@@ -49,7 +49,7 @@ static void FromColor_D565(void* dst, const SkColor src[], int width, static void FromColor_D4444(void* dst, const SkColor src[], int width,
int x, int y) {
SkPMColor16* d = (SkPMColor16*)dst;
-
+
DITHER_4444_SCAN(y);
for (int stop = x + width; x < stop; x++) {
SkPMColor c = SkPreMultiplyColor(*src++);
@@ -80,14 +80,14 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, SkAutoLockPixels alp(dstBitmap);
void* dst = dstBitmap.getPixels();
FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
-
+
if (NULL == dst || NULL == proc) {
return false;
}
-
+
const jint* array = env->GetIntArrayElements(srcColors, NULL);
const SkColor* src = (const SkColor*)array + srcOffset;
-
+
// reset to to actual choice from caller
dst = dstBitmap.getAddr(x, y);
// now copy/convert each scanline
@@ -96,7 +96,7 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, src += srcStride;
dst = (char*)dst + dstBitmap.rowBytes();
}
-
+
env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
JNI_ABORT);
return true;
@@ -212,7 +212,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, doThrowIAE(env, "width and height must be > 0");
return NULL;
}
-
+
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
@@ -222,7 +222,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, }
SkBitmap bitmap;
-
+
bitmap.setConfig(config, width, height);
if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) {
return NULL;
@@ -232,7 +232,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, GraphicsJNI::SetPixels(env, jColors, offset, stride,
0, 0, width, height, bitmap);
}
-
+
return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,
NULL);
}
@@ -245,7 +245,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL;
}
-
+
return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,
NULL);
}
@@ -324,15 +324,15 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { SkDebugf("-------- unparcel parcel is NULL\n");
return NULL;
}
-
+
android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
+
const bool isMutable = p->readInt32() != 0;
const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
const int width = p->readInt32();
const int height = p->readInt32();
const int rowBytes = p->readInt32();
-
+
if (SkBitmap::kARGB_8888_Config != config &&
SkBitmap::kRGB_565_Config != config &&
SkBitmap::kARGB_4444_Config != config &&
@@ -355,7 +355,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { ctable = new SkColorTable(src, count);
}
}
-
+
if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) {
ctable->safeUnref();
delete bitmap;
@@ -368,7 +368,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { bitmap->lockPixels();
memcpy(bitmap->getPixels(), p->readInplace(size), size);
bitmap->unlockPixels();
-
+
return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL);
}
@@ -381,7 +381,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, }
android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
+
p->writeInt32(isMutable);
p->writeInt32(bitmap->config());
p->writeInt32(bitmap->width());
@@ -413,7 +413,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jintArray offsetXY) {
SkIPoint offset;
SkBitmap* dst = new SkBitmap;
-
+
src->extractAlpha(dst, paint, &offset);
if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
int* array = env->GetIntArrayElements(offsetXY, NULL);
@@ -421,7 +421,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, array[1] = offset.fY;
env->ReleaseIntArrayElements(offsetXY, array, 0);
}
-
+
return GraphicsJNI::createBitmap(env, dst, true, NULL);
}
@@ -439,7 +439,7 @@ static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, if (NULL == src) {
return 0;
}
-
+
SkColor dst[1];
proc(dst, src, 1, bitmap->getColorTable());
return dst[0];
@@ -449,7 +449,7 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, jintArray pixelArray, int offset, int stride,
int x, int y, int width, int height) {
SkAutoLockPixels alp(*bitmap);
-
+
ToColorProc proc = ChooseToColorProc(*bitmap);
if (NULL == proc) {
return;
@@ -498,7 +498,7 @@ static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, const SkBitmap* bitmap, jobject jbuffer) {
SkAutoLockPixels alp(*bitmap);
const void* src = bitmap->getPixels();
-
+
if (NULL != src) {
android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
@@ -511,7 +511,7 @@ static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, const SkBitmap* bitmap, jobject jbuffer) {
SkAutoLockPixels alp(*bitmap);
void* dst = bitmap->getPixels();
-
+
if (NULL != dst) {
android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
// the java side has already checked that buffer is large enough
@@ -519,6 +519,11 @@ static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, }
}
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ bitmap->lockPixels();
+ bitmap->unlockPixels();
+}
+
///////////////////////////////////////////////////////////////////////////////
#include <android_runtime/AndroidRuntime.h>
@@ -552,7 +557,8 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
(void*)Bitmap_copyPixelsToBuffer },
{ "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsFromBuffer }
+ (void*)Bitmap_copyPixelsFromBuffer },
+ { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw }
};
#define kClassPathName "android/graphics/Bitmap"
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 1fd15d687baf..137707fa93bf 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -311,7 +311,7 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, int sampleSize) { SkPixelRef* pr; // only use ashmem for large images, since mmaps come at a price - if (bitmap->getSize() >= 32 * 65536) { + if (bitmap->getSize() >= 32 * 1024) { pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); } else { pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); @@ -520,7 +520,10 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, */ AutoFDSeek as(descriptor); - return doDecode(env, stream, padding, bitmapFactoryOptions, true); + /* Allow purgeable iff we own the FD, i.e., in the puregeable and + shareable case. + */ + return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); } /* make a deep copy of the asset, and return it as a stream, or NULL if there diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 76e6f028d7b6..d1fe83edce66 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -69,6 +69,8 @@ public: static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) { obj->reset(); + // utf16 is required for java + obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); } static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) { diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp new file mode 100644 index 000000000000..cf8a8e8a5efa --- /dev/null +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -0,0 +1,164 @@ +/* + * 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 "FileBackupHelper_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <utils/BackupHelpers.h> + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +// android.backup.BackupDataInput$EntityHeader +static jfieldID s_keyField = 0; +static jfieldID s_dataSizeField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataReader(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int r) +{ + delete (BackupDataReader*)r; +} + +static jint +readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) +{ + int err; + bool done; + BackupDataReader* reader = (BackupDataReader*)r; + + int type = 0; + + err = reader->ReadNextHeader(&done, &type); + if (done) { + return 1; + } + + if (err != 0) { + return err < 0 ? err : -1; + } + + switch (type) { + case BACKUP_HEADER_ENTITY_V1: + { + String8 key; + size_t dataSize; + err = reader->ReadEntityHeader(&key, &dataSize); + if (err != 0) { + return err < 0 ? err : -1; + } + // TODO: Set the fields in the entity object + jstring keyStr = env->NewStringUTF(key.string()); + env->SetObjectField(entity, s_keyField, keyStr); + env->SetIntField(entity, s_dataSizeField, dataSize); + return 0; + } + default: + LOGD("Unknown header type: 0x%08x\n", type); + return -1; + } + + // done + return 1; +} + +static jint +readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int offset, int size) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + if (env->GetArrayLength(data) < (size+offset)) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -2; + } + + err = reader->ReadEntityData(dataBytes+offset, size); + + env->ReleaseByteArrayElements(data, dataBytes, 0); + + return err; +} + +static jint +skipEntityData_native(JNIEnv* env, jobject clazz, int r) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + err = reader->SkipEntityData(); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I", + (void*)readNextHeader_native }, + { "readEntityData_native", "(I[BII)I", (void*)readEntityData_native }, + { "skipEntityData_native", "(I)I", (void*)skipEntityData_native }, +}; + +int register_android_backup_BackupDataInput(JNIEnv* env) +{ + //LOGD("register_android_backup_BackupDataInput"); + + 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/BackupDataInput$EntityHeader"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader"); + s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyField == NULL, + "Unable to find key field in android.backup.BackupDataInput.EntityHeader"); + s_dataSizeField = env->GetFieldID(clazz, "dataSize", "I"); + LOG_FATAL_IF(s_dataSizeField == NULL, + "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp new file mode 100644 index 000000000000..d02590e62e69 --- /dev/null +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -0,0 +1,128 @@ +/* + * 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 "FileBackupHelper_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <utils/BackupHelpers.h> + +namespace android +{ + +static jfieldID s_descriptorField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataWriter(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int w) +{ + delete (BackupDataWriter*)w; +} + +static jint +writeEntityHeader_native(JNIEnv* env, jobject clazz, int w, jstring key, int dataSize) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyUTF = env->GetStringUTFChars(key, NULL); + if (keyUTF == NULL) { + return -1; + } + + err = writer->WriteEntityHeader(String8(keyUTF), dataSize); + + env->ReleaseStringUTFChars(key, keyUTF); + + return err; +} + +static jint +writeEntityData_native(JNIEnv* env, jobject clazz, int w, jbyteArray data, int size) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + if (env->GetArrayLength(data) > size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -1; + } + + err = writer->WriteEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); + + return err; +} + +static void +setKeyPrefix_native(JNIEnv* env, jobject clazz, int w, jstring keyPrefixObj) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyPrefixUTF = env->GetStringUTFChars(keyPrefixObj, NULL); + String8 keyPrefix(keyPrefixUTF ? keyPrefixUTF : ""); + + writer->SetKeyPrefix(keyPrefix); + + env->ReleaseStringUTFChars(keyPrefixObj, keyPrefixUTF); +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "writeEntityHeader_native", "(ILjava/lang/String;I)I", (void*)writeEntityHeader_native }, + { "writeEntityData_native", "(I[BI)I", (void*)writeEntityData_native }, + { "setKeyPrefix_native", "(ILjava/lang/String;)V", (void*)setKeyPrefix_native }, +}; + +int register_android_backup_BackupDataOutput(JNIEnv* env) +{ + //LOGD("register_android_backup_BackupDataOutput"); + + 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"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataOutput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp new file mode 100644 index 000000000000..2e3f0b969127 --- /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_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp deleted file mode 100644 index c6de3a52f69b..000000000000 --- a/core/jni/android_backup_FileBackupHelper.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 "FileBackupHelper_native" -#include <utils/Log.h> - -#include "JNIHelp.h" -#include <android_runtime/AndroidRuntime.h> - -#include <utils/backup_helpers.h> - -namespace android -{ - -static jfieldID s_descriptorField = 0; - -static int -performBackup_native(JNIEnv* env, jobject clazz, jstring basePath, jobject oldState, jobject data, - jobject newState, jobjectArray files) -{ - int err; - - // all parameters have already been checked against null - LOGD("oldState=%p newState=%p data=%p\n", oldState, newState, data); - int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; - int newStateFD = env->GetIntField(newState, s_descriptorField); - int dataFD = env->GetIntField(data, s_descriptorField); - - char const* basePathUTF = env->GetStringUTFChars(basePath, NULL); - LOGD("basePathUTF=\"%s\"\n", basePathUTF); - const int fileCount = env->GetArrayLength(files); - char const** filesUTF = (char const**)malloc(sizeof(char*)*fileCount); - for (int i=0; i<fileCount; i++) { - filesUTF[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(files, i), NULL); - } - - err = back_up_files(oldStateFD, dataFD, newStateFD, basePathUTF, filesUTF, fileCount); - - for (int i=0; i<fileCount; i++) { - env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(files, i), filesUTF[i]); - } - free(filesUTF); - env->ReleaseStringUTFChars(basePath, basePathUTF); - - return err; -} - -static const JNINativeMethod g_methods[] = { - { "performBackup_native", - "(Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" - "Ljava/io/FileDescriptor;[Ljava/lang/String;)I", - (void*)performBackup_native }, -}; - -int register_android_backup_FileBackupHelper(JNIEnv* env) -{ - LOGD("register_android_backup_FileBackupHelper"); - - 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"); - - return AndroidRuntime::registerNativeMethods(env, "android/backup/FileBackupHelper", - g_methods, NELEM(g_methods)); -} - -} diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp new file mode 100644 index 000000000000..8225a36b4164 --- /dev/null +++ b/core/jni/android_backup_FileBackupHelperBase.cpp @@ -0,0 +1,136 @@ +/* + * 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 "FileBackupHelper_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <utils/BackupHelpers.h> + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +static int +ctor(JNIEnv* env, jobject clazz) +{ + return (int)new RestoreHelperBase(); +} + +static void +dtor(JNIEnv* env, jobject clazz, jint ptr) +{ + delete (RestoreHelperBase*)ptr; +} + +static int +performBackup_native(JNIEnv* env, jobject clazz, jobject oldState, int data, + jobject newState, jobjectArray files, jobjectArray keys) +{ + int err; + + // all parameters have already been checked against null + int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; + int newStateFD = env->GetIntField(newState, s_descriptorField); + BackupDataWriter* dataStream = (BackupDataWriter*)data; + + const int fileCount = env->GetArrayLength(files); + char const** filesUTF = (char const**)malloc(sizeof(char*)*fileCount); + for (int i=0; i<fileCount; i++) { + filesUTF[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(files, i), NULL); + } + + const int keyCount = env->GetArrayLength(keys); + char const** keysUTF = (char const**)malloc(sizeof(char*)*keyCount); + for (int i=0; i<keyCount; i++) { + keysUTF[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), NULL); + } + + err = back_up_files(oldStateFD, dataStream, newStateFD, filesUTF, keysUTF, fileCount); + + for (int i=0; i<fileCount; i++) { + env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(files, i), filesUTF[i]); + } + free(filesUTF); + + for (int i=0; i<keyCount; i++) { + env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), keysUTF[i]); + } + free(keysUTF); + + return err; +} + + +static int +writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr) +{ + int err; + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + BackupDataReader* reader = (BackupDataReader*)backupReaderPtr; + char const* filename; + + filename = env->GetStringUTFChars(filenameObj, NULL); + + err = restore->WriteFile(String8(filename), reader); + + env->ReleaseStringUTFChars(filenameObj, filename); + + return err; +} + +static int +writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor) +{ + int err; + + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + + err = restore->WriteSnapshot(fd); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "()I", (void*)ctor }, + { "dtor", "(I)V", (void*)dtor }, + { "performBackup_native", + "(Ljava/io/FileDescriptor;ILjava/io/FileDescriptor;[Ljava/lang/String;[Ljava/lang/String;)I", + (void*)performBackup_native }, + { "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native }, + { "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native }, +}; + +int register_android_backup_FileBackupHelperBase(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"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/FileBackupHelperBase", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp index 136c9a328d55..73b8efd3e3f9 100644 --- a/core/jni/android_bluetooth_Database.cpp +++ b/core/jni/android_bluetooth_Database.cpp @@ -53,6 +53,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGE("Could not get onto the system bus!"); dbus_error_free(&err); } + dbus_connection_set_exit_on_disconnect(conn, FALSE); } #endif } diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp index 59f63a83cac5..7d6e24fa2567 100644 --- a/core/jni/android_emoji_EmojiFactory.cpp +++ b/core/jni/android_emoji_EmojiFactory.cpp @@ -1,7 +1,7 @@ #include "SkTypes.h" #include "SkImageDecoder.h" -#define LOG_TAG "DoCoMoEmojiFactory_jni" +#define LOG_TAG "EmojiFactory_jni" #include <utils/Log.h> #include <utils/String8.h> @@ -13,15 +13,11 @@ namespace android { -// Note: This class is originally developed so that libandroid_runtime does -// not have to depend on libemoji which is optional library. However, we -// cannot use this class, since current (2009-02-16) bionic libc does not allow -// dlopen()-ing inside dlopen(), while not only this class but also libemoji -// uses dlopen(). class EmojiFactoryCaller { public: - EmojiFactoryCaller(); + EmojiFactoryCaller() {} virtual ~EmojiFactoryCaller(); + bool Init(); EmojiFactory *TryCallGetImplementation(const char* name); EmojiFactory *TryCallGetAvailableImplementation(); private: @@ -30,35 +26,45 @@ class EmojiFactoryCaller { EmojiFactory *(*m_get_available_implementation)(); }; -EmojiFactoryCaller::EmojiFactoryCaller() { +bool EmojiFactoryCaller::Init() { + const char* error_msg; m_handle = dlopen("libemoji.so", RTLD_LAZY | RTLD_LOCAL); - const char* error_str = dlerror(); - if (error_str) { - LOGI("Failed to load libemoji.so: %s", error_str); - return; + + if (m_handle == NULL) { + error_msg = "Failed to load libemoji.so"; + goto FAIL; } m_get_implementation = reinterpret_cast<EmojiFactory *(*)(const char*)>( dlsym(m_handle, "GetImplementation")); - error_str = dlerror(); - if (error_str) { - LOGE("Failed to get symbol of GetImplementation: %s", error_str); - dlclose(m_handle); - m_handle = NULL; - return; + if (m_get_implementation == NULL) { + error_msg = "Failed to get symbol of GetImplementation"; + goto FAIL; } m_get_available_implementation = reinterpret_cast<EmojiFactory *(*)()>( dlsym(m_handle,"GetAvailableImplementation")); - error_str = dlerror(); - if (error_str) { - LOGE("Failed to get symbol of GetAvailableImplementation: %s", error_str); + if (m_get_available_implementation == NULL) { + error_msg = "Failed to get symbol of GetAvailableImplementation"; + goto FAIL; + } + + return true; + +FAIL: + const char* error_str = dlerror(); + if (error_str == NULL) { + error_str = "unknown reason"; + } + + LOGE("%s: %s", error_msg, error_str); + if (m_handle != NULL) { dlclose(m_handle); m_handle = NULL; - return; } + return false; } EmojiFactoryCaller::~EmojiFactoryCaller() { @@ -82,10 +88,9 @@ EmojiFactory *EmojiFactoryCaller::TryCallGetAvailableImplementation() { return m_get_available_implementation(); } -// Note: bionic libc's dlopen() does not allow recursive dlopen(). So currently -// we cannot use EmojiFactoryCaller here. -// static EmojiFactoryCaller* gCaller; -// static pthread_once_t g_once = PTHREAD_ONCE_INIT; +static EmojiFactoryCaller* gCaller; +static pthread_once_t g_once = PTHREAD_ONCE_INIT; +static bool lib_emoji_factory_is_ready; static jclass gString_class; @@ -95,9 +100,10 @@ static jmethodID gBitmap_constructorMethodID; static jclass gEmojiFactory_class; static jmethodID gEmojiFactory_constructorMethodID; -// static void InitializeCaller() { -// gCaller = new EmojiFactoryCaller(); -// } +static void InitializeCaller() { + gCaller = new EmojiFactoryCaller(); + lib_emoji_factory_is_ready = gCaller->Init(); +} static jobject create_java_EmojiFactory( JNIEnv* env, EmojiFactory* factory, jstring name) { @@ -116,19 +122,23 @@ static jobject create_java_EmojiFactory( static jobject android_emoji_EmojiFactory_newInstance( JNIEnv* env, jobject clazz, jstring name) { - // pthread_once(&g_once, InitializeCaller); - if (NULL == name) { return NULL; } + pthread_once(&g_once, InitializeCaller); + if (!lib_emoji_factory_is_ready) { + return NULL; + } const jchar* jchars = env->GetStringChars(name, NULL); jsize len = env->GetStringLength(name); String8 str(String16(jchars, len)); - // EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string()); - EmojiFactory *factory = EmojiFactory::GetImplementation(str.string()); - + EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string()); + // EmojiFactory *factory = EmojiFactory::GetImplementation(str.string()); + if (NULL == factory) { + return NULL; + } env->ReleaseStringChars(name, jchars); return create_java_EmojiFactory(env, factory, name); @@ -136,10 +146,13 @@ static jobject android_emoji_EmojiFactory_newInstance( static jobject android_emoji_EmojiFactory_newAvailableInstance( JNIEnv* env, jobject clazz) { - // pthread_once(&g_once, InitializeCaller); + pthread_once(&g_once, InitializeCaller); + if (!lib_emoji_factory_is_ready) { + return NULL; + } - // EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation(); - EmojiFactory *factory = EmojiFactory::GetAvailableImplementation(); + EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation(); + // EmojiFactory *factory = EmojiFactory::GetAvailableImplementation(); if (NULL == factory) { return NULL; } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index c10799316b81..77a8a7286c63 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -38,12 +38,6 @@ enum CallbackMessageID { kErrorCallback = 5 }; -enum CameraError { - kCameraErrorUnknown = 1, - kCameraErrorMediaServer = 100 -}; - - struct fields_t { jfieldID context; jfieldID surface; @@ -53,19 +47,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 +84,108 @@ 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(); +} + +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; + } + 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(); - if (env == NULL) { - LOGE("err_callback on dead VM"); + if (mCameraJObjectWeak == NULL) { + LOGW("callback on dead camera object"); return; } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kErrorCallback, error, 0, NULL); + + // 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 @@ -115,33 +201,24 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // make sure camera hardware is alive if (camera->getStatus() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "Camera initialization failed"); + jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed"); return; } jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { - LOGE("Can't find android/hardware/Camera"); - // XXX no idea what to throw here, can this even happen? - jniThrowException(env, "java/lang/Exception", NULL); + jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera"); return; } // 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 @@ -150,11 +227,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); @@ -162,21 +239,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); } } @@ -186,54 +260,15 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + sp<Surface> surface = NULL; + if (jSurface != NULL) { + surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + } if (camera->setPreviewDisplay(surface) != NO_ERROR) { jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); } } -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"); @@ -241,7 +276,7 @@ static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) if (camera == 0) return; if (camera->startPreview() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "startPreview failed"); + jniThrowException(env, "java/lang/RuntimeException", "startPreview failed"); return; } } @@ -269,7 +304,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; @@ -279,130 +314,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/io/IOException", "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; + jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed"); } - 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/io/IOException", "takePicture failed"); + jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); return; } - - return; } static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) @@ -418,7 +355,7 @@ static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jst env->ReleaseStringCritical(params, str); } if (camera->setParameters(params8) != NO_ERROR) { - jniThrowException(env, "java/lang/IllegalArgumentException", "setParameters failed"); + jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); return; } } diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 75aa458035bd..3e27978ec02e 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -14,9 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "Sensors" +#define LOG_TAG "SensorManager" + +#define LOG_NDEBUG 0 +#include "utils/Log.h" #include <hardware/sensors.h> +#include <cutils/native_handle.h> #include "jni.h" #include "JNIHelp.h" @@ -106,12 +110,33 @@ sensors_data_uninit(JNIEnv *env, jclass clazz) } static jint -sensors_data_open(JNIEnv *env, jclass clazz, jobject fdo) +sensors_data_open(JNIEnv *env, jclass clazz, jobjectArray fdArray, jintArray intArray) { jclass FileDescriptor = env->FindClass("java/io/FileDescriptor"); - jfieldID offset = env->GetFieldID(FileDescriptor, "descriptor", "I"); - int fd = env->GetIntField(fdo, offset); - return sSensorDevice->data_open(sSensorDevice, fd); // doesn't take ownership of fd + jfieldID fieldOffset = env->GetFieldID(FileDescriptor, "descriptor", "I"); + int numFds = (fdArray ? env->GetArrayLength(fdArray) : 0); + int numInts = (intArray ? env->GetArrayLength(intArray) : 0); + native_handle_t* handle = native_handle_create(numFds, numInts); + int offset = 0; + + for (int i = 0; i < numFds; i++) { + jobject fdo = env->GetObjectArrayElement(fdArray, i); + if (fdo) { + handle->data[offset++] = env->GetIntField(fdo, fieldOffset); + } else { + handle->data[offset++] = -1; + } + } + if (numInts > 0) { + jint* ints = env->GetIntArrayElements(intArray, 0); + for (int i = 0; i < numInts; i++) { + handle->data[offset++] = ints[i]; + } + env->ReleaseIntArrayElements(intArray, ints, 0); + } + + // doesn't take ownership of the native handle + return sSensorDevice->data_open(sSensorDevice, handle); } static jint @@ -157,7 +182,7 @@ static JNINativeMethod gMethods[] = { (void*)sensors_module_get_next_sensor }, {"sensors_data_init", "()I", (void*)sensors_data_init }, {"sensors_data_uninit", "()I", (void*)sensors_data_uninit }, - {"sensors_data_open", "(Ljava/io/FileDescriptor;)I", (void*)sensors_data_open }, + {"sensors_data_open", "([Ljava/io/FileDescriptor;[I)I", (void*)sensors_data_open }, {"sensors_data_close", "()I", (void*)sensors_data_close }, {"sensors_data_poll", "([F[I[J)I", (void*)sensors_data_poll }, }; diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp index 004b0e3ea07e..bf0bd65eddbc 100644..100755 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -176,7 +176,7 @@ static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject { int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); if (result) { - return result; + return false; } return (sGpsInterface->start() == 0); @@ -270,6 +270,12 @@ static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobjec sGpsInterface->inject_time(time, timeReference, uncertainty); } +static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, + jdouble latitude, jdouble longitude, jfloat accuracy) +{ + sGpsInterface->inject_location(latitude, longitude, accuracy); +} + static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) { if (!sGpsXtraInterface) { @@ -330,13 +336,15 @@ static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* e } static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, - jint type, jint addr, jint port) + jint type, jstring hostname, jint port) { if (!sAGpsInterface) { sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); } if (sAGpsInterface) { - sAGpsInterface->set_server(type, addr, port); + const char *c_hostname = env->GetStringUTFChars(hostname, NULL); + sAGpsInterface->set_server(type, c_hostname, port); + env->ReleaseStringUTFChars(hostname, c_hostname); } } @@ -353,12 +361,13 @@ static JNINativeMethod sMethods[] = { {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, - {"native_set_agps_server", "(III)V", (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, }; int register_android_location_GpsLocationProvider(JNIEnv* env) diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 288433af6a0d..e71e3481862e 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -45,8 +45,6 @@ struct fields_t { jmethodID postNativeEventInJava; //... event post callback method int PCM16; //... format constants int PCM8; //... format constants - int SOURCE_DEFAULT; //... record source constants - int SOURCE_MIC; //... record source constants jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data }; @@ -66,7 +64,7 @@ struct audiorecord_callback_cookie { #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 -#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -19 +#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateRecorderErrorCode(int code) { @@ -154,17 +152,16 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, int frameSize = nbChannels * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; - // compare the source against the Java constants - AudioRecord::stream_type arSource; - if (source == javaAudioRecordFields.SOURCE_DEFAULT) { - arSource = AudioRecord::DEFAULT_INPUT; - } else if (source == javaAudioRecordFields.SOURCE_MIC) { - arSource = AudioRecord::MIC_INPUT; - } else { + // convert and check input source value + // input_source values defined in AudioRecord.h are equal to + // JAVA MediaRecord.AudioSource values minus 1. + AudioRecord::input_source arSource = (AudioRecord::input_source)(source - 1); + if (arSource < AudioRecord::DEFAULT_INPUT || + arSource >= AudioRecord::NUM_INPUT_SOURCES) { LOGE("Error creating AudioRecord: unknown source."); - return AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE; + return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; } - + audiorecord_callback_cookie *lpCallbackData = NULL; AudioRecord* lpRecorder = NULL; @@ -511,8 +508,6 @@ static JNINativeMethod gMethods[] = { #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" #define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" #define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" -#define JAVA_CONST_SOURCEDEFAULT_NAME "SOURCE_DEFAULT" -#define JAVA_CONST_SOURCEMIC_NAME "SOURCE_MIC" #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" @@ -583,17 +578,6 @@ int register_android_media_AudioRecord(JNIEnv *env) return -1; } - // Get the recording source constants from the AudioRecord class - if ( !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, - kClassPathName, - JAVA_CONST_SOURCEDEFAULT_NAME, &(javaAudioRecordFields.SOURCE_DEFAULT)) - || !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, - kClassPathName, - JAVA_CONST_SOURCEMIC_NAME, &(javaAudioRecordFields.SOURCE_MIC)) ) { - // error log performed in getIntConstantFromClass() - return -1; - } - return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 42ada5450744..e7d4694a2aaf 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -539,16 +539,17 @@ static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobjec // ---------------------------------------------------------------------------- -static void android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, +static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, jint sampleRateInHz) { AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( thiz, javaAudioTrackFields.nativeTrackInJavaObj); if (lpTrack) { - lpTrack->setSampleRate(sampleRateInHz); + return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz)); } else { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for setSampleRate()"); + return AUDIOTRACK_ERROR; } } @@ -788,7 +789,7 @@ static JNINativeMethod gMethods[] = { {"native_get_native_frame_count", "()I", (void *)android_media_AudioTrack_get_native_frame_count}, {"native_set_playback_rate", - "(I)V", (void *)android_media_AudioTrack_set_playback_rate}, + "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate}, {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 25670df9d373..9f93e2f042d1 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -317,8 +317,13 @@ static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) } // reply comes back in the form "<SSID> rssi XX" where XX is the // number we're interested in. if we're associating, it returns "OK". + // beware - <SSID> can contain spaces. if (strcmp(reply, "OK") != 0) { - sscanf(reply, "%*s %*s %d", &rssi); + char* lastSpace = strrchr(reply, ' '); + // lastSpace should be preceded by "rssi" and followed by the value + if (lastSpace && !strncmp(lastSpace - 4, "rssi", 4)) { + sscanf(lastSpace + 1, "%d", &rssi); + } } return (jint)rssi; } diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp index 482d8eb1b88b..2685d7595e32 100644 --- a/core/jni/android_opengl_GLES10.cpp +++ b/core/jni/android_opengl_GLES10.cpp @@ -133,6 +133,19 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + char* buf = (char*) _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf += position << elementSizeShift; + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + return (void*) buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; @@ -305,9 +318,8 @@ android_glColorPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -2779,9 +2791,8 @@ android_glNormalPointerBounds__IILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3034,9 +3045,8 @@ android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3392,9 +3402,8 @@ android_glVertexPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index edf7dc45991b..8643393f7e8a 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -26,7 +26,7 @@ namespace android { -static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) +static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) { const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); @@ -37,37 +37,52 @@ static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, if (name) env->ReleaseStringUTFChars(name, namestr); - if (result < 0) + if (result < 0) { jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); - return result; + return NULL; + } + + return jniCreateFileDescriptor(env, result); } -static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jint fd, jint length) +static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor, + jint length, jint prot) { - jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0); if (!result) jniThrowException(env, "java/io/IOException", "mmap failed"); return result; } -static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jint fd) +static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jint addr, jint length) +{ + int result = munmap((void *)addr, length); + if (result < 0) + jniThrowException(env, "java/io/IOException", "munmap failed"); +} + +static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor) { - close(fd); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + close(fd); + } } static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } - jbyte* bytes = env->GetByteArrayElements(buffer, 0); - memcpy(bytes + destOffset, (const char *)address + srcOffset, count); - env->ReleaseByteArrayElements(buffer, bytes, 0); + env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); @@ -76,18 +91,17 @@ static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, } static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } - jbyte* bytes = env->GetByteArrayElements(buffer, 0); - memcpy((char *)address + destOffset, bytes + srcOffset, count); - env->ReleaseByteArrayElements(buffer, bytes, 0); + env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); @@ -95,21 +109,45 @@ static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, return count; } -static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jint fd, jboolean pin) +static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0)); if (result < 0) { jniThrowException(env, "java/io/IOException", NULL); } } +static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz, + jobject fileDescriptor) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. + // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel + // should return ENOTTY for all other valid file descriptors + int result = ashmem_get_size_region(fd); + if (result < 0) { + if (errno == ENOTTY) { + // ENOTTY means that the ioctl does not apply to this object, + // i.e., it is not an ashmem region. + return JNI_FALSE; + } + // Some other error, throw exception + jniThrowIOException(env, errno); + return JNI_FALSE; + } + return JNI_TRUE; +} + static const JNINativeMethod methods[] = { - {"native_open", "(Ljava/lang/String;I)I", (void*)android_os_MemoryFile_open}, - {"native_mmap", "(II)I", (void*)android_os_MemoryFile_mmap}, - {"native_close", "(I)V", (void*)android_os_MemoryFile_close}, - {"native_read", "(II[BIIIZ)I", (void*)android_os_MemoryFile_read}, - {"native_write", "(II[BIIIZ)V", (void*)android_os_MemoryFile_write}, - {"native_pin", "(IZ)V", (void*)android_os_MemoryFile_pin}, + {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open}, + {"native_mmap", "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap}, + {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap}, + {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close}, + {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read}, + {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write}, + {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, + {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z", + (void*)android_os_MemoryFile_is_ashmem_region} }; static const char* const kClassPathName = "android/os/MemoryFile"; diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index fe94642e258f..91a8e8e60353 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -84,6 +84,7 @@ static bool initNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); #endif /*HAVE_BLUETOOTH*/ return true; } diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index b6e979811e26..58ae4f60a78d 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -109,6 +109,7 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; #endif /*HAVE_BLUETOOTH*/ diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 7c5da5bdb564..ad24136ac28f 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -132,6 +132,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGE("%s: Could not get onto the system bus!", __FUNCTION__); dbus_error_free(&err); } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); } #endif } @@ -161,6 +162,19 @@ static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL }; +static unsigned int unix_events_to_dbus_flags(short events) { + return (events & DBUS_WATCH_READABLE ? POLLIN : 0) | + (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) | + (events & DBUS_WATCH_ERROR ? POLLERR : 0) | + (events & DBUS_WATCH_HANGUP ? POLLHUP : 0); +} + +static short dbus_flags_to_unix_events(unsigned int flags) { + return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) | + (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) | + (flags & POLLERR ? DBUS_WATCH_ERROR : 0) | + (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0); +} static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); @@ -384,8 +398,7 @@ static void handleWatchAdd(native_data_t *nat) { read(nat->controlFdR, &newFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); read(nat->controlFdR, &watch, sizeof(DBusWatch *)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; y<nat->pollMemberCount; y++) { if ((nat->pollData[y].fd == newFD) && @@ -429,8 +442,7 @@ static void handleWatchRemove(native_data_t *nat) { read(nat->controlFdR, &removeFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; y < nat->pollMemberCount; y++) { if ((nat->pollData[y].fd == removeFD) && @@ -494,13 +506,12 @@ static void *eventLoopMain(void *ptr) { } } } else { - int event = nat->pollData[i].revents; - int flags = (event & POLLIN ? DBUS_WATCH_READABLE : 0) | - (event & POLLOUT ? DBUS_WATCH_WRITABLE : 0); - dbus_watch_handle(nat->watchData[i], event); - nat->pollData[i].revents = 0; - // can only do one - it may have caused a 'remove' - break; + short events = nat->pollData[i].revents; + unsigned int flags = unix_events_to_dbus_flags(events); + dbus_watch_handle(nat->watchData[i], flags); + nat->pollData[i].revents = 0; + // can only do one - it may have caused a 'remove' + break; } } while (dbus_connection_dispatch(nat->conn) == diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index 923e1aa05cc8..98f4e035c0c9 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> @@ -44,6 +44,7 @@ static jfieldID g_timezoneField = 0; static jfieldID g_shortMonthsField = 0; static jfieldID g_longMonthsField = 0; +static jfieldID g_longStandaloneMonthsField = 0; static jfieldID g_shortWeekdaysField = 0; static jfieldID g_longWeekdaysField = 0; static jfieldID g_timeOnlyFormatField = 0; @@ -193,6 +194,7 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, static jobject js_locale_previous = NULL; static struct strftime_locale locale; static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7]; + static jstring js_standalone_month[12]; static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt; Time t; @@ -206,8 +208,10 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, for (int i = 0; i < 12; i++) { env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]); env->ReleaseStringUTFChars(js_month[i], locale.month[i]); + env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]); env->DeleteGlobalRef(js_mon[i]); env->DeleteGlobalRef(js_month[i]); + env->DeleteGlobalRef(js_standalone_month[i]); } for (int i = 0; i < 7; i++) { @@ -245,6 +249,12 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); } + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField); + for (int i = 0; i < 12; i++) { + js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); + locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL); + } + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); for (int i = 0; i < 7; i++) { js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); @@ -639,6 +649,7 @@ int register_android_text_format_Time(JNIEnv* env) g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); + g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;"); g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d147bcc883c2..2d90ba49aecc 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 d760feb3a3aa..aee0ed7f42f8 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -50,8 +50,6 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif -#define ENABLE_CGROUP_ERR_LOGGING 0 - /* * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h * and Process.java @@ -198,50 +196,82 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) static int add_pid_to_cgroup(int pid, int grp) { - FILE *fp; + int fd; char path[255]; - int rc; + char text[64]; - sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup_names[grp] ? cgroup_names[grp] : "")); + sprintf(path, "/dev/cpuctl/%s/tasks", + (cgroup_names[grp] ? cgroup_names[grp] : "")); - if (!(fp = fopen(path, "w"))) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to open %s (%s)\n", path, strerror(errno)); -#endif - return -errno; + if ((fd = open(path, O_WRONLY)) < 0) + return -1; + + sprintf(text, "%d", pid); + if (write(fd, text, strlen(text)) < 0) { + close(fd); + return -1; } - rc = fprintf(fp, "%d", pid); - fclose(fp); + close(fd); + return 0; +} - if (rc < 0) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to move pid %d to cgroup %s (%s)\n", pid, - (cgroup_names[grp] ? cgroup_names[grp] : "<default>"), - strerror(errno)); -#endif +void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) +{ + if (grp > ANDROID_TGROUP_MAX || grp < 0) { + signalExceptionForGroupError(env, clazz, EINVAL); + return; } - return (rc < 0) ? errno : 0; + if (add_pid_to_cgroup(pid, grp)) { + // If the thread exited on us, don't generate an exception + if (errno != ESRCH && errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); + } } -void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) +void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { + DIR *d; + FILE *fp; + char proc_path[255]; + struct dirent *de; + if (grp > ANDROID_TGROUP_MAX || grp < 0) { signalExceptionForGroupError(env, clazz, EINVAL); return; } - if (add_pid_to_cgroup(pid, grp)) - signalExceptionForGroupError(env, clazz, errno); + sprintf(proc_path, "/proc/%d/task", pid); + if (!(d = opendir(proc_path))) { + // If the process exited on us, don't generate an exception + if (errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); + return; + } + + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + + if (add_pid_to_cgroup(atoi(de->d_name), grp)) { + // If the thread exited on us, ignore it and keep going + if (errno != ESRCH && errno != ENOENT) { + signalExceptionForGroupError(env, clazz, errno); + closedir(d); + return; + } + } + } + closedir(d); } 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); } @@ -466,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'; @@ -820,6 +850,7 @@ static const JNINativeMethod methods[] = { {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, + {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, {"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj}, {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, {"setUid", "(I)I", (void*)android_os_Process_setUid}, diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 11822e014d9c..89b1f96879e5 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -45,9 +45,11 @@ static jclass OOMEClass; static jclass UOEClass; static jclass IAEClass; static jclass AIOOBEClass; +static jclass G11ImplClass; static jmethodID getBasePointerID; static jmethodID getBaseArrayID; static jmethodID getBaseArrayOffsetID; +static jmethodID allowIndirectBuffersID; static jfieldID positionID; static jfieldID limitID; static jfieldID elementSizeShiftID; @@ -63,13 +65,17 @@ nativeClassInitBuffer(JNIEnv *_env) jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl"); + G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal); + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); - + allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal, + "allowIndirectBuffers", "(Ljava/lang/String;)Z"); positionID = _env->GetFieldID(bufferClass, "position", "I"); limitID = _env->GetFieldID(bufferClass, "limit", "I"); elementSizeShiftID = @@ -119,6 +125,9 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + if (*array == NULL) { + return (void*) NULL; + } offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); @@ -133,6 +142,45 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +extern "C" { +extern char* __progname; +} + +static bool +allowIndirectBuffers(JNIEnv *_env) { + static jint sIndirectBufferCompatability; + if (sIndirectBufferCompatability == 0) { + jobject appName = _env->NewStringUTF(::__progname); + sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1; + } + return sIndirectBufferCompatability == 2; +} + +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + if (!buffer) { + return NULL; + } + void* buf = _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf = ((char*) buf) + (position << elementSizeShift); + } else { + if (allowIndirectBuffers(_env)) { + jarray array = 0; + jint remaining; + buf = getPointer(_env, buffer, &array, &remaining); + if (array) { + releasePointer(_env, array, buf, 0); + } + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + } + return buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; @@ -305,9 +353,8 @@ android_glColorPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -2779,9 +2826,8 @@ android_glNormalPointerBounds__IILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3034,9 +3080,8 @@ android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3392,9 +3437,8 @@ android_glVertexPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bff6b9dfda5e..599360f06244 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -175,6 +175,22 @@ android:label="@string/permlab_writeDictionary" android:description="@string/permdesc_writeDictionary" /> + <!-- Allows an application to read (but not write) the user's + browsing history and bookmarks. --> + <permission android:name="android.permission.READ_HISTORY_BOOKMARKS" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:label="@string/permlab_readHistoryBookmarks" + android:description="@string/permdesc_readHistoryBookmarks" + android:protectionLevel="dangerous" /> + + <!-- Allows an application to write (but not read) the user's + browsing history and bookmarks. --> + <permission android:name="android.permission.WRITE_HISTORY_BOOKMARKS" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:label="@string/permlab_writeHistoryBookmarks" + android:description="@string/permdesc_writeHistoryBookmarks" + android:protectionLevel="dangerous" /> + <!-- ======================================= --> <!-- Permissions for accessing location info --> <!-- ======================================= --> @@ -220,12 +236,6 @@ android:label="@string/permlab_installLocationProvider" android:description="@string/permdesc_installLocationProvider" /> - <!-- Allows an application to install a location collector into the Location Manager --> - <permission android:name="android.permission.INSTALL_LOCATION_COLLECTOR" - android:protectionLevel="signatureOrSystem" - android:label="@string/permlab_installLocationCollector" - android:description="@string/permdesc_installLocationCollector" /> - <!-- ======================================= --> <!-- Permissions for accessing networks --> <!-- ======================================= --> @@ -388,12 +398,12 @@ android:label="@string/permgrouplab_storage" android:description="@string/permgroupdesc_storage" /> - <!-- Allows an application to write to the SD card --> - <permission android:name="android.permission.SDCARD_WRITE" + <!-- Allows an application to write to external storage --> + <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.STORAGE" android:label="@string/permlab_sdcardWrite" android:description="@string/permdesc_sdcardWrite" - android:protectionLevel="normal" /> + android:protectionLevel="dangerous" /> <!-- ============================================ --> <!-- Permissions for low-level system interaction --> @@ -650,6 +660,13 @@ android:description="@string/permdesc_changeWifiState" android:label="@string/permlab_changeWifiState" /> + <!-- Allows applications to enter Wi-Fi Multicast mode --> + <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="dangerous" + android:description="@string/permdesc_changeWifiMulticastState" + android:label="@string/permlab_changeWifiMulticastState" /> + <!-- Allows applications to discover and pair bluetooth devices --> <permission android:name="android.permission.BLUETOOTH_ADMIN" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" @@ -737,13 +754,6 @@ android:description="@string/permdesc_statusBar" android:protectionLevel="signatureOrSystem" /> - <!-- Allows an application to force any currently running process to be - in the foreground. --> - <permission android:name="android.permission.SET_PROCESS_FOREGROUND" - android:label="@string/permlab_setProcessForeground" - android:description="@string/permdesc_setProcessForeground" - android:protectionLevel="signature" /> - <!-- Allows an application to force a BACK operation on whatever is the top activity. --> <permission android:name="android.permission.FORCE_BACK" @@ -751,19 +761,6 @@ android:description="@string/permdesc_forceBack" android:protectionLevel="signature" /> - <!-- Allows an application to publish system-level services. Such services - can only be published from processes that never go away, so this is - not something that any normal application can do. --> - <permission android:name="android.permission.ADD_SYSTEM_SERVICE" - android:label="@string/permlab_addSystemService" - android:description="@string/permdesc_addSystemService" - android:protectionLevel="signature" /> - - <permission android:name="android.permission.FOTA_UPDATE" - android:label="@string/permlab_fotaUpdate" - android:description="@string/permdesc_fotaUpdate" - android:protectionLevel="signature" /> - <!-- Allows an application to update device statistics. Not for use by third party apps. --> <permission android:name="android.permission.UPDATE_DEVICE_STATS" @@ -803,14 +800,22 @@ android:description="@string/permdesc_runSetActivityWatcher" android:protectionLevel="signature" /> - <!-- Allows an application to watch and control how activities are - started globally in the system. Only for is in debugging - (usually the monkey command). --> + <!-- Allows an application to call the activity manager shutdown() API + to put the higher-level system there into a shutdown state. --> <permission android:name="android.permission.SHUTDOWN" android:label="@string/permlab_shutdown" android:description="@string/permdesc_shutdown" android:protectionLevel="signature" /> + <!-- Allows an application to tell the activity manager to temporarily + stop application switches, putting it into a special mode that + prevents applications from immediately switching away from some + critical UI such as the home screen. --> + <permission android:name="android.permission.STOP_APP_SWITCHES" + android:label="@string/permlab_stopAppSwitches" + android:description="@string/permdesc_stopAppSwitches" + android:protectionLevel="signature" /> + <!-- Allows an application to retrieve the current state of keys and switches. This is only for use by the system.--> <permission android:name="android.permission.READ_INPUT_STATE" @@ -963,6 +968,13 @@ android:description="@string/permdesc_batteryStats" android:protectionLevel="normal" /> + <!-- Allows an application to control the backup and restore process + @hide pending API council --> + <permission android:name="android.permission.BACKUP" + android:label="@string/permlab_backup" + android:description="@string/permdesc_backup" + android:protectionLevel="signatureOrSystem" /> + <!-- Allows an application to tell the AppWidget service which application can access AppWidget's data. The normal user flow is that a user picks an AppWidget to go into a particular host, thereby giving that @@ -973,7 +985,7 @@ android:permissionGroup="android.permission-group.PERSONAL_INFO" android:label="@string/permlab_bindGadget" android:description="@string/permdesc_bindGadget" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows applications to change the background data setting @hide pending API council --> @@ -988,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/anim/slide_in_up.xml b/core/res/res/anim/slide_in_up.xml new file mode 100644 index 000000000000..bf471c38361e --- /dev/null +++ b/core/res/res/anim/slide_in_up.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<translate + xmlns:android="http://schemas.android.com/apk/res/android" + + android:fromYDelta="100%p" + android:toYDelta="0" + + android:duration="@android:integer/config_longAnimTime" /> diff --git a/core/res/res/anim/slide_out_down.xml b/core/res/res/anim/slide_out_down.xml new file mode 100644 index 000000000000..9b8d5b7919a2 --- /dev/null +++ b/core/res/res/anim/slide_out_down.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<translate + xmlns:android="http://schemas.android.com/apk/res/android" + + android:fromYDelta="0" + android:toYDelta="100%p" + + android:duration="@android:integer/config_longAnimTime" /> diff --git a/core/res/res/drawable/call_contact.png b/core/res/res/drawable/call_contact.png Binary files differnew file mode 100644 index 000000000000..1abeb5da3bcb --- /dev/null +++ b/core/res/res/drawable/call_contact.png diff --git a/core/res/res/drawable/create_contact.png b/core/res/res/drawable/create_contact.png Binary files differnew file mode 100644 index 000000000000..5c5718bafc00 --- /dev/null +++ b/core/res/res/drawable/create_contact.png diff --git a/core/res/res/drawable/progress.xml b/core/res/res/drawable/progress.xml deleted file mode 100644 index d2705209be75..000000000000 --- a/core/res/res/drawable/progress.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/progress.xml -** -** Copyright 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. -*/ ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@android:drawable/progress_circular_background" /> - <item> - <shape android:shape="ring" - android:innerRadiusRatio="3.4" - android:thicknessRatio="6.0"> - <gradient - android:useLevel="true" - android:type="sweep" - android:startColor="#ff000000" - android:endColor="#ffffffff" /> - </shape> - </item> - <item> - <rotate - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360" - android:drawable="@android:drawable/progress_particle" /> - </item> -</layer-list> diff --git a/core/res/res/drawable/progress_circular_background.png b/core/res/res/drawable/progress_circular_background.png Binary files differdeleted file mode 100644 index 7c637fd602d9..000000000000 --- a/core/res/res/drawable/progress_circular_background.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png Binary files differdeleted file mode 100644 index 6b8ba9b43906..000000000000 --- a/core/res/res/drawable/progress_circular_background_small.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png Binary files differdeleted file mode 100644 index 125a264ca7d0..000000000000 --- a/core/res/res/drawable/progress_circular_indeterminate.png +++ /dev/null diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 466910420e0f..4f016bcc2e83 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -1,45 +1,25 @@ <?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. +<!-- +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <shape - android:shape="ring" - android:innerRadiusRatio="3" - android:thicknessRatio="8" - android:useLevel="false"> - - <size - android:width="76dip" - android:height="76dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> - +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_76" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/java/android/app/IIntentSender.aidl b/core/res/res/drawable/progress_large_white.xml index 53e135ab1bb0..c690ed4e0e9a 100644 --- a/core/java/android/app/IIntentSender.aidl +++ b/core/res/res/drawable/progress_large_white.xml @@ -1,6 +1,8 @@ -/* //device/java/android/android/app/IActivityPendingResult.aidl +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -14,14 +16,10 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ - -package android.app; - -import android.app.IIntentReceiver; -import android.content.Intent; - -/** @hide */ -interface IIntentSender { - int send(int code, in Intent intent, String resolvedType, - IIntentReceiver finishedReceiver); -} +--> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_76" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index 92aebb51a512..eb1bd50d17d7 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -1,43 +1,25 @@ <?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. +<!-- +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ --> - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <shape - android:shape="ring" - android:innerRadiusRatio="3" - android:thicknessRatio="8" - android:useLevel="false"> - - <size - android:width="48dip" - android:height="48dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_48" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_medium_white.xml index 1bf715e51269..b4f9b318a902 100644 --- a/core/res/res/drawable/progress_indeterminate.xml +++ b/core/res/res/drawable/progress_medium_white.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/drawable/progress.xml +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,16 +17,9 @@ ** limitations under the License. */ --> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:drawable="@android:drawable/progress_circular_background" /> - - <item><rotate - android:pivotX="50%" - android:pivotY="50%" - android:fromDegrees="0" - android:toDegrees="360" - android:drawable="@android:drawable/progress_circular_indeterminate" /> - </item> -</layer-list> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_48" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e5b0021d59cf..e0ee5e47d830 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -1,45 +1,25 @@ <?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. +<!-- +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <!-- An extra pixel is added on both ratios for stroke --> - <shape - android:shape="ring" - android:innerRadiusRatio="3.2" - android:thicknessRatio="5.333" - android:useLevel="false"> - - <size - android:width="16dip" - android:height="16dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index cf8e41cb373d..8cfba864b5b2 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -1,45 +1,25 @@ <?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. +<!-- +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <!-- An extra pixel is added on both ratios for stroke --> - <shape - android:shape="ring" - android:innerRadiusRatio="3.2" - android:thicknessRatio="5.333" - android:useLevel="false"> - - <size - android:width="16dip" - android:height="16dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#ff666666" - android:centerColor="#ff666666" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/java/android/app/IIntentReceiver.aidl b/core/res/res/drawable/progress_small_white.xml index 5f5d0eb133e5..8cfba864b5b2 100755..100644 --- a/core/java/android/app/IIntentReceiver.aidl +++ b/core/res/res/drawable/progress_small_white.xml @@ -1,6 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- /* ** -** Copyright 2006, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -14,20 +16,10 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ -package android.app; - -import android.content.Intent; -import android.os.Bundle; - -/** - * System private API for dispatching intent broadcasts. This is given to the - * activity manager as part of registering for an intent broadcasts, and is - * called when it receives intents. - * - * {@hide} - */ -oneway interface IIntentReceiver { - void performReceive(in Intent intent, int resultCode, - String data, in Bundle extras, boolean ordered); -} - +--> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/rate_star_big_half.png b/core/res/res/drawable/rate_star_big_half.png Binary files differindex e73ca799b398..9762292a40a6 100644 --- a/core/res/res/drawable/rate_star_big_half.png +++ b/core/res/res/drawable/rate_star_big_half.png diff --git a/core/res/res/drawable/rate_star_big_off.png b/core/res/res/drawable/rate_star_big_off.png Binary files differindex b4dfa9dd3ae1..6b5039fc73a4 100644 --- a/core/res/res/drawable/rate_star_big_off.png +++ b/core/res/res/drawable/rate_star_big_off.png diff --git a/core/res/res/drawable/rate_star_big_on.png b/core/res/res/drawable/rate_star_big_on.png Binary files differindex 7442c93c411c..a972db27486b 100644 --- a/core/res/res/drawable/rate_star_big_on.png +++ b/core/res/res/drawable/rate_star_big_on.png 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 a6923b7c0136..804260afa97f 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 56b697d97b8e..000000000000 --- a/core/res/res/drawable/search_dropdown_background_apps.9.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml new file mode 100644 index 000000000000..31a77c30cf2a --- /dev/null +++ b/core/res/res/drawable/search_spinner.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_20" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png Binary files differnew file mode 100644 index 000000000000..5ee33cea6fa7 --- /dev/null +++ b/core/res/res/drawable/spinner_black_16.png diff --git a/core/res/res/drawable/spinner_black_20.png b/core/res/res/drawable/spinner_black_20.png Binary files differnew file mode 100755 index 000000000000..e55b60dc6cce --- /dev/null +++ b/core/res/res/drawable/spinner_black_20.png diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png Binary files differnew file mode 100644 index 000000000000..3a681926b537 --- /dev/null +++ b/core/res/res/drawable/spinner_black_48.png diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png Binary files differnew file mode 100644 index 000000000000..ec57460277a6 --- /dev/null +++ b/core/res/res/drawable/spinner_black_76.png diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/spinner_white_16.png Binary files differindex 916010869186..dd2e1fd7da16 100644 --- a/core/res/res/drawable/progress_particle.png +++ b/core/res/res/drawable/spinner_white_16.png diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png Binary files differnew file mode 100644 index 000000000000..d25a33e24534 --- /dev/null +++ b/core/res/res/drawable/spinner_white_48.png diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png Binary files differnew file mode 100644 index 000000000000..f53e8ffdb193 --- /dev/null +++ b/core/res/res/drawable/spinner_white_76.png diff --git a/core/res/res/drawable/stat_ecb_mode.png b/core/res/res/drawable/stat_ecb_mode.png Binary files differnew file mode 100644 index 000000000000..a948770f7dfd --- /dev/null +++ b/core/res/res/drawable/stat_ecb_mode.png diff --git a/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png b/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png Binary files differnew file mode 100755 index 000000000000..11c2eaebb1ab --- /dev/null +++ b/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png diff --git a/core/res/res/drawable/stat_sys_data_dormant_evdo.png b/core/res/res/drawable/stat_sys_data_dormant_evdo.png Binary files differnew file mode 100755 index 000000000000..811fcb56f8ae --- /dev/null +++ b/core/res/res/drawable/stat_sys_data_dormant_evdo.png diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_0.png b/core/res/res/drawable/stat_sys_roaming_cdma_0.png Binary files differnew file mode 100755 index 000000000000..c61cce774ec7 --- /dev/null +++ b/core/res/res/drawable/stat_sys_roaming_cdma_0.png diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml b/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml new file mode 100644 index 000000000000..07dc4465ee32 --- /dev/null +++ b/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/res/drawable/stat_sys_battery.xml +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="false"> + <item android:drawable="@drawable/stat_sys_roaming_cdma_flash_anim0" android:duration="800" /> + <item android:drawable="@drawable/stat_sys_roaming_cdma_flash_anim1" android:duration="1200" /> +</animation-list> diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png Binary files differnew file mode 100755 index 000000000000..d62502dcb78d --- /dev/null +++ b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png Binary files differnew file mode 100755 index 000000000000..c61cce774ec7 --- /dev/null +++ b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png diff --git a/core/res/res/drawable/stat_sys_signal_cdma_0.png b/core/res/res/drawable/stat_sys_signal_cdma_0.png Binary files differnew file mode 100755 index 000000000000..0ef7d534c371 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_cdma_0.png diff --git a/core/res/res/drawable/stat_sys_signal_cdma_1.png b/core/res/res/drawable/stat_sys_signal_cdma_1.png Binary files differnew file mode 100755 index 000000000000..f4839d4954fe --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_cdma_1.png diff --git a/core/res/res/drawable/stat_sys_signal_cdma_2.png b/core/res/res/drawable/stat_sys_signal_cdma_2.png Binary files differnew file mode 100755 index 000000000000..e25a99cff692 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_cdma_2.png diff --git a/core/res/res/drawable/stat_sys_signal_cdma_3.png b/core/res/res/drawable/stat_sys_signal_cdma_3.png Binary files differnew file mode 100755 index 000000000000..d828d99a5b08 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_cdma_3.png diff --git a/core/res/res/drawable/stat_sys_signal_cdma_4.png b/core/res/res/drawable/stat_sys_signal_cdma_4.png Binary files differnew file mode 100755 index 000000000000..53a31ea89e90 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_cdma_4.png diff --git a/core/res/res/drawable/stat_sys_signal_evdo_0.png b/core/res/res/drawable/stat_sys_signal_evdo_0.png Binary files differnew file mode 100755 index 000000000000..1b8aec7cfc80 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_evdo_0.png diff --git a/core/res/res/drawable/stat_sys_signal_evdo_1.png b/core/res/res/drawable/stat_sys_signal_evdo_1.png Binary files differnew file mode 100755 index 000000000000..7ce01fd96a1b --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_evdo_1.png diff --git a/core/res/res/drawable/stat_sys_signal_evdo_2.png b/core/res/res/drawable/stat_sys_signal_evdo_2.png Binary files differnew file mode 100755 index 000000000000..890cd59669de --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_evdo_2.png diff --git a/core/res/res/drawable/stat_sys_signal_evdo_3.png b/core/res/res/drawable/stat_sys_signal_evdo_3.png Binary files differnew file mode 100755 index 000000000000..712c6403bee0 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_evdo_3.png diff --git a/core/res/res/drawable/stat_sys_signal_evdo_4.png b/core/res/res/drawable/stat_sys_signal_evdo_4.png Binary files differnew file mode 100755 index 000000000000..f0537ddd8472 --- /dev/null +++ b/core/res/res/drawable/stat_sys_signal_evdo_4.png diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png Binary files differnew file mode 100644 index 000000000000..7abfd194fb80 --- /dev/null +++ b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml index bb4955a6c71c..03448497f5fd 100644 --- a/core/res/res/layout/character_picker.xml +++ b/core/res/res/layout/character_picker.xml @@ -23,8 +23,8 @@ android:id="@+id/characterPicker" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="12dp" - android:verticalSpacing="8dp" + android:padding="4dp" + android:verticalSpacing="4dp" android:horizontalSpacing="8dp" android:stretchMode="spacingWidth" android:gravity="left" diff --git a/core/res/res/layout/google_web_content_helper_layout.xml b/core/res/res/layout/google_web_content_helper_layout.xml index 40f84bf8f3da..546c4586bcc2 100644 --- a/core/res/res/layout/google_web_content_helper_layout.xml +++ b/core/res/res/layout/google_web_content_helper_layout.xml @@ -18,10 +18,28 @@ android:foregroundGravity="center" android:measureAllChildren="false"> - <!-- Include the indeterminate progress dialog's layout. --> - <include - android:id="@+id/progressContainer" - layout="@android:layout/progress_dialog" /> + <LinearLayout android:id="@+id/progressContainer" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:baselineAligned="false" + android:paddingLeft="8dip" + android:paddingTop="10dip" + android:paddingRight="8dip" + android:paddingBottom="10dip"> + + <ProgressBar android:id="@android:id/progress" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:max="10000" + android:layout_marginRight="12dip" /> + + <TextView android:id="@+id/message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" /> + </LinearLayout> <WebView android:id="@+id/web" diff --git a/core/res/res/layout/list_gestures_overlay.xml b/core/res/res/layout/list_gestures_overlay.xml new file mode 100644 index 000000000000..54d72c8504e7 --- /dev/null +++ b/core/res/res/layout/list_gestures_overlay.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" /> diff --git a/core/res/res/layout/progress_dialog.xml b/core/res/res/layout/progress_dialog.xml index 2d7afd60ee16..8f66451e4b3e 100644 --- a/core/res/res/layout/progress_dialog.xml +++ b/core/res/res/layout/progress_dialog.xml @@ -33,6 +33,7 @@ android:paddingBottom="10dip"> <ProgressBar android:id="@android:id/progress" + style="@android:style/Widget.ProgressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:max="10000" diff --git a/core/res/res/layout/recent_apps_dialog.xml b/core/res/res/layout/recent_apps_dialog.xml index 852b2f17ea1d..c4ee95d0a82a 100644 --- a/core/res/res/layout/recent_apps_dialog.xml +++ b/core/res/res/layout/recent_apps_dialog.xml @@ -17,67 +17,63 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" - android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:padding="3dip" + android:orientation="vertical"> + + <!-- This is only intended to be visible when all buttons (below) are invisible --> + <TextView + android:id="@+id/no_applications_message" + android:layout_width="285dip" + android:layout_height="wrap_content" + android:layout_marginTop="15dip" + android:layout_marginBottom="15dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="@android:string/no_recent_tasks" /> + + <!-- The first row has a fixed-width because the UI spec requires the box + to display with full-width no matter how many icons are visible, but to + adjust height based on number of rows. --> + <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> + <LinearLayout + android:layout_width="285dip" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button1" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button2" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button3" /> + + </LinearLayout> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="3dip" - android:orientation="vertical"> - - <!-- This is only intended to be visible when all buttons (below) are invisible --> - <TextView - android:id="@+id/no_applications_message" - android:layout_width="285dip" - android:layout_height="wrap_content" - android:layout_marginTop="15dip" - android:layout_marginBottom="15dip" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceMedium" - android:text="@android:string/no_recent_tasks" /> - - <!-- The first row has a fixed-width because the UI spec requires the box - to display with full-width no matter how many icons are visible, but to - adjust height based on number of rows. --> - <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> - <LinearLayout - android:layout_width="285dip" - android:layout_height="wrap_content" - android:orientation="horizontal" > + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button4" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button5" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button6" /> - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button1" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button2" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button3" /> - - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" > - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button4" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button5" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button6" /> - - </LinearLayout> </LinearLayout> -</FrameLayout> +</LinearLayout>
\ No newline at end of file diff --git a/core/res/res/layout/recent_apps_icon.xml b/core/res/res/layout/recent_apps_icon.xml index b8cf08964697..d32643cb15dd 100644 --- a/core/res/res/layout/recent_apps_icon.xml +++ b/core/res/res/layout/recent_apps_icon.xml @@ -18,27 +18,22 @@ --> <!-- This is not a standalone element - it is imported into recent_apps_dialog.xml --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/label" + style="?android:attr/buttonStyle" + android:background="@drawable/btn_application_selector" android:layout_width="87dip" - android:layout_height="78dip" + android:layout_height="88dip" android:layout_margin="3dip" - android:orientation="vertical" - android:gravity="center_vertical" - style="?android:attr/buttonStyle" - android:background="@drawable/btn_application_selector"> - <ImageView android:id="@+id/icon" - android:layout_width="@android:dimen/app_icon_size" - android:layout_height="@android:dimen/app_icon_size" - android:layout_gravity="center_horizontal" - android:scaleType="fitCenter" /> - <TextView android:id="@+id/label" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textSize="12dip" - android:maxLines="1" - android:ellipsize="end" - android:duplicateParentState="true" - android:textColor="@color/primary_text_dark_focused" - android:gravity="center_horizontal" /> -</LinearLayout> + android:textColor="@color/primary_text_dark_focused" + + android:paddingTop="5dip" + android:paddingBottom="2dip" + android:drawablePadding="0dip" + + android:textSize="13dip" + android:maxLines="2" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top|center_horizontal" /> diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index b5124904e427..13e66aad0d25 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -71,9 +71,12 @@ android:layout_weight="1.0" android:paddingLeft="8dip" android:paddingRight="6dip" + android:drawablePadding="2dip" android:singleLine="true" + 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/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml index 0d074909361e..2710b3bffcb6 100644 --- a/core/res/res/layout/search_dropdown_item_icons_2line.xml +++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml @@ -67,13 +67,10 @@ android:textAppearance="?android:attr/textAppearanceSearchResultTitle" android:singleLine="true" android:layout_width="fill_parent" - android:layout_height="29dip" - android:paddingTop="4dip" - android:gravity="center_vertical" - android:layout_alignParentTop="true" + android:layout_height="wrap_content" + android:layout_centerVertical="true" android:layout_toRightOf="@android:id/icon1" android:layout_toLeftOf="@android:id/icon2" - android:layout_above="@android:id/text2" - android:layout_alignWithParentIfMissing="true" /> + android:layout_above="@android:id/text2" /> </RelativeLayout> diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml new file mode 100644 index 000000000000..2c20ffcdf38e --- /dev/null +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">يناير</string> + <string name="month_long_standalone_february">فبراير</string> + <string name="month_long_standalone_march">مارس</string> + <string name="month_long_standalone_april">أبريل</string> + <string name="month_long_standalone_may">مايو</string> + <string name="month_long_standalone_june">يونيو</string> + <string name="month_long_standalone_july">يوليو</string> + <string name="month_long_standalone_august">أغسطس</string> + <string name="month_long_standalone_september">سبتمبر</string> + <string name="month_long_standalone_october">أكتوبر</string> + <string name="month_long_standalone_november">نوفمبر</string> + <string name="month_long_standalone_december">ديسمبر</string> + + <string name="month_long_january">يناير</string> + <string name="month_long_february">فبراير</string> + <string name="month_long_march">مارس</string> + <string name="month_long_april">أبريل</string> + <string name="month_long_may">مايو</string> + <string name="month_long_june">يونيو</string> + <string name="month_long_july">يوليو</string> + <string name="month_long_august">أغسطس</string> + <string name="month_long_september">سبتمبر</string> + <string name="month_long_october">أكتوبر</string> + <string name="month_long_november">نوفمبر</string> + <string name="month_long_december">ديسمبر</string> + + <string name="month_medium_january">يناير</string> + <string name="month_medium_february">فبراير</string> + <string name="month_medium_march">مارس</string> + <string name="month_medium_april">أبريل</string> + <string name="month_medium_may">مايو</string> + <string name="month_medium_june">يونيو</string> + <string name="month_medium_july">يوليو</string> + <string name="month_medium_august">أغسطس</string> + <string name="month_medium_september">سبتمبر</string> + <string name="month_medium_october">أكتوبر</string> + <string name="month_medium_november">نوفمبر</string> + <string name="month_medium_december">ديسمبر</string> + + <string name="month_shortest_january">ي</string> + <string name="month_shortest_february">ف</string> + <string name="month_shortest_march">م</string> + <string name="month_shortest_april">أ</string> + <string name="month_shortest_may">و</string> + <string name="month_shortest_june">ن</string> + <string name="month_shortest_july">ل</string> + <string name="month_shortest_august">غ</string> + <string name="month_shortest_september">س</string> + <string name="month_shortest_october">ك</string> + <string name="month_shortest_november">ب</string> + <string name="month_shortest_december">د</string> + + <string name="day_of_week_long_sunday">الأحد</string> + <string name="day_of_week_long_monday">الإثنين</string> + <string name="day_of_week_long_tuesday">الثلاثاء</string> + <string name="day_of_week_long_wednesday">الأربعاء</string> + <string name="day_of_week_long_thursday">الخميس</string> + <string name="day_of_week_long_friday">الجمعة</string> + <string name="day_of_week_long_saturday">السبت</string> + + <string name="day_of_week_medium_sunday">أحد</string> + <string name="day_of_week_medium_monday">إثنين</string> + <string name="day_of_week_medium_tuesday">ثلاثاء</string> + <string name="day_of_week_medium_wednesday">أربعاء</string> + <string name="day_of_week_medium_thursday">خميس</string> + <string name="day_of_week_medium_friday">جمعة</string> + <string name="day_of_week_medium_saturday">سبت</string> + + <string name="day_of_week_short_sunday">أحد</string> + <string name="day_of_week_short_monday">إثنين</string> + <string name="day_of_week_short_tuesday">ثلاثاء</string> + <string name="day_of_week_short_wednesday">أربعاء</string> + <string name="day_of_week_short_thursday">خميس</string> + <string name="day_of_week_short_friday">جمعة</string> + <string name="day_of_week_short_saturday">سبت</string> + + <string name="day_of_week_shortest_sunday">ح</string> + <string name="day_of_week_shortest_monday">ن</string> + <string name="day_of_week_shortest_tuesday">ث</string> + <string name="day_of_week_shortest_wednesday">ر</string> + <string name="day_of_week_shortest_thursday">خ</string> + <string name="day_of_week_shortest_friday">ج</string> + <string name="day_of_week_shortest_saturday">س</string> + + <string name="am">ص</string> + <string name="pm">م</string> + <string name="yesterday">أمس</string> + <string name="today">اليوم</string> + <string name="tomorrow">غدًا</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e/%-m/%Y</string> + <string name="numeric_date_format">d/M/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B، %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-l:%M:%S %p %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s، %3$s/%2$s - %6$s، %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s، %3$s/%2$s/%4$s - %6$s، %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s، %3$s/%2$s/%4$s – %10$s %6$s، %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s، %3$s-%2$s – %10$s %6$s، %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s، %4$s – %10$s %8$s %7$s، %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s، %4$s – %10$s %8$s %7$s، %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s، %3$s %2$s %4$s – %10$s %6$s، %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s، %3$s %2$s %4$s – %10$s %6$s، %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s، %3$s %2$s %4$s – %6$s، %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s، %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s، %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s، %3$s %2$s - %6$s، %8$s %7$s، %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-bg-rBG/donottranslate-cldr.xml b/core/res/res/values-bg-rBG/donottranslate-cldr.xml new file mode 100644 index 000000000000..b8b50cccdd78 --- /dev/null +++ b/core/res/res/values-bg-rBG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">януари</string> + <string name="month_long_standalone_february">февруари</string> + <string name="month_long_standalone_march">март</string> + <string name="month_long_standalone_april">април</string> + <string name="month_long_standalone_may">май</string> + <string name="month_long_standalone_june">юни</string> + <string name="month_long_standalone_july">юли</string> + <string name="month_long_standalone_august">август</string> + <string name="month_long_standalone_september">септември</string> + <string name="month_long_standalone_october">октомври</string> + <string name="month_long_standalone_november">ноември</string> + <string name="month_long_standalone_december">декември</string> + + <string name="month_long_january">януари</string> + <string name="month_long_february">февруари</string> + <string name="month_long_march">март</string> + <string name="month_long_april">април</string> + <string name="month_long_may">май</string> + <string name="month_long_june">юни</string> + <string name="month_long_july">юли</string> + <string name="month_long_august">август</string> + <string name="month_long_september">септември</string> + <string name="month_long_october">октомври</string> + <string name="month_long_november">ноември</string> + <string name="month_long_december">декември</string> + + <string name="month_medium_january">ян.</string> + <string name="month_medium_february">февр.</string> + <string name="month_medium_march">март</string> + <string name="month_medium_april">апр.</string> + <string name="month_medium_may">май</string> + <string name="month_medium_june">юни</string> + <string name="month_medium_july">юли</string> + <string name="month_medium_august">авг.</string> + <string name="month_medium_september">септ.</string> + <string name="month_medium_october">окт.</string> + <string name="month_medium_november">ноем.</string> + <string name="month_medium_december">дек.</string> + + <string name="month_shortest_january">я</string> + <string name="month_shortest_february">ф</string> + <string name="month_shortest_march">м</string> + <string name="month_shortest_april">а</string> + <string name="month_shortest_may">м</string> + <string name="month_shortest_june">ю</string> + <string name="month_shortest_july">ю</string> + <string name="month_shortest_august">а</string> + <string name="month_shortest_september">с</string> + <string name="month_shortest_october">о</string> + <string name="month_shortest_november">н</string> + <string name="month_shortest_december">д</string> + + <string name="day_of_week_long_sunday">неделя</string> + <string name="day_of_week_long_monday">понеделник</string> + <string name="day_of_week_long_tuesday">вторник</string> + <string name="day_of_week_long_wednesday">сряда</string> + <string name="day_of_week_long_thursday">четвъртък</string> + <string name="day_of_week_long_friday">петък</string> + <string name="day_of_week_long_saturday">събота</string> + + <string name="day_of_week_medium_sunday">нд</string> + <string name="day_of_week_medium_monday">пн</string> + <string name="day_of_week_medium_tuesday">вт</string> + <string name="day_of_week_medium_wednesday">ср</string> + <string name="day_of_week_medium_thursday">чт</string> + <string name="day_of_week_medium_friday">пт</string> + <string name="day_of_week_medium_saturday">сб</string> + + <string name="day_of_week_short_sunday">нд</string> + <string name="day_of_week_short_monday">пн</string> + <string name="day_of_week_short_tuesday">вт</string> + <string name="day_of_week_short_wednesday">ср</string> + <string name="day_of_week_short_thursday">чт</string> + <string name="day_of_week_short_friday">пт</string> + <string name="day_of_week_short_saturday">сб</string> + + <string name="day_of_week_shortest_sunday">н</string> + <string name="day_of_week_shortest_monday">п</string> + <string name="day_of_week_shortest_tuesday">в</string> + <string name="day_of_week_shortest_wednesday">с</string> + <string name="day_of_week_shortest_thursday">ч</string> + <string name="day_of_week_shortest_friday">п</string> + <string name="day_of_week_shortest_saturday">с</string> + + <string name="am">пр. об.</string> + <string name="pm">сл. об.</string> + <string name="yesterday">Вчера</string> + <string name="today">Днес</string> + <string name="tomorrow">Утре</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s, %1$s - %8$s.%7$s, %6$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s, %1$s - %8$s.%7$s.%9$s, %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s, %1$s - %10$s %8$s.%7$s.%9$s, %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s.%2$s, %1$s - %10$s %8$s.%7$s, %6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s, %1$s - %6$s %5$s, %4$s</string> + <string name="wday1_date1_wday2_date2">%2$s, %1$s - %5$s, %4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s, %2$s</string> + <string name="wday_date">%3$s, %2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s, %1$s - %8$s %7$s %9$s, %6$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ca-rES/donottranslate-cldr.xml b/core/res/res/values-ca-rES/donottranslate-cldr.xml new file mode 100644 index 000000000000..d5abeef1a96a --- /dev/null +++ b/core/res/res/values-ca-rES/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">gener</string> + <string name="month_long_standalone_february">febrer</string> + <string name="month_long_standalone_march">març</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">maig</string> + <string name="month_long_standalone_june">juny</string> + <string name="month_long_standalone_july">juliol</string> + <string name="month_long_standalone_august">agost</string> + <string name="month_long_standalone_september">setembre</string> + <string name="month_long_standalone_october">octubre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">desembre</string> + + <string name="month_long_january">gener</string> + <string name="month_long_february">febrer</string> + <string name="month_long_march">març</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">maig</string> + <string name="month_long_june">juny</string> + <string name="month_long_july">juliol</string> + <string name="month_long_august">agost</string> + <string name="month_long_september">setembre</string> + <string name="month_long_october">octubre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">desembre</string> + + <string name="month_medium_january">gen.</string> + <string name="month_medium_february">febr.</string> + <string name="month_medium_march">març</string> + <string name="month_medium_april">abr.</string> + <string name="month_medium_may">maig</string> + <string name="month_medium_june">juny</string> + <string name="month_medium_july">jul.</string> + <string name="month_medium_august">ag.</string> + <string name="month_medium_september">set.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">des.</string> + + <string name="month_shortest_january">g</string> + <string name="month_shortest_february">f</string> + <string name="month_shortest_march">m</string> + <string name="month_shortest_april">a</string> + <string name="month_shortest_may">m</string> + <string name="month_shortest_june">j</string> + <string name="month_shortest_july">j</string> + <string name="month_shortest_august">a</string> + <string name="month_shortest_september">s</string> + <string name="month_shortest_october">o</string> + <string name="month_shortest_november">n</string> + <string name="month_shortest_december">d</string> + + <string name="day_of_week_long_sunday">diumenge</string> + <string name="day_of_week_long_monday">dilluns</string> + <string name="day_of_week_long_tuesday">dimarts</string> + <string name="day_of_week_long_wednesday">dimecres</string> + <string name="day_of_week_long_thursday">dijous</string> + <string name="day_of_week_long_friday">divendres</string> + <string name="day_of_week_long_saturday">dissabte</string> + + <string name="day_of_week_medium_sunday">dg.</string> + <string name="day_of_week_medium_monday">dl.</string> + <string name="day_of_week_medium_tuesday">dt.</string> + <string name="day_of_week_medium_wednesday">dc.</string> + <string name="day_of_week_medium_thursday">dj.</string> + <string name="day_of_week_medium_friday">dv.</string> + <string name="day_of_week_medium_saturday">ds.</string> + + <string name="day_of_week_short_sunday">dg.</string> + <string name="day_of_week_short_monday">dl.</string> + <string name="day_of_week_short_tuesday">dt.</string> + <string name="day_of_week_short_wednesday">dc.</string> + <string name="day_of_week_short_thursday">dj.</string> + <string name="day_of_week_short_friday">dv.</string> + <string name="day_of_week_short_saturday">ds.</string> + + <string name="day_of_week_shortest_sunday">g</string> + <string name="day_of_week_shortest_monday">l</string> + <string name="day_of_week_shortest_tuesday">t</string> + <string name="day_of_week_shortest_wednesday">c</string> + <string name="day_of_week_shortest_thursday">j</string> + <string name="day_of_week_shortest_friday">v</string> + <string name="day_of_week_shortest_saturday">s</string> + + <string name="am">a.m.</string> + <string name="pm">p.m.</string> + <string name="yesterday">ahir</string> + <string name="today">avui</string> + <string name="tomorrow">demà</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B del %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s - %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s - %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s de %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml new file mode 100644 index 000000000000..41f5dea81f00 --- /dev/null +++ b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">leden</string> + <string name="month_long_standalone_february">únor</string> + <string name="month_long_standalone_march">březen</string> + <string name="month_long_standalone_april">duben</string> + <string name="month_long_standalone_may">květen</string> + <string name="month_long_standalone_june">červen</string> + <string name="month_long_standalone_july">červenec</string> + <string name="month_long_standalone_august">srpen</string> + <string name="month_long_standalone_september">září</string> + <string name="month_long_standalone_october">říjen</string> + <string name="month_long_standalone_november">listopad</string> + <string name="month_long_standalone_december">prosinec</string> + + <string name="month_long_january">ledna</string> + <string name="month_long_february">února</string> + <string name="month_long_march">března</string> + <string name="month_long_april">dubna</string> + <string name="month_long_may">května</string> + <string name="month_long_june">června</string> + <string name="month_long_july">července</string> + <string name="month_long_august">srpna</string> + <string name="month_long_september">září</string> + <string name="month_long_october">října</string> + <string name="month_long_november">listopadu</string> + <string name="month_long_december">prosince</string> + + <string name="month_medium_january">1</string> + <string name="month_medium_february">2</string> + <string name="month_medium_march">3</string> + <string name="month_medium_april">4</string> + <string name="month_medium_may">5</string> + <string name="month_medium_june">6</string> + <string name="month_medium_july">7</string> + <string name="month_medium_august">8</string> + <string name="month_medium_september">9</string> + <string name="month_medium_october">10</string> + <string name="month_medium_november">11</string> + <string name="month_medium_december">12</string> + + <string name="month_shortest_january">l</string> + <string name="month_shortest_february">ú</string> + <string name="month_shortest_march">b</string> + <string name="month_shortest_april">d</string> + <string name="month_shortest_may">k</string> + <string name="month_shortest_june">č</string> + <string name="month_shortest_july">č</string> + <string name="month_shortest_august">s</string> + <string name="month_shortest_september">z</string> + <string name="month_shortest_october">ř</string> + <string name="month_shortest_november">l</string> + <string name="month_shortest_december">p</string> + + <string name="day_of_week_long_sunday">neděle</string> + <string name="day_of_week_long_monday">pondělí</string> + <string name="day_of_week_long_tuesday">úterý</string> + <string name="day_of_week_long_wednesday">středa</string> + <string name="day_of_week_long_thursday">čtvrtek</string> + <string name="day_of_week_long_friday">pátek</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">ne</string> + <string name="day_of_week_medium_monday">po</string> + <string name="day_of_week_medium_tuesday">út</string> + <string name="day_of_week_medium_wednesday">st</string> + <string name="day_of_week_medium_thursday">čt</string> + <string name="day_of_week_medium_friday">pá</string> + <string name="day_of_week_medium_saturday">so</string> + + <string name="day_of_week_short_sunday">ne</string> + <string name="day_of_week_short_monday">po</string> + <string name="day_of_week_short_tuesday">út</string> + <string name="day_of_week_short_wednesday">st</string> + <string name="day_of_week_short_thursday">čt</string> + <string name="day_of_week_short_friday">pá</string> + <string name="day_of_week_short_saturday">so</string> + + <string name="day_of_week_shortest_sunday">N</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">Ú</string> + <string name="day_of_week_shortest_wednesday">S</string> + <string name="day_of_week_shortest_thursday">Č</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">dop.</string> + <string name="pm">odp.</string> + <string name="yesterday">Včera</string> + <string name="today">Dnes</string> + <string name="tomorrow">Zítra</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e.%-m.%Y</string> + <string name="numeric_date_format">d.M.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %-e.%-m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e.%-m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e.%-m</string> + <string name="abbrev_month">%-B</string> + <string name="abbrev_month_year">%-B %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%B</string> +</resources> diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml new file mode 100644 index 000000000000..41f5dea81f00 --- /dev/null +++ b/core/res/res/values-cs/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">leden</string> + <string name="month_long_standalone_february">únor</string> + <string name="month_long_standalone_march">březen</string> + <string name="month_long_standalone_april">duben</string> + <string name="month_long_standalone_may">květen</string> + <string name="month_long_standalone_june">červen</string> + <string name="month_long_standalone_july">červenec</string> + <string name="month_long_standalone_august">srpen</string> + <string name="month_long_standalone_september">září</string> + <string name="month_long_standalone_october">říjen</string> + <string name="month_long_standalone_november">listopad</string> + <string name="month_long_standalone_december">prosinec</string> + + <string name="month_long_january">ledna</string> + <string name="month_long_february">února</string> + <string name="month_long_march">března</string> + <string name="month_long_april">dubna</string> + <string name="month_long_may">května</string> + <string name="month_long_june">června</string> + <string name="month_long_july">července</string> + <string name="month_long_august">srpna</string> + <string name="month_long_september">září</string> + <string name="month_long_october">října</string> + <string name="month_long_november">listopadu</string> + <string name="month_long_december">prosince</string> + + <string name="month_medium_january">1</string> + <string name="month_medium_february">2</string> + <string name="month_medium_march">3</string> + <string name="month_medium_april">4</string> + <string name="month_medium_may">5</string> + <string name="month_medium_june">6</string> + <string name="month_medium_july">7</string> + <string name="month_medium_august">8</string> + <string name="month_medium_september">9</string> + <string name="month_medium_october">10</string> + <string name="month_medium_november">11</string> + <string name="month_medium_december">12</string> + + <string name="month_shortest_january">l</string> + <string name="month_shortest_february">ú</string> + <string name="month_shortest_march">b</string> + <string name="month_shortest_april">d</string> + <string name="month_shortest_may">k</string> + <string name="month_shortest_june">č</string> + <string name="month_shortest_july">č</string> + <string name="month_shortest_august">s</string> + <string name="month_shortest_september">z</string> + <string name="month_shortest_october">ř</string> + <string name="month_shortest_november">l</string> + <string name="month_shortest_december">p</string> + + <string name="day_of_week_long_sunday">neděle</string> + <string name="day_of_week_long_monday">pondělí</string> + <string name="day_of_week_long_tuesday">úterý</string> + <string name="day_of_week_long_wednesday">středa</string> + <string name="day_of_week_long_thursday">čtvrtek</string> + <string name="day_of_week_long_friday">pátek</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">ne</string> + <string name="day_of_week_medium_monday">po</string> + <string name="day_of_week_medium_tuesday">út</string> + <string name="day_of_week_medium_wednesday">st</string> + <string name="day_of_week_medium_thursday">čt</string> + <string name="day_of_week_medium_friday">pá</string> + <string name="day_of_week_medium_saturday">so</string> + + <string name="day_of_week_short_sunday">ne</string> + <string name="day_of_week_short_monday">po</string> + <string name="day_of_week_short_tuesday">út</string> + <string name="day_of_week_short_wednesday">st</string> + <string name="day_of_week_short_thursday">čt</string> + <string name="day_of_week_short_friday">pá</string> + <string name="day_of_week_short_saturday">so</string> + + <string name="day_of_week_shortest_sunday">N</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">Ú</string> + <string name="day_of_week_shortest_wednesday">S</string> + <string name="day_of_week_shortest_thursday">Č</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">dop.</string> + <string name="pm">odp.</string> + <string name="yesterday">Včera</string> + <string name="today">Dnes</string> + <string name="tomorrow">Zítra</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e.%-m.%Y</string> + <string name="numeric_date_format">d.M.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %-e.%-m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e.%-m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e.%-m</string> + <string name="abbrev_month">%-B</string> + <string name="abbrev_month_year">%-B %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%B</string> +</resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 35a3f9a9cb50..7dbeaebe5d72 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Umožňuje aplikaci změnit aktuální konfiguraci, např. národní prostředí či obecnou velikost písma."</string> <string name="permlab_restartPackages">"restartování ostatních aplikací"</string> <string name="permdesc_restartPackages">"Umožňuje aplikaci vynutit restartování jiných aplikací."</string> - <string name="permlab_setProcessForeground">"zamezení zastavení aplikace"</string> - <string name="permdesc_setProcessForeground">"Umožňuje aplikaci spustit jakýkoli proces v popředí tak, že ho nelze ukončit. Běžné aplikace by toto nastavení nikdy neměly používat."</string> <string name="permlab_forceBack">"vynucení zavření aplikace"</string> <string name="permdesc_forceBack">"Umožňuje aplikaci vynutit zavření a přesunutí libovolné činnosti v popředí na pozadí. Běžné aplikace by toto nastavení neměly nikdy využívat."</string> <string name="permlab_dump">"načtení interního stavu systému"</string> <string name="permdesc_dump">"Umožňuje aplikaci načíst interní stav systému. Škodlivé aplikace mohou načíst řádu soukromých a zabezpečených informací, které by nikdy neměly potřebovat."</string> - <string name="permlab_addSystemService">"zveřejnění nízkoúrovňových služeb"</string> - <string name="permdesc_addSystemService">"Umožňuje aplikaci zveřejnit své vlastní nízkoúrovňové systémové služby. Škodlivé aplikace mohou převzít kontrolu nad systémem a získat či poškodit jakákoli data v něm obsažená."</string> <string name="permlab_runSetActivityWatcher">"sledování a řízení spouštění všech aplikací"</string> <string name="permdesc_runSetActivityWatcher">"Umožňuje aplikaci sledovat a řídit spouštění činností systémem. Škodlivé aplikace mohou zcela ovládnout systém. Toto oprávnění je zapotřebí pouze pro účely vývoje, nikdy pro běžné použití telefonu."</string> <string name="permlab_broadcastPackageRemoved">"odeslání vysílání o odstranění balíčku"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Umožňuje aplikaci řídit maximální počet spuštěných procesů. Běžné aplikace toto nastavení nikdy nevyužívají."</string> <string name="permlab_setAlwaysFinish">"zavření všech aplikací na pozadí"</string> <string name="permdesc_setAlwaysFinish">"Umožňuje aplikaci ovládat, zda jsou činnosti vždy dokončeny po přesunutí do pozadí. Běžné aplikace toto nastavení nikdy nevyužívají."</string> - <string name="permlab_fotaUpdate">"automatická instalace aktualizací systému"</string> - <string name="permdesc_fotaUpdate">"Umožňuje aplikaci přijímat oznámení o čekajících aktualizacích systému a spouštět jejich instalaci. Škodlivé aplikace mohou díky tomuto nastavení poškodit systém pomocí neoprávněných aktualizací nebo celkově narušovat proces aktualizace."</string> <string name="permlab_batteryStats">"změna statistických údajů o baterii"</string> <string name="permdesc_batteryStats">"Umožňuje změnu shromážděných statistických údajů o baterii. Není určeno pro běžné aplikace."</string> <string name="permlab_internalSystemWindow">"zobrazení nepovolených oken"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Heslo"</string> <string name="lockscreen_glogin_submit_button">"Přihlásit se"</string> <string name="lockscreen_glogin_invalid_input">"Neplatné uživatelské jméno nebo heslo."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Vymazat oznámení"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"enter"</string> <string name="menu_delete_shortcut_label">"smazat"</string> <string name="search_go">"Hledat"</string> - <string name="today">"Dnes"</string> - <string name="yesterday">"Včera"</string> - <string name="tomorrow">"Zítra"</string> <string name="oneMonthDurationPast">"před 1 měsícem"</string> <string name="beforeOneMonthDurationPast">"Déle než před 1 měsícem"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"týd."</string> <string name="year">"rokem"</string> <string name="years">"lety"</string> - <string name="sunday">"neděle"</string> - <string name="monday">"pondělí"</string> - <string name="tuesday">"úterý"</string> - <string name="wednesday">"středa"</string> - <string name="thursday">"čtvrtek"</string> - <string name="friday">"pátek"</string> - <string name="saturday">"sobota"</string> <string name="every_weekday">"Každý pracovní den (Po – Pá)"</string> <string name="daily">"Denně"</string> <string name="weekly">"Každý týden v <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Omlouváme se, ale toto video nelze přenášet datovým proudem do tohoto zařízení."</string> <string name="VideoView_error_text_unknown">"Toto video bohužel nelze přehrát."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"dop."</string> - <string name="pm">"odp."</string> - <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"poledne"</string> <string name="Noon">"Poledne"</string> <string name="midnight">"půlnoc"</string> <string name="Midnight">"Půlnoc"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>."</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>. <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>. <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>., <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>., <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"neděle"</string> - <string name="day_of_week_long_monday">"pondělí"</string> - <string name="day_of_week_long_tuesday">"úterý"</string> - <string name="day_of_week_long_wednesday">"středa"</string> - <string name="day_of_week_long_thursday">"čtvrtek"</string> - <string name="day_of_week_long_friday">"pátek"</string> - <string name="day_of_week_long_saturday">"sobota"</string> - <string name="day_of_week_medium_sunday">"Ne"</string> - <string name="day_of_week_medium_monday">"Po"</string> - <string name="day_of_week_medium_tuesday">"Út"</string> - <string name="day_of_week_medium_wednesday">"St"</string> - <string name="day_of_week_medium_thursday">"Čt"</string> - <string name="day_of_week_medium_friday">"Pá"</string> - <string name="day_of_week_medium_saturday">"So"</string> - <string name="day_of_week_short_sunday">"Ne"</string> - <string name="day_of_week_short_monday">"Po"</string> - <string name="day_of_week_short_tuesday">"Út"</string> - <string name="day_of_week_short_wednesday">"St"</string> - <string name="day_of_week_short_thursday">"Čt"</string> - <string name="day_of_week_short_friday">"Pá"</string> - <string name="day_of_week_short_saturday">"So"</string> - <string name="day_of_week_shorter_sunday">"Ne"</string> - <string name="day_of_week_shorter_monday">"Po"</string> - <string name="day_of_week_shorter_tuesday">"Út"</string> - <string name="day_of_week_shorter_wednesday">"St"</string> - <string name="day_of_week_shorter_thursday">"Čt"</string> - <string name="day_of_week_shorter_friday">"Pá"</string> - <string name="day_of_week_shorter_saturday">"So"</string> - <string name="day_of_week_shortest_sunday">"Ne"</string> - <string name="day_of_week_shortest_monday">"Po"</string> - <string name="day_of_week_shortest_tuesday">"Čt"</string> - <string name="day_of_week_shortest_wednesday">"St"</string> - <string name="day_of_week_shortest_thursday">"Čt"</string> - <string name="day_of_week_shortest_friday">"Pá"</string> - <string name="day_of_week_shortest_saturday">"So"</string> - <string name="month_long_january">"leden"</string> - <string name="month_long_february">"únor"</string> - <string name="month_long_march">"březen"</string> - <string name="month_long_april">"duben"</string> - <string name="month_long_may">"květen"</string> - <string name="month_long_june">"červen"</string> - <string name="month_long_july">"červenec"</string> - <string name="month_long_august">"srpen"</string> - <string name="month_long_september">"září"</string> - <string name="month_long_october">"říjen"</string> - <string name="month_long_november">"listopad"</string> - <string name="month_long_december">"prosinec"</string> - <string name="month_medium_january">"leden"</string> - <string name="month_medium_february">"únor"</string> - <string name="month_medium_march">"březen"</string> - <string name="month_medium_april">"duben"</string> - <string name="month_medium_may">"květen"</string> - <string name="month_medium_june">"červen"</string> - <string name="month_medium_july">"červenec"</string> - <string name="month_medium_august">"srpen"</string> - <string name="month_medium_september">"září"</string> - <string name="month_medium_october">"říjen"</string> - <string name="month_medium_november">"listopad"</string> - <string name="month_medium_december">"prosinec"</string> - <string name="month_shortest_january">"1."</string> - <string name="month_shortest_february">"2."</string> - <string name="month_shortest_march">"Po"</string> - <string name="month_shortest_april">"4."</string> - <string name="month_shortest_may">"5."</string> - <string name="month_shortest_june">"6."</string> - <string name="month_shortest_july">"7."</string> - <string name="month_shortest_august">"8."</string> - <string name="month_shortest_september">"9."</string> - <string name="month_shortest_october">"10."</string> - <string name="month_shortest_november">"11."</string> - <string name="month_shortest_december">"12."</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Vybrat vše"</string> diff --git a/core/res/res/values-da-rDK/donottranslate-cldr.xml b/core/res/res/values-da-rDK/donottranslate-cldr.xml new file mode 100644 index 000000000000..2d0db938a69f --- /dev/null +++ b/core/res/res/values-da-rDK/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januar</string> + <string name="month_long_standalone_february">februar</string> + <string name="month_long_standalone_march">marts</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">maj</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">december</string> + + <string name="month_long_january">januar</string> + <string name="month_long_february">februar</string> + <string name="month_long_march">marts</string> + <string name="month_long_april">april</string> + <string name="month_long_may">maj</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">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mar.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">maj</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">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">søndag</string> + <string name="day_of_week_long_monday">mandag</string> + <string name="day_of_week_long_tuesday">tirsdag</string> + <string name="day_of_week_long_wednesday">onsdag</string> + <string name="day_of_week_long_thursday">torsdag</string> + <string name="day_of_week_long_friday">fredag</string> + <string name="day_of_week_long_saturday">lørdag</string> + + <string name="day_of_week_medium_sunday">søn</string> + <string name="day_of_week_medium_monday">man</string> + <string name="day_of_week_medium_tuesday">tir</string> + <string name="day_of_week_medium_wednesday">ons</string> + <string name="day_of_week_medium_thursday">tor</string> + <string name="day_of_week_medium_friday">fre</string> + <string name="day_of_week_medium_saturday">lør</string> + + <string name="day_of_week_short_sunday">søn</string> + <string name="day_of_week_short_monday">man</string> + <string name="day_of_week_short_tuesday">tir</string> + <string name="day_of_week_short_wednesday">ons</string> + <string name="day_of_week_short_thursday">tor</string> + <string name="day_of_week_short_friday">fre</string> + <string name="day_of_week_short_saturday">lør</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">O</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">f.m.</string> + <string name="pm">e.m.</string> + <string name="yesterday">i går</string> + <string name="today">i dag</string> + <string name="tomorrow">i morgen</string> + + <string name="hour_minute_24">%H.%M</string> + <string name="hour_minute_ampm">%-l.%M %p</string> + <string name="hour_minute_cap_ampm">%-l.%M %^p</string> + <string name="twelve_hour_time_format">h.mm a</string> + <string name="twenty_four_hour_time_format">HH.mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e. %b %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s den %2$s - %6$s %4$s den %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s den %2$s - %4$s den %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s den %3$s</string> + <string name="wday_date">%2$s den %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml new file mode 100644 index 000000000000..2d0db938a69f --- /dev/null +++ b/core/res/res/values-da/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januar</string> + <string name="month_long_standalone_february">februar</string> + <string name="month_long_standalone_march">marts</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">maj</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">december</string> + + <string name="month_long_january">januar</string> + <string name="month_long_february">februar</string> + <string name="month_long_march">marts</string> + <string name="month_long_april">april</string> + <string name="month_long_may">maj</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">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mar.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">maj</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">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">søndag</string> + <string name="day_of_week_long_monday">mandag</string> + <string name="day_of_week_long_tuesday">tirsdag</string> + <string name="day_of_week_long_wednesday">onsdag</string> + <string name="day_of_week_long_thursday">torsdag</string> + <string name="day_of_week_long_friday">fredag</string> + <string name="day_of_week_long_saturday">lørdag</string> + + <string name="day_of_week_medium_sunday">søn</string> + <string name="day_of_week_medium_monday">man</string> + <string name="day_of_week_medium_tuesday">tir</string> + <string name="day_of_week_medium_wednesday">ons</string> + <string name="day_of_week_medium_thursday">tor</string> + <string name="day_of_week_medium_friday">fre</string> + <string name="day_of_week_medium_saturday">lør</string> + + <string name="day_of_week_short_sunday">søn</string> + <string name="day_of_week_short_monday">man</string> + <string name="day_of_week_short_tuesday">tir</string> + <string name="day_of_week_short_wednesday">ons</string> + <string name="day_of_week_short_thursday">tor</string> + <string name="day_of_week_short_friday">fre</string> + <string name="day_of_week_short_saturday">lør</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">O</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">f.m.</string> + <string name="pm">e.m.</string> + <string name="yesterday">i går</string> + <string name="today">i dag</string> + <string name="tomorrow">i morgen</string> + + <string name="hour_minute_24">%H.%M</string> + <string name="hour_minute_ampm">%-l.%M %p</string> + <string name="hour_minute_cap_ampm">%-l.%M %^p</string> + <string name="twelve_hour_time_format">h.mm a</string> + <string name="twenty_four_hour_time_format">HH.mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e. %b %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s den %2$s - %6$s %4$s den %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s den %2$s - %4$s den %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s den %3$s</string> + <string name="wday_date">%2$s den %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de-rAT/donottranslate-cldr.xml b/core/res/res/values-de-rAT/donottranslate-cldr.xml new file mode 100644 index 000000000000..27624a36ab42 --- /dev/null +++ b/core/res/res/values-de-rAT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sonntag</string> + <string name="day_of_week_long_monday">Montag</string> + <string name="day_of_week_long_tuesday">Dienstag</string> + <string name="day_of_week_long_wednesday">Mittwoch</string> + <string name="day_of_week_long_thursday">Donnerstag</string> + <string name="day_of_week_long_friday">Freitag</string> + <string name="day_of_week_long_saturday">Samstag</string> + + <string name="day_of_week_medium_sunday">So.</string> + <string name="day_of_week_medium_monday">Mo.</string> + <string name="day_of_week_medium_tuesday">Di.</string> + <string name="day_of_week_medium_wednesday">Mi.</string> + <string name="day_of_week_medium_thursday">Do.</string> + <string name="day_of_week_medium_friday">Fr.</string> + <string name="day_of_week_medium_saturday">Sa.</string> + + <string name="day_of_week_short_sunday">So.</string> + <string name="day_of_week_short_monday">Mo.</string> + <string name="day_of_week_short_tuesday">Di.</string> + <string name="day_of_week_short_wednesday">Mi.</string> + <string name="day_of_week_short_thursday">Do.</string> + <string name="day_of_week_short_friday">Fr.</string> + <string name="day_of_week_short_saturday">Sa.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">vorm.</string> + <string name="pm">nachm.</string> + <string name="yesterday">Gestern</string> + <string name="today">Heute</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%d. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%d. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de-rCH/donottranslate-cldr.xml b/core/res/res/values-de-rCH/donottranslate-cldr.xml new file mode 100644 index 000000000000..f32095bd336e --- /dev/null +++ b/core/res/res/values-de-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Januar</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">Januar</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">Jan</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sonntag</string> + <string name="day_of_week_long_monday">Montag</string> + <string name="day_of_week_long_tuesday">Dienstag</string> + <string name="day_of_week_long_wednesday">Mittwoch</string> + <string name="day_of_week_long_thursday">Donnerstag</string> + <string name="day_of_week_long_friday">Freitag</string> + <string name="day_of_week_long_saturday">Samstag</string> + + <string name="day_of_week_medium_sunday">So.</string> + <string name="day_of_week_medium_monday">Mo.</string> + <string name="day_of_week_medium_tuesday">Di.</string> + <string name="day_of_week_medium_wednesday">Mi.</string> + <string name="day_of_week_medium_thursday">Do.</string> + <string name="day_of_week_medium_friday">Fr.</string> + <string name="day_of_week_medium_saturday">Sa.</string> + + <string name="day_of_week_short_sunday">So.</string> + <string name="day_of_week_short_monday">Mo.</string> + <string name="day_of_week_short_tuesday">Di.</string> + <string name="day_of_week_short_wednesday">Mi.</string> + <string name="day_of_week_short_thursday">Do.</string> + <string name="day_of_week_short_friday">Fr.</string> + <string name="day_of_week_short_saturday">Sa.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">vorm.</string> + <string name="pm">nachm.</string> + <string name="yesterday">Gestern</string> + <string name="today">Heute</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de-rDE/donottranslate-cldr.xml b/core/res/res/values-de-rDE/donottranslate-cldr.xml new file mode 100644 index 000000000000..f32095bd336e --- /dev/null +++ b/core/res/res/values-de-rDE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Januar</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">Januar</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">Jan</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sonntag</string> + <string name="day_of_week_long_monday">Montag</string> + <string name="day_of_week_long_tuesday">Dienstag</string> + <string name="day_of_week_long_wednesday">Mittwoch</string> + <string name="day_of_week_long_thursday">Donnerstag</string> + <string name="day_of_week_long_friday">Freitag</string> + <string name="day_of_week_long_saturday">Samstag</string> + + <string name="day_of_week_medium_sunday">So.</string> + <string name="day_of_week_medium_monday">Mo.</string> + <string name="day_of_week_medium_tuesday">Di.</string> + <string name="day_of_week_medium_wednesday">Mi.</string> + <string name="day_of_week_medium_thursday">Do.</string> + <string name="day_of_week_medium_friday">Fr.</string> + <string name="day_of_week_medium_saturday">Sa.</string> + + <string name="day_of_week_short_sunday">So.</string> + <string name="day_of_week_short_monday">Mo.</string> + <string name="day_of_week_short_tuesday">Di.</string> + <string name="day_of_week_short_wednesday">Mi.</string> + <string name="day_of_week_short_thursday">Do.</string> + <string name="day_of_week_short_friday">Fr.</string> + <string name="day_of_week_short_saturday">Sa.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">vorm.</string> + <string name="pm">nachm.</string> + <string name="yesterday">Gestern</string> + <string name="today">Heute</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de-rLI/donottranslate-cldr.xml b/core/res/res/values-de-rLI/donottranslate-cldr.xml new file mode 100644 index 000000000000..f32095bd336e --- /dev/null +++ b/core/res/res/values-de-rLI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Januar</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">Januar</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">Jan</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sonntag</string> + <string name="day_of_week_long_monday">Montag</string> + <string name="day_of_week_long_tuesday">Dienstag</string> + <string name="day_of_week_long_wednesday">Mittwoch</string> + <string name="day_of_week_long_thursday">Donnerstag</string> + <string name="day_of_week_long_friday">Freitag</string> + <string name="day_of_week_long_saturday">Samstag</string> + + <string name="day_of_week_medium_sunday">So.</string> + <string name="day_of_week_medium_monday">Mo.</string> + <string name="day_of_week_medium_tuesday">Di.</string> + <string name="day_of_week_medium_wednesday">Mi.</string> + <string name="day_of_week_medium_thursday">Do.</string> + <string name="day_of_week_medium_friday">Fr.</string> + <string name="day_of_week_medium_saturday">Sa.</string> + + <string name="day_of_week_short_sunday">So.</string> + <string name="day_of_week_short_monday">Mo.</string> + <string name="day_of_week_short_tuesday">Di.</string> + <string name="day_of_week_short_wednesday">Mi.</string> + <string name="day_of_week_short_thursday">Do.</string> + <string name="day_of_week_short_friday">Fr.</string> + <string name="day_of_week_short_saturday">Sa.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">vorm.</string> + <string name="pm">nachm.</string> + <string name="yesterday">Gestern</string> + <string name="today">Heute</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml new file mode 100644 index 000000000000..f32095bd336e --- /dev/null +++ b/core/res/res/values-de/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Januar</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">Januar</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">Jan</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sonntag</string> + <string name="day_of_week_long_monday">Montag</string> + <string name="day_of_week_long_tuesday">Dienstag</string> + <string name="day_of_week_long_wednesday">Mittwoch</string> + <string name="day_of_week_long_thursday">Donnerstag</string> + <string name="day_of_week_long_friday">Freitag</string> + <string name="day_of_week_long_saturday">Samstag</string> + + <string name="day_of_week_medium_sunday">So.</string> + <string name="day_of_week_medium_monday">Mo.</string> + <string name="day_of_week_medium_tuesday">Di.</string> + <string name="day_of_week_medium_wednesday">Mi.</string> + <string name="day_of_week_medium_thursday">Do.</string> + <string name="day_of_week_medium_friday">Fr.</string> + <string name="day_of_week_medium_saturday">Sa.</string> + + <string name="day_of_week_short_sunday">So.</string> + <string name="day_of_week_short_monday">Mo.</string> + <string name="day_of_week_short_tuesday">Di.</string> + <string name="day_of_week_short_wednesday">Mi.</string> + <string name="day_of_week_short_thursday">Do.</string> + <string name="day_of_week_short_friday">Fr.</string> + <string name="day_of_week_short_saturday">Sa.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">vorm.</string> + <string name="pm">nachm.</string> + <string name="yesterday">Gestern</string> + <string name="today">Heute</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 674c64b91c4e..dfb454991aa4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Ermöglicht einer Anwendung, die aktuelle Konfiguration zu ändern, etwa das Gebietsschema oder die Schriftgröße."</string> <string name="permlab_restartPackages">"Andere Anwendungen neu starten"</string> <string name="permdesc_restartPackages">"Ermöglicht einer Anwendung, den Neustart anderer Anwendungen zu erzwingen."</string> - <string name="permlab_setProcessForeground">"Beenden nicht zulassen"</string> - <string name="permdesc_setProcessForeground">"Ermöglicht einer Anwendung, beliebige Prozesse im Vordergrund auszuführen, damit diese nicht beendet werden können. Sollte nicht für normale Anwendungen benötigt werden."</string> <string name="permlab_forceBack">"Schließen von Anwendung erzwingen"</string> <string name="permdesc_forceBack">"Ermöglicht einer Anwendung, alle Aktivitäten, die im Vordergrund ablaufen, zu beenden und in den Hintergrund zu schieben. Sollte nicht für normale Anwendungen benötigt werden."</string> <string name="permlab_dump">"Systeminternen Status abrufen"</string> <string name="permdesc_dump">"Ermöglicht einer Anwendung, den internen Status des Systems abzurufen. Schädliche Anwendungen rufen hierbei möglicherweise eine Vielzahl an privaten und geschützten Daten ab, die Sie in der Regel nicht benötigen würden."</string> - <string name="permlab_addSystemService">"systemnahe Dienste veröffentlichen"</string> - <string name="permdesc_addSystemService">"Ermöglicht der Anwendung, ihre eigenen systemnahen Dienste anzubieten. Schädliche Anwendungen könnten in das System eindringen und darin befindliche Daten stehlen oder manipulieren."</string> <string name="permlab_runSetActivityWatcher">"Start von Anwendungen überwachen und steuern"</string> <string name="permdesc_runSetActivityWatcher">"Ermöglicht der Anwendung, den Start von Systemaktivitäten zu überwachen und zu steuern. Schädliche Anwendungen können so das gesamte System beeinträchtigen. Diese Berechtigung wird nur zu Entwicklungszwecken und nie für die normale Telefonnutzung benötigt."</string> <string name="permlab_broadcastPackageRemoved">"Broadcast ohne Paket senden"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Ermöglicht einer Anwendung, die maximale Anzahl an laufenden Prozessen zu steuern. Wird nicht für normale Anwendungen benötigt."</string> <string name="permlab_setAlwaysFinish">"alle Anwendungen im Hintergrund schließen"</string> <string name="permdesc_setAlwaysFinish">"Überlässt einer Anwendung die Entscheidung, ob Aktivitäten beendet werden, sobald Sie in den Hintergrund rücken. Wird nicht für normale Anwendungen benötigt."</string> - <string name="permlab_fotaUpdate">"System-Updates automatisch installieren"</string> - <string name="permdesc_fotaUpdate">"Ermöglicht einer Anwendung, Benachrichtigungen zu ausstehenden System-Updates zu erhalten und deren Installation einzuleiten. Schädliche Anwendungen können so das System durch nicht autorisierte Updates beschädigen oder in den Update-Prozess eingreifen."</string> <string name="permlab_batteryStats">"Akku-Daten ändern"</string> <string name="permdesc_batteryStats">"Ermöglicht die Änderung von gesammelten Akku-Daten. Nicht für normale Anwendungen vorgesehen."</string> <string name="permlab_internalSystemWindow">"nicht autorisierte Fenster anzeigen"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Passwort"</string> <string name="lockscreen_glogin_submit_button">"Anmelden"</string> <string name="lockscreen_glogin_invalid_input">"Ungültiger Nutzername oder ungültiges Passwort."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Benachrichtigungen löschen"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"Enter"</string> <string name="menu_delete_shortcut_label">"löschen"</string> <string name="search_go">"Suche"</string> - <string name="today">"Heute"</string> - <string name="yesterday">"Gestern"</string> - <string name="tomorrow">"Morgen"</string> <string name="oneMonthDurationPast">"Vor 1 Monat"</string> <string name="beforeOneMonthDurationPast">"Vor mehr als 1 Monat"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"Wochen"</string> <string name="year">"Jahr"</string> <string name="years">"Jahre"</string> - <string name="sunday">"Sonntag"</string> - <string name="monday">"Montag"</string> - <string name="tuesday">"Dienstag"</string> - <string name="wednesday">"Mittwoch"</string> - <string name="thursday">"Donnerstag"</string> - <string name="friday">"Freitag"</string> - <string name="saturday">"Samstag"</string> <string name="every_weekday">"Jeden Wochentag (Mo-Fr)"</string> <string name="daily">"Täglich"</string> <string name="weekly">"Jede Woche am <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Leider ist dieses Video nicht für Streaming auf diesem Gerät gültig."</string> <string name="VideoView_error_text_unknown">"Dieses Video kann leider nicht abgespielt werden."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">".."</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"Mittag"</string> <string name="Noon">"Mittag"</string> <string name="midnight">"Mitternacht"</string> <string name="Midnight">"Mitternacht"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"Sonntag"</string> - <string name="day_of_week_long_monday">"Montag"</string> - <string name="day_of_week_long_tuesday">"Dienstag"</string> - <string name="day_of_week_long_wednesday">"Mittwoch"</string> - <string name="day_of_week_long_thursday">"Donnerstag"</string> - <string name="day_of_week_long_friday">"Freitag"</string> - <string name="day_of_week_long_saturday">"Samstag"</string> - <string name="day_of_week_medium_sunday">"So"</string> - <string name="day_of_week_medium_monday">"Mo"</string> - <string name="day_of_week_medium_tuesday">"Di"</string> - <string name="day_of_week_medium_wednesday">"Mi"</string> - <string name="day_of_week_medium_thursday">"Do"</string> - <string name="day_of_week_medium_friday">"Fr"</string> - <string name="day_of_week_medium_saturday">"Sa"</string> - <string name="day_of_week_short_sunday">"So"</string> - <string name="day_of_week_short_monday">"Mo"</string> - <string name="day_of_week_short_tuesday">"Di"</string> - <string name="day_of_week_short_wednesday">"Mi"</string> - <string name="day_of_week_short_thursday">"Do"</string> - <string name="day_of_week_short_friday">"Fr"</string> - <string name="day_of_week_short_saturday">"Sa"</string> - <string name="day_of_week_shorter_sunday">"So"</string> - <string name="day_of_week_shorter_monday">"März"</string> - <string name="day_of_week_shorter_tuesday">"Di"</string> - <string name="day_of_week_shorter_wednesday">"Mi"</string> - <string name="day_of_week_shorter_thursday">"Do"</string> - <string name="day_of_week_shorter_friday">"Fr"</string> - <string name="day_of_week_shorter_saturday">"Sa"</string> - <string name="day_of_week_shortest_sunday">"Sep"</string> - <string name="day_of_week_shortest_monday">"Mo"</string> - <string name="day_of_week_shortest_tuesday">"Do"</string> - <string name="day_of_week_shortest_wednesday">"Mi"</string> - <string name="day_of_week_shortest_thursday">"Do"</string> - <string name="day_of_week_shortest_friday">"Fr"</string> - <string name="day_of_week_shortest_saturday">"Sa"</string> - <string name="month_long_january">"Januar"</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">"Jan."</string> - <string name="month_medium_february">"Feb."</string> - <string name="month_medium_march">"März"</string> - <string name="month_medium_april">"Apr."</string> - <string name="month_medium_may">"Mai"</string> - <string name="month_medium_june">"Juni"</string> - <string name="month_medium_july">"Juli"</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">"Juli"</string> - <string name="month_shortest_february">"Fr"</string> - <string name="month_shortest_march">"März"</string> - <string name="month_shortest_april">"Apr"</string> - <string name="month_shortest_may">"Mo"</string> - <string name="month_shortest_june">"Juni"</string> - <string name="month_shortest_july">"Juli"</string> - <string name="month_shortest_august">"Aug."</string> - <string name="month_shortest_september">"Sep"</string> - <string name="month_shortest_october">"Okt."</string> - <string name="month_shortest_november">"No"</string> - <string name="month_shortest_december">"Dez."</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Alles auswählen"</string> diff --git a/core/res/res/values-el-rGR/donottranslate-cldr.xml b/core/res/res/values-el-rGR/donottranslate-cldr.xml new file mode 100644 index 000000000000..e8f02fb0d47b --- /dev/null +++ b/core/res/res/values-el-rGR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Ιανουάριος</string> + <string name="month_long_standalone_february">Φεβρουάριος</string> + <string name="month_long_standalone_march">Μάρτιος</string> + <string name="month_long_standalone_april">Απρίλιος</string> + <string name="month_long_standalone_may">Μάιος</string> + <string name="month_long_standalone_june">Ιούνιος</string> + <string name="month_long_standalone_july">Ιούλιος</string> + <string name="month_long_standalone_august">Αύγουστος</string> + <string name="month_long_standalone_september">Σεπτέμβριος</string> + <string name="month_long_standalone_october">Οκτώβριος</string> + <string name="month_long_standalone_november">Νοέμβριος</string> + <string name="month_long_standalone_december">Δεκέμβριος</string> + + <string name="month_long_january">Ιανουαρίου</string> + <string name="month_long_february">Φεβρουαρίου</string> + <string name="month_long_march">Μαρτίου</string> + <string name="month_long_april">Απριλίου</string> + <string name="month_long_may">Μαΐου</string> + <string name="month_long_june">Ιουνίου</string> + <string name="month_long_july">Ιουλίου</string> + <string name="month_long_august">Αυγούστου</string> + <string name="month_long_september">Σεπτεμβρίου</string> + <string name="month_long_october">Οκτωβρίου</string> + <string name="month_long_november">Νοεμβρίου</string> + <string name="month_long_december">Δεκεμβρίου</string> + + <string name="month_medium_january">Ιαν</string> + <string name="month_medium_february">Φεβ</string> + <string name="month_medium_march">Μαρ</string> + <string name="month_medium_april">Απρ</string> + <string name="month_medium_may">Μαϊ</string> + <string name="month_medium_june">Ιουν</string> + <string name="month_medium_july">Ιουλ</string> + <string name="month_medium_august">Αυγ</string> + <string name="month_medium_september">Σεπ</string> + <string name="month_medium_october">Οκτ</string> + <string name="month_medium_november">Νοε</string> + <string name="month_medium_december">Δεκ</string> + + <string name="month_shortest_january">Ι</string> + <string name="month_shortest_february">Φ</string> + <string name="month_shortest_march">Μ</string> + <string name="month_shortest_april">Α</string> + <string name="month_shortest_may">Μ</string> + <string name="month_shortest_june">Ι</string> + <string name="month_shortest_july">Ι</string> + <string name="month_shortest_august">Α</string> + <string name="month_shortest_september">Σ</string> + <string name="month_shortest_october">Ο</string> + <string name="month_shortest_november">Ν</string> + <string name="month_shortest_december">Δ</string> + + <string name="day_of_week_long_sunday">Κυριακή</string> + <string name="day_of_week_long_monday">Δευτέρα</string> + <string name="day_of_week_long_tuesday">Τρίτη</string> + <string name="day_of_week_long_wednesday">Τετάρτη</string> + <string name="day_of_week_long_thursday">Πέμπτη</string> + <string name="day_of_week_long_friday">Παρασκευή</string> + <string name="day_of_week_long_saturday">Σάββατο</string> + + <string name="day_of_week_medium_sunday">Κυρ</string> + <string name="day_of_week_medium_monday">Δευ</string> + <string name="day_of_week_medium_tuesday">Τρι</string> + <string name="day_of_week_medium_wednesday">Τετ</string> + <string name="day_of_week_medium_thursday">Πεμ</string> + <string name="day_of_week_medium_friday">Παρ</string> + <string name="day_of_week_medium_saturday">Σαβ</string> + + <string name="day_of_week_short_sunday">Κυρ</string> + <string name="day_of_week_short_monday">Δευ</string> + <string name="day_of_week_short_tuesday">Τρι</string> + <string name="day_of_week_short_wednesday">Τετ</string> + <string name="day_of_week_short_thursday">Πεμ</string> + <string name="day_of_week_short_friday">Παρ</string> + <string name="day_of_week_short_saturday">Σαβ</string> + + <string name="day_of_week_shortest_sunday">Κ</string> + <string name="day_of_week_shortest_monday">Δ</string> + <string name="day_of_week_shortest_tuesday">Τ</string> + <string name="day_of_week_shortest_wednesday">Τ</string> + <string name="day_of_week_shortest_thursday">Π</string> + <string name="day_of_week_shortest_friday">Π</string> + <string name="day_of_week_shortest_saturday">Σ</string> + + <string name="am">π.μ.</string> + <string name="pm">μ.μ.</string> + <string name="yesterday">Χτες</string> + <string name="today">Σήμερα</string> + <string name="tomorrow">Αύριο</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-l:%M:%S %p %d %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml new file mode 100644 index 000000000000..e8f02fb0d47b --- /dev/null +++ b/core/res/res/values-el/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Ιανουάριος</string> + <string name="month_long_standalone_february">Φεβρουάριος</string> + <string name="month_long_standalone_march">Μάρτιος</string> + <string name="month_long_standalone_april">Απρίλιος</string> + <string name="month_long_standalone_may">Μάιος</string> + <string name="month_long_standalone_june">Ιούνιος</string> + <string name="month_long_standalone_july">Ιούλιος</string> + <string name="month_long_standalone_august">Αύγουστος</string> + <string name="month_long_standalone_september">Σεπτέμβριος</string> + <string name="month_long_standalone_october">Οκτώβριος</string> + <string name="month_long_standalone_november">Νοέμβριος</string> + <string name="month_long_standalone_december">Δεκέμβριος</string> + + <string name="month_long_january">Ιανουαρίου</string> + <string name="month_long_february">Φεβρουαρίου</string> + <string name="month_long_march">Μαρτίου</string> + <string name="month_long_april">Απριλίου</string> + <string name="month_long_may">Μαΐου</string> + <string name="month_long_june">Ιουνίου</string> + <string name="month_long_july">Ιουλίου</string> + <string name="month_long_august">Αυγούστου</string> + <string name="month_long_september">Σεπτεμβρίου</string> + <string name="month_long_october">Οκτωβρίου</string> + <string name="month_long_november">Νοεμβρίου</string> + <string name="month_long_december">Δεκεμβρίου</string> + + <string name="month_medium_january">Ιαν</string> + <string name="month_medium_february">Φεβ</string> + <string name="month_medium_march">Μαρ</string> + <string name="month_medium_april">Απρ</string> + <string name="month_medium_may">Μαϊ</string> + <string name="month_medium_june">Ιουν</string> + <string name="month_medium_july">Ιουλ</string> + <string name="month_medium_august">Αυγ</string> + <string name="month_medium_september">Σεπ</string> + <string name="month_medium_october">Οκτ</string> + <string name="month_medium_november">Νοε</string> + <string name="month_medium_december">Δεκ</string> + + <string name="month_shortest_january">Ι</string> + <string name="month_shortest_february">Φ</string> + <string name="month_shortest_march">Μ</string> + <string name="month_shortest_april">Α</string> + <string name="month_shortest_may">Μ</string> + <string name="month_shortest_june">Ι</string> + <string name="month_shortest_july">Ι</string> + <string name="month_shortest_august">Α</string> + <string name="month_shortest_september">Σ</string> + <string name="month_shortest_october">Ο</string> + <string name="month_shortest_november">Ν</string> + <string name="month_shortest_december">Δ</string> + + <string name="day_of_week_long_sunday">Κυριακή</string> + <string name="day_of_week_long_monday">Δευτέρα</string> + <string name="day_of_week_long_tuesday">Τρίτη</string> + <string name="day_of_week_long_wednesday">Τετάρτη</string> + <string name="day_of_week_long_thursday">Πέμπτη</string> + <string name="day_of_week_long_friday">Παρασκευή</string> + <string name="day_of_week_long_saturday">Σάββατο</string> + + <string name="day_of_week_medium_sunday">Κυρ</string> + <string name="day_of_week_medium_monday">Δευ</string> + <string name="day_of_week_medium_tuesday">Τρι</string> + <string name="day_of_week_medium_wednesday">Τετ</string> + <string name="day_of_week_medium_thursday">Πεμ</string> + <string name="day_of_week_medium_friday">Παρ</string> + <string name="day_of_week_medium_saturday">Σαβ</string> + + <string name="day_of_week_short_sunday">Κυρ</string> + <string name="day_of_week_short_monday">Δευ</string> + <string name="day_of_week_short_tuesday">Τρι</string> + <string name="day_of_week_short_wednesday">Τετ</string> + <string name="day_of_week_short_thursday">Πεμ</string> + <string name="day_of_week_short_friday">Παρ</string> + <string name="day_of_week_short_saturday">Σαβ</string> + + <string name="day_of_week_shortest_sunday">Κ</string> + <string name="day_of_week_shortest_monday">Δ</string> + <string name="day_of_week_shortest_tuesday">Τ</string> + <string name="day_of_week_shortest_wednesday">Τ</string> + <string name="day_of_week_shortest_thursday">Π</string> + <string name="day_of_week_shortest_friday">Π</string> + <string name="day_of_week_shortest_saturday">Σ</string> + + <string name="am">π.μ.</string> + <string name="pm">μ.μ.</string> + <string name="yesterday">Χτες</string> + <string name="today">Σήμερα</string> + <string name="tomorrow">Αύριο</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-l:%M:%S %p %d %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml new file mode 100644 index 000000000000..9811b68e0aa1 --- /dev/null +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-e/%m/%Y</string> + <string name="numeric_date_format">d/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%d/%m/%Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s/%2$s, %5$s - %8$s/%7$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s - %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 9da879b34a16..3de378bee211 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -314,10 +314,6 @@ <skip /> <!-- no translation found for permdesc_setAlwaysFinish (2437195869854312148) --> <skip /> - <!-- no translation found for permlab_fotaUpdate (1813039882829307079) --> - <skip /> - <!-- no translation found for permdesc_fotaUpdate (2544137712607584763) --> - <skip /> <!-- no translation found for permlab_batteryStats (1598947993704535568) --> <skip /> <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> @@ -710,7 +706,6 @@ <!-- no translation found for lockscreen_glogin_invalid_input (4881057177478491580) --> <skip /> <!-- no translation found for status_bar_time_format (2168573805413119180) --> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> <!-- no translation found for hour_minute_ampm (1850330605794978742) --> <skip /> <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> @@ -864,35 +859,24 @@ <!-- from values-de/strings.xml and removal of all the german craziyness--> <skip /> <!-- no translation found for numeric_date (5120078478872821100) --> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for wday1_date1_time1_wday2_date2_time2 (7066878981949584861) --> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> <!-- no translation found for wday1_date1_wday2_date2 (8671068747172261907) --> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> <!-- no translation found for numeric_date (5537215108967329745) --> <skip /> <!-- no translation found for date1_time1_date2_time2 (3645498975775629615) --> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> <!-- no translation found for date1_date2 (377057563556488062) --> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> <!-- no translation found for time1_time2 (3173474242109288305) --> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> <!-- no translation found for time_wday_date (8928955562064570313) --> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for wday_date (8794741400546136975) --> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for time_date (1922644512833014496) --> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for time_wday (1422050241301754712) --> <skip /> <!-- no translation found for full_date_month_first (6011143962222283357) --> <skip /> <!-- no translation found for full_date_day_first (8621594762705478189) --> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> <!-- no translation found for medium_date_month_first (48990963718825728) --> <skip /> <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> <skip /> <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> @@ -906,73 +890,43 @@ <!-- no translation found for Midnight (1260172107848123187) --> <skip /> <!-- no translation found for month_day (3356633704511426364) --> - <string name="month_day">"<xliff:g id="day" example="9">%-d</xliff:g> <xliff:g id="month" example="October">%B</xliff:g>"</string> <!-- no translation found for month (3017405760734206414) --> <skip /> <!-- no translation found for month_day_year (2435948225709176752) --> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for month_year (6228414124777343135) --> <skip /> <!-- no translation found for time_of_day (8375993139317154157) --> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> <!-- no translation found for date_and_time (9197690194373107109) --> <skip /> <!-- no translation found for same_year_md1_md2 (9199324363135981317) --> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_md1_wday2_md2 (6006392413355305178) --> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for date_and_time (353898423108629694) --> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for same_year_mdy1_mdy2 (1576657593937827090) --> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_mdy1_wday2_mdy2 (9135935796468891580) --> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> <!-- no translation found for same_year_md1_time1_md2_time2 (2172964106375558081) --> - <string name="same_year_md1_time1_md2_time2">" <xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_md1_time1_wday2_md2_time2 (1702879534101786310) --> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_year_mdy1_time1_mdy2_time2 (2476443311723358767) --> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_mdy1_time1_wday2_mdy2_time2 (1564837340334069879) --> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_md1_md2 (8908376522875100300) --> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_md1_wday2_md2 (3239690882018292077) --> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for numeric_mdy1_mdy2 (8883797176939233525) --> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_mdy1_wday2_mdy2 (4150475769255828954) --> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for numeric_md1_time1_md2_time2 (3624746590607741419) --> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_md1_time1_wday2_md2_time2 (4258040955467298134) --> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_mdy1_time1_mdy2_time2 (3598215409314517987) --> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_mdy1_time1_wday2_mdy2_time2 (264076937155877259) --> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_md1_md2 (2393563617438036111) --> - <string name="same_month_md1_md2">"<xliff:g id="DAY1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="DAY2" example="3">%8$s</xliff:g> <xliff:g id="MONTH1" example="Oct">%2$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_md1_wday2_md2 (1208946773794057819) --> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for same_month_mdy1_mdy2 (3713236637869030492) --> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="DAY2" example="3">%8$s</xliff:g> <xliff:g id="MONTH1" example="Oct">%2$s</xliff:g> <xliff:g id="YEAR2" example="2007">%9$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_mdy1_wday2_mdy2 (389638922479870472) --> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for same_month_md1_time1_md2_time2 (7477075526337542685) --> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_md1_time1_wday2_md2_time2 (3516978303779391173) --> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_mdy1_time1_mdy2_time2 (7320410992514057310) --> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_mdy1_time1_wday2_mdy2_time2 (1332950588774239228) --> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for abbrev_month_day_year (5767271534015320250) --> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for abbrev_month_year (8058929633673942490) --> <skip /> <!-- no translation found for abbrev_month_day (458867920693482757) --> - <string name="abbrev_month_day">"<xliff:g id="day" example="31">%-d</xliff:g> <xliff:g id="month" example="Oct">%b</xliff:g>"</string> <!-- no translation found for abbrev_month (1674509986330181349) --> <skip /> <!-- no translation found for day_of_week_long_sunday (9057662850446501884) --> diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml new file mode 100644 index 000000000000..1e250c739b73 --- /dev/null +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%Y-%m-%d</string> + <string name="numeric_date_format">yyyy-MM-dd</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%B %-e, %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%Y-%m-%d, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%Y-%m-%d</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s - %7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s-%3$s - %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s - %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s - %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %4$s-%2$s-%3$s, %5$s - %6$s, %9$s-%7$s-%8$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s-%3$s, %5$s - %7$s-%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s-%3$s, %5$s - %6$s, %7$s-%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%4$s-%2$s-%3$s, %5$s - %9$s-%7$s-%8$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s - %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s - %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %2$s %3$s - %6$s, %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s - %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s - %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s - %6$s, %7$s %8$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s - %6$s, %7$s %8$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s - %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s - %7$s %8$s, %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s - %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s - %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s, %4$s - %6$s, %7$s %8$s, %9$s</string> + <string name="same_month_md1_md2">%2$s %3$s-%8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %2$s %3$s - %6$s, %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%2$s %3$s - %7$s %8$s, %9$s</string> + <string name="same_month_mdy1_mdy2">%2$s %3$s-%8$s, %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s - %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml new file mode 100644 index 000000000000..0e3e035b7587 --- /dev/null +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%-e %b %Y, %H:%M:%S</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s/%2$s, %5$s - %8$s/%7$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s %2$s, %3$s - %4$s %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml new file mode 100644 index 000000000000..2e59dcfeaffa --- /dev/null +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">a.m.</string> + <string name="pm">p.m.</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%-e %b %Y, %H:%M:%S</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s/%2$s, %5$s - %8$s/%7$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s %2$s, %3$s - %4$s %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml new file mode 100644 index 000000000000..e39a59a84019 --- /dev/null +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%d-%b-%Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%d-%b-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s/%2$s/%4$s, %5$s - %6$s %8$s/%7$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s/%2$s, %5$s - %8$s/%7$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s %3$s/%2$s, %5$s - %6$s %8$s/%7$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s %2$s, %3$s - %4$s %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml new file mode 100644 index 000000000000..3a8b50bdb9f5 --- /dev/null +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-e/%m/%Y</string> + <string name="numeric_date_format">d/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-e/%m/%Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%-e/%m/%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s/%2$s, %5$s - %8$s/%7$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s - %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s, %5$s - %8$s %7$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rSG/donottranslate-cldr.xml b/core/res/res/values-en-rSG/donottranslate-cldr.xml new file mode 100644 index 000000000000..286cc0e554f2 --- /dev/null +++ b/core/res/res/values-en-rSG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-m/%-e/%Y</string> + <string name="numeric_date_format">M/d/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%B %-e, %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%b %-e, %Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%b %-e, %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s – %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s/%3$s – %6$s, %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%2$s/%3$s/%4$s – %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s/%3$s, %5$s – %7$s/%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s – %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s – %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s – %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s</string> + <string name="same_month_md1_md2">%2$s %3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> + <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rSG/strings.xml b/core/res/res/values-en-rSG/strings.xml index 6850a5d2835b..2ec6b0b02c48 100644 --- a/core/res/res/values-en-rSG/strings.xml +++ b/core/res/res/values-en-rSG/strings.xml @@ -314,10 +314,6 @@ <skip /> <!-- no translation found for permdesc_setAlwaysFinish (2437195869854312148) --> <skip /> - <!-- no translation found for permlab_fotaUpdate (1813039882829307079) --> - <skip /> - <!-- no translation found for permdesc_fotaUpdate (2544137712607584763) --> - <skip /> <!-- no translation found for permlab_batteryStats (1598947993704535568) --> <skip /> <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> @@ -710,7 +706,6 @@ <!-- no translation found for lockscreen_glogin_invalid_input (4881057177478491580) --> <skip /> <!-- no translation found for status_bar_time_format (2168573805413119180) --> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> <!-- no translation found for hour_minute_ampm (1850330605794978742) --> <skip /> <!-- no translation found for hour_minute_cap_ampm (1122840227537374196) --> @@ -863,33 +858,21 @@ <skip /> <!-- copied from values-de/strings.xml with the crazyness of the . removed--> <!-- no translation found for wday1_date1_time1_wday2_date2_time2 (7066878981949584861) --> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> <!-- no translation found for wday1_date1_wday2_date2 (8671068747172261907) --> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> <!-- no translation found for numeric_date (5537215108967329745) --> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for date1_time1_date2_time2 (3645498975775629615) --> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> <!-- no translation found for date1_date2 (377057563556488062) --> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> <!-- no translation found for time1_time2 (3173474242109288305) --> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> <!-- no translation found for time_wday_date (8928955562064570313) --> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for wday_date (8794741400546136975) --> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for time_date (1922644512833014496) --> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> <!-- no translation found for time_wday (1422050241301754712) --> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> <!-- no translation found for full_date_month_first (6011143962222283357) --> <skip /> <!-- no translation found for full_date_day_first (8621594762705478189) --> - <string name="full_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> <!-- no translation found for medium_date_month_first (48990963718825728) --> <skip /> <!-- no translation found for medium_date_day_first (2898992016440387123) --> - <string name="medium_date_day_first">"<xliff:g id="DAY">dd</xliff:g> <xliff:g id="MONTH">MMM</xliff:g> <xliff:g id="YEAR">yyyy</xliff:g>"</string> <!-- no translation found for twelve_hour_time_format (6015557937879492156) --> <skip /> <!-- no translation found for twenty_four_hour_time_format (5176807998669709535) --> @@ -903,71 +886,42 @@ <!-- no translation found for Midnight (1260172107848123187) --> <skip /> <!-- no translation found for month_day (3356633704511426364) --> - <string name="month_day">"<xliff:g id="day" example="9">%-d</xliff:g> <xliff:g id="month" example="October">%B</xliff:g>"</string> <!-- no translation found for month (3017405760734206414) --> <skip /> <!-- no translation found for month_day_year (2435948225709176752) --> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for month_year (6228414124777343135) --> <skip /> <!-- no translation found for time_of_day (8375993139317154157) --> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> <!-- no translation found for date_and_time (9197690194373107109) --> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for same_year_md1_md2 (9199324363135981317) --> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_md1_wday2_md2 (6006392413355305178) --> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for same_year_mdy1_mdy2 (1576657593937827090) --> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_mdy1_wday2_mdy2 (9135935796468891580) --> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> <!-- no translation found for same_year_md1_time1_md2_time2 (2172964106375558081) --> <skip /> <!-- no translation found for same_year_wday1_md1_time1_wday2_md2_time2 (1702879534101786310) --> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_year_mdy1_time1_mdy2_time2 (2476443311723358767) --> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_year_wday1_mdy1_time1_wday2_mdy2_time2 (1564837340334069879) --> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_md1_md2 (8908376522875100300) --> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_md1_wday2_md2 (3239690882018292077) --> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for numeric_mdy1_mdy2 (8883797176939233525) --> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_mdy1_wday2_mdy2 (4150475769255828954) --> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for numeric_md1_time1_md2_time2 (3624746590607741419) --> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_md1_time1_wday2_md2_time2 (4258040955467298134) --> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_mdy1_time1_mdy2_time2 (3598215409314517987) --> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for numeric_wday1_mdy1_time1_wday2_mdy2_time2 (264076937155877259) --> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_md1_md2 (2393563617438036111) --> - <string name="same_month_md1_md2">"<xliff:g id="DAY1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="DAY2" example="3">%8$s</xliff:g> <xliff:g id="MONTH1" example="Oct">%2$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_md1_wday2_md2 (1208946773794057819) --> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> <!-- no translation found for same_month_mdy1_mdy2 (3713236637869030492) --> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="DAY2" example="3">%8$s</xliff:g> <xliff:g id="MONTH1" example="Oct">%2$s</xliff:g> <xliff:g id="YEAR2" example="2007">%9$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_mdy1_wday2_mdy2 (389638922479870472) --> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> <!-- no translation found for same_month_md1_time1_md2_time2 (7477075526337542685) --> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_md1_time1_wday2_md2_time2 (3516978303779391173) --> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_mdy1_time1_mdy2_time2 (7320410992514057310) --> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for same_month_wday1_mdy1_time1_wday2_mdy2_time2 (1332950588774239228) --> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> <!-- no translation found for abbrev_month_day_year (5767271534015320250) --> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for abbrev_month_year (8058929633673942490) --> <skip /> <!-- no translation found for abbrev_month_day (458867920693482757) --> - <string name="abbrev_month_day">"<xliff:g id="day" example="31">%-d</xliff:g> <xliff:g id="month" example="Oct">%b</xliff:g>"</string> <!-- no translation found for abbrev_month (1674509986330181349) --> <skip /> <!-- no translation found for day_of_week_long_sunday (9057662850446501884) --> diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml new file mode 100644 index 000000000000..286cc0e554f2 --- /dev/null +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-m/%-e/%Y</string> + <string name="numeric_date_format">M/d/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%B %-e, %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%b %-e, %Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%b %-e, %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s – %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s/%3$s – %6$s, %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%2$s/%3$s/%4$s – %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s/%3$s, %5$s – %7$s/%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s – %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s – %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s – %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s</string> + <string name="same_month_md1_md2">%2$s %3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> + <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-en-rUS/strings.xml b/core/res/res/values-en-rUS/strings.xml index b9df983a3fc3..05f30fca350f 100644 --- a/core/res/res/values-en-rUS/strings.xml +++ b/core/res/res/values-en-rUS/strings.xml @@ -314,10 +314,6 @@ <skip /> <!-- no translation found for permdesc_setAlwaysFinish (2437195869854312148) --> <skip /> - <!-- no translation found for permlab_fotaUpdate (1813039882829307079) --> - <skip /> - <!-- no translation found for permdesc_fotaUpdate (2544137712607584763) --> - <skip /> <!-- no translation found for permlab_batteryStats (1598947993704535568) --> <skip /> <!-- no translation found for permdesc_batteryStats (6247598531831307989) --> diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml new file mode 100644 index 000000000000..2e2d6080d71c --- /dev/null +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%Y/%m/%d</string> + <string name="numeric_date_format">yyyy/MM/dd</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%d %b %Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%d %b %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%d %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s - %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %2$s/%3$s - %6$s %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%4$s/%2$s/%3$s - %9$s/%7$s/%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %4$s/%2$s/%3$s - %6$s %9$s/%7$s/%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %4$s/%2$s/%3$s, %5$s - %6$s %9$s/%7$s/%8$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s/%3$s, %5$s - %7$s/%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s %2$s/%3$s, %5$s - %6$s %7$s/%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%4$s/%2$s/%3$s, %5$s - %9$s/%7$s/%8$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s %2$s, %3$s - %4$s %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s - %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s - %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s - %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s - %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-es-rES/donottranslate-cldr.xml b/core/res/res/values-es-rES/donottranslate-cldr.xml new file mode 100644 index 000000000000..c1dc58b815d0 --- /dev/null +++ b/core/res/res/values-es-rES/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">enero</string> + <string name="month_long_standalone_february">febrero</string> + <string name="month_long_standalone_march">marzo</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">mayo</string> + <string name="month_long_standalone_june">junio</string> + <string name="month_long_standalone_july">julio</string> + <string name="month_long_standalone_august">agosto</string> + <string name="month_long_standalone_september">septiembre</string> + <string name="month_long_standalone_october">octubre</string> + <string name="month_long_standalone_november">noviembre</string> + <string name="month_long_standalone_december">diciembre</string> + + <string name="month_long_january">enero</string> + <string name="month_long_february">febrero</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">mayo</string> + <string name="month_long_june">junio</string> + <string name="month_long_july">julio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">septiembre</string> + <string name="month_long_october">octubre</string> + <string name="month_long_november">noviembre</string> + <string name="month_long_december">diciembre</string> + + <string name="month_medium_january">ene</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">abr</string> + <string name="month_medium_may">may</string> + <string name="month_medium_june">jun</string> + <string name="month_medium_july">jul</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">sep</string> + <string name="month_medium_october">oct</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">E</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">lunes</string> + <string name="day_of_week_long_tuesday">martes</string> + <string name="day_of_week_long_wednesday">miércoles</string> + <string name="day_of_week_long_thursday">jueves</string> + <string name="day_of_week_long_friday">viernes</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mié</string> + <string name="day_of_week_medium_thursday">jue</string> + <string name="day_of_week_medium_friday">vie</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mié</string> + <string name="day_of_week_short_thursday">jue</string> + <string name="day_of_week_short_friday">vie</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">a.m.</string> + <string name="pm">p.m.</string> + <string name="yesterday">ayer</string> + <string name="today">hoy</string> + <string name="tomorrow">mañana</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s – %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s-%2$s – %10$s %6$s, %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s – %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s – %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s – %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml new file mode 100644 index 000000000000..d6d89540af91 --- /dev/null +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">enero</string> + <string name="month_long_standalone_february">febrero</string> + <string name="month_long_standalone_march">marzo</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">mayo</string> + <string name="month_long_standalone_june">junio</string> + <string name="month_long_standalone_july">julio</string> + <string name="month_long_standalone_august">agosto</string> + <string name="month_long_standalone_september">septiembre</string> + <string name="month_long_standalone_october">octubre</string> + <string name="month_long_standalone_november">noviembre</string> + <string name="month_long_standalone_december">diciembre</string> + + <string name="month_long_january">enero</string> + <string name="month_long_february">febrero</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">mayo</string> + <string name="month_long_june">junio</string> + <string name="month_long_july">julio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">septiembre</string> + <string name="month_long_october">octubre</string> + <string name="month_long_november">noviembre</string> + <string name="month_long_december">diciembre</string> + + <string name="month_medium_january">ene</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">abr</string> + <string name="month_medium_may">may</string> + <string name="month_medium_june">jun</string> + <string name="month_medium_july">jul</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">sep</string> + <string name="month_medium_october">oct</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">E</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">lunes</string> + <string name="day_of_week_long_tuesday">martes</string> + <string name="day_of_week_long_wednesday">miércoles</string> + <string name="day_of_week_long_thursday">jueves</string> + <string name="day_of_week_long_friday">viernes</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mié</string> + <string name="day_of_week_medium_thursday">jue</string> + <string name="day_of_week_medium_friday">vie</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mié</string> + <string name="day_of_week_short_thursday">jue</string> + <string name="day_of_week_short_friday">vie</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">a.m.</string> + <string name="pm">p.m.</string> + <string name="yesterday">ayer</string> + <string name="today">hoy</string> + <string name="tomorrow">mañana</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-m/%-e/%Y</string> + <string name="numeric_date_format">M/d/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-l:%M:%S %p %b %-e, %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%b %-e, %Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e de %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b de %Y</string> + <string name="time1_time2">%1$s a el %2$s</string> + <string name="date1_date2">%2$s a el %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s - %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %2$s/%3$s - %6$s %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%2$s/%3$s/%4$s - %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %2$s/%3$s/%4$s - %6$s %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %2$s/%3$s/%4$s a el %10$s %6$s %7$s/%8$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s/%3$s a el %10$s %7$s/%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s/%3$s a el %10$s %6$s %7$s/%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %2$s/%3$s/%4$s a el %10$s %7$s/%8$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s a el %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s a el %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s a el %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s a el %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s de %2$s a el %6$s %8$s de %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s a el %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s a el %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s de %2$s a el %10$s %6$s %8$s de %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s de %2$s a el %10$s %6$s %8$s de %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s a el %10$s %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s a el %10$s %8$s de %7$s de %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s de %2$s de %4$s a el %10$s %6$s %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s de %2$s de %4$s a el %10$s %6$s %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s de %4$s a el %6$s %8$s de %7$s de %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s de %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s de %2$s a el %6$s %8$s de %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s de %2$s al %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s al %6$s %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 7b60a39f042b..84435aa8dee8 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -178,14 +178,10 @@ <string name="permdesc_changeConfiguration">"Admite una aplicación para cambiar la configuración actual, como el tamaño de fuente local o general."</string> <string name="permlab_restartPackages">"reiniciar otras aplicaciones"</string> <string name="permdesc_restartPackages">"Admite una aplicación que reinicia otras aplicaciones por la fuerza."</string> - <string name="permlab_setProcessForeground">"impedir la detención"</string> - <string name="permdesc_setProcessForeground">"Admite una aplicación que ejecuta cualquier tipo de proceso en primer plano, de manera que no se pueda suprimir. Se debe evitar utilizarlo en aplicaciones normales."</string> <string name="permlab_forceBack">"provocar que la aplicación se acerque"</string> <string name="permdesc_forceBack">"Admite una aplicación que provoca que cualquier actividad del fondo se acerque y vuelva a alejarse. Se debe evitar utilizarlo en aplicaciones normales."</string> <string name="permlab_dump">"recuperar el estado interno del sistema"</string> <string name="permdesc_dump">"Admite que la aplicación recupere el estado interno del sistema. Las aplicaciones maliciosas pueden recuperar una gran variedad de información privada y segura que normalmente nunca necesitaría."</string> - <string name="permlab_addSystemService">"publicar servicios de bajo nivel"</string> - <string name="permdesc_addSystemService">"Admite que la aplicación publique sus propios servicios del sistema de bajo nivel. Las aplicaciones maliciosas pueden apropiarse del sistema y robar o corromper cualquiera de sus datos."</string> <string name="permlab_runSetActivityWatcher">"verificar y controlar todos los lanzamientos de actividades"</string> <string name="permdesc_runSetActivityWatcher">"Admite una aplicación que verifica y controla el lanzamiento de actividades por parte del sistema. Las aplicaciones maliciosas pueden comprometer totalmente al sistema. Este permiso sólo es necesario para el desarrollo, nunca para el uso normal del teléfono."</string> <string name="permlab_broadcastPackageRemoved">"enviar emisión de paquete eliminado"</string> @@ -198,8 +194,6 @@ <string name="permdesc_setProcessLimit">"Admite una aplicación que controla la cantidad máxima de procesos que se ejecutarán. No se utiliza nunca en aplicaciones normales."</string> <string name="permlab_setAlwaysFinish">"cerrar todas las aplicaciones del fondo"</string> <string name="permdesc_setAlwaysFinish">"Admite una aplicación que controla si las actividades siempre finalizan cuando van al fondo. No se utiliza nunca en aplicaciones normales."</string> - <string name="permlab_fotaUpdate">"instalar automáticamente las actualizaciones del sistema"</string> - <string name="permdesc_fotaUpdate">"Admite una aplicación que recibe notificaciones sobre las actualizaciones pendientes del sistema y activa su instalación. Las aplicaciones maliciosas pueden utilizarlo para corromper el sistema con actualizaciones no autorizadas o, en general, para interferir en el proceso de actualización."</string> <string name="permlab_batteryStats">"modificar la estadística de la batería"</string> <string name="permdesc_batteryStats">"Admite la modificación de estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben utilizarlo."</string> <string name="permlab_internalSystemWindow">"mostrar ventanas no autorizadas"</string> @@ -436,9 +430,6 @@ <string name="lockscreen_glogin_password_hint">"Contraseña"</string> <string name="lockscreen_glogin_submit_button">"Inicia sesión"</string> <string name="lockscreen_glogin_invalid_input">"Nombre de usuario o contraseña incorrecta."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Borrar notificaciones"</string> @@ -470,9 +461,6 @@ <string name="menu_enter_shortcut_label">"ingresar"</string> <string name="menu_delete_shortcut_label">"borrar"</string> <string name="search_go">"Buscar"</string> - <string name="today">"Hoy"</string> - <string name="yesterday">"Ayer"</string> - <string name="tomorrow">"Mañana"</string> <string name="oneMonthDurationPast">"hace 1 mes"</string> <string name="beforeOneMonthDurationPast">"Anterior a 1 mes atrás"</string> <plurals name="num_seconds_ago"> @@ -554,13 +542,6 @@ <string name="weeks">"semanas"</string> <string name="year">"año"</string> <string name="years">"años"</string> - <string name="sunday">"Domingo"</string> - <string name="monday">"Lunes"</string> - <string name="tuesday">"Martes"</string> - <string name="wednesday">"Miércoles"</string> - <string name="thursday">"Jueves"</string> - <string name="friday">"Viernes"</string> - <string name="saturday">"Sábado"</string> <string name="every_weekday">"Los días de semana (lunes a viernes)"</string> <string name="daily">"Diariamente"</string> <string name="weekly">"Semanalmente el día <xliff:g id="DAY">%s</xliff:g>"</string> @@ -570,137 +551,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Lo sentimos, este video no es válido para las transmisiones a este dispositivo."</string> <string name="VideoView_error_text_unknown">"Lo sentimos, no se puede reproducir este video."</string> <string name="VideoView_error_button">"Aceptar"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"mediodía"</string> <string name="Noon">"Mediodía"</string> <string name="midnight">"medianoche"</string> <string name="Midnight">"Medianoche"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"Domingo"</string> - <string name="day_of_week_long_monday">"Lunes"</string> - <string name="day_of_week_long_tuesday">"Martes"</string> - <string name="day_of_week_long_wednesday">"Miércoles"</string> - <string name="day_of_week_long_thursday">"Jueves"</string> - <string name="day_of_week_long_friday">"Viernes"</string> - <string name="day_of_week_long_saturday">"Sábado"</string> - <string name="day_of_week_medium_sunday">"Dom."</string> - <string name="day_of_week_medium_monday">"Lun."</string> - <string name="day_of_week_medium_tuesday">"Mar."</string> - <string name="day_of_week_medium_wednesday">"Mié."</string> - <string name="day_of_week_medium_thursday">"Jue."</string> - <string name="day_of_week_medium_friday">"Vie."</string> - <string name="day_of_week_medium_saturday">"Sáb."</string> - <string name="day_of_week_short_sunday">"Dom."</string> - <string name="day_of_week_short_monday">"Lun."</string> - <string name="day_of_week_short_tuesday">"Mar."</string> - <string name="day_of_week_short_wednesday">"Nosotros"</string> - <string name="day_of_week_short_thursday">"Jue."</string> - <string name="day_of_week_short_friday">"V"</string> - <string name="day_of_week_short_saturday">"Sáb."</string> - <string name="day_of_week_shorter_sunday">"Dom."</string> - <string name="day_of_week_shorter_monday">"L"</string> - <string name="day_of_week_shorter_tuesday">"Mar."</string> - <string name="day_of_week_shorter_wednesday">"M"</string> - <string name="day_of_week_shorter_thursday">"Jue."</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"Sáb."</string> - <string name="day_of_week_shortest_sunday">"D"</string> - <string name="day_of_week_shortest_monday">"L"</string> - <string name="day_of_week_shortest_tuesday">"Mar."</string> - <string name="day_of_week_shortest_wednesday">"M"</string> - <string name="day_of_week_shortest_thursday">"Jue."</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"D"</string> - <string name="month_long_january">"Enero"</string> - <string name="month_long_february">"Febrero"</string> - <string name="month_long_march">"Marzo"</string> - <string name="month_long_april">"Abril"</string> - <string name="month_long_may">"Mayo"</string> - <string name="month_long_june">"Junio"</string> - <string name="month_long_july">"Julio"</string> - <string name="month_long_august">"Agosto"</string> - <string name="month_long_september">"Septiembre"</string> - <string name="month_long_october">"Octubre"</string> - <string name="month_long_november">"Noviembre"</string> - <string name="month_long_december">"Diciembre"</string> - <string name="month_medium_january">"Ene."</string> - <string name="month_medium_february">"Feb."</string> - <string name="month_medium_march">"Mar."</string> - <string name="month_medium_april">"Abr."</string> - <string name="month_medium_may">"Mayo"</string> - <string name="month_medium_june">"Jun."</string> - <string name="month_medium_july">"Jul."</string> - <string name="month_medium_august">"Ago."</string> - <string name="month_medium_september">"Sep."</string> - <string name="month_medium_october">"Oct."</string> - <string name="month_medium_november">"Nov."</string> - <string name="month_medium_december">"Dic."</string> - <string name="month_shortest_january">"E"</string> - <string name="month_shortest_february">"V"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"E"</string> - <string name="month_shortest_july">"J"</string> - <string name="month_shortest_august">"Ago."</string> - <string name="month_shortest_september">"D"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"Dic."</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Seleccionar todos"</string> diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml new file mode 100644 index 000000000000..c1dc58b815d0 --- /dev/null +++ b/core/res/res/values-es/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">enero</string> + <string name="month_long_standalone_february">febrero</string> + <string name="month_long_standalone_march">marzo</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">mayo</string> + <string name="month_long_standalone_june">junio</string> + <string name="month_long_standalone_july">julio</string> + <string name="month_long_standalone_august">agosto</string> + <string name="month_long_standalone_september">septiembre</string> + <string name="month_long_standalone_october">octubre</string> + <string name="month_long_standalone_november">noviembre</string> + <string name="month_long_standalone_december">diciembre</string> + + <string name="month_long_january">enero</string> + <string name="month_long_february">febrero</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">mayo</string> + <string name="month_long_june">junio</string> + <string name="month_long_july">julio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">septiembre</string> + <string name="month_long_october">octubre</string> + <string name="month_long_november">noviembre</string> + <string name="month_long_december">diciembre</string> + + <string name="month_medium_january">ene</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">abr</string> + <string name="month_medium_may">may</string> + <string name="month_medium_june">jun</string> + <string name="month_medium_july">jul</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">sep</string> + <string name="month_medium_october">oct</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">E</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">lunes</string> + <string name="day_of_week_long_tuesday">martes</string> + <string name="day_of_week_long_wednesday">miércoles</string> + <string name="day_of_week_long_thursday">jueves</string> + <string name="day_of_week_long_friday">viernes</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mié</string> + <string name="day_of_week_medium_thursday">jue</string> + <string name="day_of_week_medium_friday">vie</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mié</string> + <string name="day_of_week_short_thursday">jue</string> + <string name="day_of_week_short_friday">vie</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">a.m.</string> + <string name="pm">p.m.</string> + <string name="yesterday">ayer</string> + <string name="today">hoy</string> + <string name="tomorrow">mañana</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s – %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s-%2$s – %10$s %6$s, %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s – %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s – %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s – %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index a9f267a308b1..920ac3e5dcd0 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Permite que una aplicación cambie la configuración actual como, por ejemplo, la configuración local o el tamaño de fuente general."</string> <string name="permlab_restartPackages">"reiniciar otras aplicaciones"</string> <string name="permdesc_restartPackages">"Permite que una aplicación reinicie de forma forzosa otras aplicaciones."</string> - <string name="permlab_setProcessForeground">"impedir su interrupción"</string> - <string name="permdesc_setProcessForeground">"Permite que una aplicación ejecute cualquier proceso en segundo plano, de forma que no se pueda interrumpir. No debería ser necesario nunca para las aplicaciones normales."</string> <string name="permlab_forceBack">"forzar el cierre de la aplicación"</string> <string name="permdesc_forceBack">"Permite que una aplicación fuerce a cualquier actividad en segundo plano a cerrarse y volver a la pantalla anterior. No debería ser necesario nunca para las aplicaciones normales."</string> <string name="permlab_dump">"recuperar estado interno del sistema"</string> <string name="permdesc_dump">"Permite que la aplicación recupere el estado interno del sistema. Las aplicaciones malintencionadas pueden recuperar una amplia variedad de información protegida y privada que normalmente no deberían necesitar."</string> - <string name="permlab_addSystemService">"publicar servicios de nivel inferior"</string> - <string name="permdesc_addSystemService">"Permite que la aplicación publique sus propios servicios de sistema de nivel inferior. Las aplicaciones malintencionadas pueden hacerse con el control del sistema, y robar o dañar los datos contenidos en él."</string> <string name="permlab_runSetActivityWatcher">"supervisar y controlar la ejecución de todas las aplicaciones"</string> <string name="permdesc_runSetActivityWatcher">"Permite que una aplicación supervise y controle la ejecución de las actividades por parte del sistema. Las aplicaciones malintencionadas pueden vulnerar la seguridad del sistema. Este permiso sólo es necesario para tareas de desarrollo, nunca para el uso habitual del teléfono."</string> <string name="permlab_broadcastPackageRemoved">"enviar emisión eliminada de paquete"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Permite que una aplicación controle el número máximo de procesos que se ejecutarán. No es necesario nunca para las aplicaciones normales."</string> <string name="permlab_setAlwaysFinish">"hacer que se cierren todas las aplicaciones en segundo plano"</string> <string name="permdesc_setAlwaysFinish">"Permite que una aplicación controle si las actividades finalizan siempre en cuanto pasan a segundo plano. No es necesario nunca para las aplicaciones normales."</string> - <string name="permlab_fotaUpdate">"instalar actualizaciones del sistema de forma automática"</string> - <string name="permdesc_fotaUpdate">"Permite que una aplicación reciba notificaciones sobre actualizaciones pendientes del sistema e inicie su instalación. Las aplicaciones malintencionadas pueden utilizar este permiso para provocar daños en el sistema con actualizaciones no autorizadas o interferir de forma general en el proceso de actualización."</string> <string name="permlab_batteryStats">"modificar estadísticas de la batería"</string> <string name="permdesc_batteryStats">"Permite la modificación de estadísticas recopiladas sobre la batería. No está destinado al uso por parte de aplicaciones normales."</string> <string name="permlab_internalSystemWindow">"mostrar ventanas no autorizadas"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Contraseña"</string> <string name="lockscreen_glogin_submit_button">"Acceder"</string> <string name="lockscreen_glogin_invalid_input">"Nombre de usuario o contraseña no válido"</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Cerrar notificaciones"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"intro"</string> <string name="menu_delete_shortcut_label">"suprimir"</string> <string name="search_go">"Buscar"</string> - <string name="today">"Hoy"</string> - <string name="yesterday">"Ayer"</string> - <string name="tomorrow">"Mañana"</string> <string name="oneMonthDurationPast">"Hace un mes"</string> <string name="beforeOneMonthDurationPast">"Hace más de un mes"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"semanas"</string> <string name="year">"año"</string> <string name="years">"años"</string> - <string name="sunday">"Domingo"</string> - <string name="monday">"Lunes"</string> - <string name="tuesday">"Martes"</string> - <string name="wednesday">"Miércoles"</string> - <string name="thursday">"Jueves"</string> - <string name="friday">"Viernes"</string> - <string name="saturday">"Sábado"</string> <string name="every_weekday">"Todos los días laborables (Lun-Vie)"</string> <string name="daily">"Diariamente"</string> <string name="weekly">"Semanalmente, el <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Este vídeo no se puede transmitir al dispositivo."</string> <string name="VideoView_error_text_unknown">"Este vídeo no se puede reproducir."</string> <string name="VideoView_error_button">"Aceptar"</string> - <string name="am">"a.m."</string> - <string name="pm">"p.m."</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="MONTH">MMMM</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="MONTH">MMMM</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' de '<xliff:g id="MONTH">MMM</xliff:g>' de '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"mediodía"</string> <string name="Noon">"Mediodía"</string> <string name="midnight">"medianoche"</string> <string name="Midnight">"Medianoche"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> de <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> de <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> de <xliff:g id="MONTH1">%2$s</xliff:g> de <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> de <xliff:g id="MONTH2">%7$s</xliff:g> de <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> de <xliff:g id="MONTH1">%2$s</xliff:g> de <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> de <xliff:g id="MONTH2">%7$s</xliff:g> de <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> de <xliff:g id="MONTH">%b</xliff:g> de <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"Domingo"</string> - <string name="day_of_week_long_monday">"Lunes"</string> - <string name="day_of_week_long_tuesday">"Martes"</string> - <string name="day_of_week_long_wednesday">"Miércoles"</string> - <string name="day_of_week_long_thursday">"Jueves"</string> - <string name="day_of_week_long_friday">"Viernes"</string> - <string name="day_of_week_long_saturday">"Sábado"</string> - <string name="day_of_week_medium_sunday">"Dom"</string> - <string name="day_of_week_medium_monday">"Lun"</string> - <string name="day_of_week_medium_tuesday">"Mar"</string> - <string name="day_of_week_medium_wednesday">"Mié"</string> - <string name="day_of_week_medium_thursday">"Jue"</string> - <string name="day_of_week_medium_friday">"Vie"</string> - <string name="day_of_week_medium_saturday">"Sáb"</string> - <string name="day_of_week_short_sunday">"Do"</string> - <string name="day_of_week_short_monday">"Lu"</string> - <string name="day_of_week_short_tuesday">"Ma"</string> - <string name="day_of_week_short_wednesday">"Mi"</string> - <string name="day_of_week_short_thursday">"Ju"</string> - <string name="day_of_week_short_friday">"Vi"</string> - <string name="day_of_week_short_saturday">"Sá"</string> - <string name="day_of_week_shorter_sunday">"Do"</string> - <string name="day_of_week_shorter_monday">"L"</string> - <string name="day_of_week_shorter_tuesday">"Ma"</string> - <string name="day_of_week_shorter_wednesday">"Mi"</string> - <string name="day_of_week_shorter_thursday">"Ju"</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"S"</string> - <string name="day_of_week_shortest_sunday">"D"</string> - <string name="day_of_week_shortest_monday">"Mz"</string> - <string name="day_of_week_shortest_tuesday">"M"</string> - <string name="day_of_week_shortest_wednesday">"Mi"</string> - <string name="day_of_week_shortest_thursday">"M"</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"D"</string> - <string name="month_long_january">"Enero"</string> - <string name="month_long_february">"Febrero"</string> - <string name="month_long_march">"Marzo"</string> - <string name="month_long_april">"Abril"</string> - <string name="month_long_may">"Mayo"</string> - <string name="month_long_june">"Junio"</string> - <string name="month_long_july">"Julio"</string> - <string name="month_long_august">"Agosto"</string> - <string name="month_long_september">"Septiembre"</string> - <string name="month_long_october">"Octubre"</string> - <string name="month_long_november">"Noviembre"</string> - <string name="month_long_december">"Diciembre"</string> - <string name="month_medium_january">"Ene"</string> - <string name="month_medium_february">"Feb"</string> - <string name="month_medium_march">"Mar"</string> - <string name="month_medium_april">"Abr"</string> - <string name="month_medium_may">"May"</string> - <string name="month_medium_june">"Jun"</string> - <string name="month_medium_july">"Jul"</string> - <string name="month_medium_august">"Ago"</string> - <string name="month_medium_september">"Sep"</string> - <string name="month_medium_october">"Oct"</string> - <string name="month_medium_november">"Nov"</string> - <string name="month_medium_december">"Dic"</string> - <string name="month_shortest_january">"E"</string> - <string name="month_shortest_february">"V"</string> - <string name="month_shortest_march">"Mz"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"My"</string> - <string name="month_shortest_june">"J"</string> - <string name="month_shortest_july">"E"</string> - <string name="month_shortest_august">"Ag"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Seleccionar todo"</string> diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml new file mode 100644 index 000000000000..df3866e72bda --- /dev/null +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">tammikuu</string> + <string name="month_long_standalone_february">helmikuu</string> + <string name="month_long_standalone_march">maaliskuu</string> + <string name="month_long_standalone_april">huhtikuu</string> + <string name="month_long_standalone_may">toukokuu</string> + <string name="month_long_standalone_june">kesäkuu</string> + <string name="month_long_standalone_july">heinäkuu</string> + <string name="month_long_standalone_august">elokuu</string> + <string name="month_long_standalone_september">syyskuu</string> + <string name="month_long_standalone_october">lokakuu</string> + <string name="month_long_standalone_november">marraskuu</string> + <string name="month_long_standalone_december">joulukuu</string> + + <string name="month_long_january">tammikuuta</string> + <string name="month_long_february">helmikuuta</string> + <string name="month_long_march">maaliskuuta</string> + <string name="month_long_april">huhtikuuta</string> + <string name="month_long_may">toukokuuta</string> + <string name="month_long_june">kesäkuuta</string> + <string name="month_long_july">heinäkuuta</string> + <string name="month_long_august">elokuuta</string> + <string name="month_long_september">syyskuuta</string> + <string name="month_long_october">lokakuuta</string> + <string name="month_long_november">marraskuuta</string> + <string name="month_long_december">joulukuuta</string> + + <string name="month_medium_january">tammikuuta</string> + <string name="month_medium_february">helmikuuta</string> + <string name="month_medium_march">maaliskuuta</string> + <string name="month_medium_april">huhtikuuta</string> + <string name="month_medium_may">toukokuuta</string> + <string name="month_medium_june">kesäkuuta</string> + <string name="month_medium_july">heinäkuuta</string> + <string name="month_medium_august">elokuuta</string> + <string name="month_medium_september">syyskuuta</string> + <string name="month_medium_october">lokakuuta</string> + <string name="month_medium_november">marraskuuta</string> + <string name="month_medium_december">joulukuuta</string> + + <string name="month_shortest_january">T</string> + <string name="month_shortest_february">H</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">H</string> + <string name="month_shortest_may">T</string> + <string name="month_shortest_june">K</string> + <string name="month_shortest_july">H</string> + <string name="month_shortest_august">E</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">L</string> + <string name="month_shortest_november">M</string> + <string name="month_shortest_december">J</string> + + <string name="day_of_week_long_sunday">sunnuntaina</string> + <string name="day_of_week_long_monday">maanantaina</string> + <string name="day_of_week_long_tuesday">tiistaina</string> + <string name="day_of_week_long_wednesday">keskiviikkona</string> + <string name="day_of_week_long_thursday">torstaina</string> + <string name="day_of_week_long_friday">perjantaina</string> + <string name="day_of_week_long_saturday">lauantaina</string> + + <string name="day_of_week_medium_sunday">su</string> + <string name="day_of_week_medium_monday">ma</string> + <string name="day_of_week_medium_tuesday">ti</string> + <string name="day_of_week_medium_wednesday">ke</string> + <string name="day_of_week_medium_thursday">to</string> + <string name="day_of_week_medium_friday">pe</string> + <string name="day_of_week_medium_saturday">la</string> + + <string name="day_of_week_short_sunday">su</string> + <string name="day_of_week_short_monday">ma</string> + <string name="day_of_week_short_tuesday">ti</string> + <string name="day_of_week_short_wednesday">ke</string> + <string name="day_of_week_short_thursday">to</string> + <string name="day_of_week_short_friday">pe</string> + <string name="day_of_week_short_saturday">la</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">K</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">ap.</string> + <string name="pm">ip.</string> + <string name="yesterday">eilen</string> + <string name="today">tänään</string> + <string name="tomorrow">huomenna</string> + + <string name="hour_minute_24">%-k.%M</string> + <string name="hour_minute_ampm">%-l.%M %p</string> + <string name="hour_minute_cap_ampm">%-l.%M %^p</string> + <string name="twelve_hour_time_format">h.mm a</string> + <string name="twenty_four_hour_time_format">H.mm</string> + <string name="numeric_date">%-e.%-m.%Y</string> + <string name="numeric_date_format">d.M.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%-k.%M.%S</string> + <string name="date_and_time">%-k.%M.%S %-e.%-m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e.%-m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-b</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%-b %Y</string> + <string name="time1_time2">%1$s–%2$s</string> + <string name="date1_date2">%2$s–%5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s.–%8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s.%2$s. – %6$s %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s–%8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s.%2$s.%4$s – %6$s %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s.%2$s.%4$s–%10$s %6$s %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s.–%10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s.%2$s.–%10$s %6$s %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s–%10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s–%6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s–%4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s–%6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s–%8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s–%6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s–%10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s–%10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s %4$s–%6$s %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.–%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s–%6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s – %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s – %6$s %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr-rBE/donottranslate-cldr.xml b/core/res/res/values-fr-rBE/donottranslate-cldr.xml new file mode 100644 index 000000000000..e1908373a3fe --- /dev/null +++ b/core/res/res/values-fr-rBE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvier</string> + <string name="month_long_standalone_february">février</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">avril</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">juin</string> + <string name="month_long_standalone_july">juillet</string> + <string name="month_long_standalone_august">août</string> + <string name="month_long_standalone_september">septembre</string> + <string name="month_long_standalone_october">octobre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">décembre</string> + + <string name="month_long_january">janvier</string> + <string name="month_long_february">février</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">avril</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">juin</string> + <string name="month_long_july">juillet</string> + <string name="month_long_august">août</string> + <string name="month_long_september">septembre</string> + <string name="month_long_october">octobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">décembre</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">févr.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">avr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juin</string> + <string name="month_medium_july">juil.</string> + <string name="month_medium_august">août</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">déc.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">dimanche</string> + <string name="day_of_week_long_monday">lundi</string> + <string name="day_of_week_long_tuesday">mardi</string> + <string name="day_of_week_long_wednesday">mercredi</string> + <string name="day_of_week_long_thursday">jeudi</string> + <string name="day_of_week_long_friday">vendredi</string> + <string name="day_of_week_long_saturday">samedi</string> + + <string name="day_of_week_medium_sunday">dim.</string> + <string name="day_of_week_medium_monday">lun.</string> + <string name="day_of_week_medium_tuesday">mar.</string> + <string name="day_of_week_medium_wednesday">mer.</string> + <string name="day_of_week_medium_thursday">jeu.</string> + <string name="day_of_week_medium_friday">ven.</string> + <string name="day_of_week_medium_saturday">sam.</string> + + <string name="day_of_week_short_sunday">dim.</string> + <string name="day_of_week_short_monday">lun.</string> + <string name="day_of_week_short_tuesday">mar.</string> + <string name="day_of_week_short_wednesday">mer.</string> + <string name="day_of_week_short_thursday">jeu.</string> + <string name="day_of_week_short_friday">ven.</string> + <string name="day_of_week_short_saturday">sam.</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">matin</string> + <string name="pm">soir</string> + <string name="yesterday">hier</string> + <string name="today">aujourd’hui</string> + <string name="tomorrow">demain</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e/%m/%Y</string> + <string name="numeric_date_format">d/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">du %1$s au %2$s</string> + <string name="date1_date2">du %2$s au %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s %3$s/%2$s/%4$s au %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">du %5$s %3$s/%2$s au %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s %3$s/%2$s au %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">du %5$s %3$s/%2$s/%4$s au %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">du %3$s %1$s %2$s au %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">du %1$s %2$s au %4$s %5$s</string> + <string name="date1_time1_date2_time2">du %3$s %2$s au %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">du %3$s %2$s au %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">du %1$s %3$s %2$s au %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">du %5$s %3$s %2$s au %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">du %5$s %3$s %2$s au %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s %3$s %2$s au %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s %3$s %2$s au %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s %3$s %2$s %4$s au %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s %3$s %2$s %4$s au %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">du %1$s %3$s %2$s %4$s au %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">du %1$s %3$s %2$s au %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr-rCA/donottranslate-cldr.xml b/core/res/res/values-fr-rCA/donottranslate-cldr.xml new file mode 100644 index 000000000000..346b97107331 --- /dev/null +++ b/core/res/res/values-fr-rCA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvier</string> + <string name="month_long_standalone_february">février</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">avril</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">juin</string> + <string name="month_long_standalone_july">juillet</string> + <string name="month_long_standalone_august">août</string> + <string name="month_long_standalone_september">septembre</string> + <string name="month_long_standalone_october">octobre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">décembre</string> + + <string name="month_long_january">janvier</string> + <string name="month_long_february">février</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">avril</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">juin</string> + <string name="month_long_july">juillet</string> + <string name="month_long_august">août</string> + <string name="month_long_september">septembre</string> + <string name="month_long_october">octobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">décembre</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">févr.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">avr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juin</string> + <string name="month_medium_july">juil.</string> + <string name="month_medium_august">août</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">déc.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">dimanche</string> + <string name="day_of_week_long_monday">lundi</string> + <string name="day_of_week_long_tuesday">mardi</string> + <string name="day_of_week_long_wednesday">mercredi</string> + <string name="day_of_week_long_thursday">jeudi</string> + <string name="day_of_week_long_friday">vendredi</string> + <string name="day_of_week_long_saturday">samedi</string> + + <string name="day_of_week_medium_sunday">dim.</string> + <string name="day_of_week_medium_monday">lun.</string> + <string name="day_of_week_medium_tuesday">mar.</string> + <string name="day_of_week_medium_wednesday">mer.</string> + <string name="day_of_week_medium_thursday">jeu.</string> + <string name="day_of_week_medium_friday">ven.</string> + <string name="day_of_week_medium_saturday">sam.</string> + + <string name="day_of_week_short_sunday">dim.</string> + <string name="day_of_week_short_monday">lun.</string> + <string name="day_of_week_short_tuesday">mar.</string> + <string name="day_of_week_short_wednesday">mer.</string> + <string name="day_of_week_short_thursday">jeu.</string> + <string name="day_of_week_short_friday">ven.</string> + <string name="day_of_week_short_saturday">sam.</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">matin</string> + <string name="pm">soir</string> + <string name="yesterday">hier</string> + <string name="today">aujourd’hui</string> + <string name="tomorrow">demain</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y-%m-%d</string> + <string name="numeric_date_format">yyyy-MM-dd</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %Y-%m-%d</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y-%m-%d</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s – %7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %2$s-%3$s – %6$s %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">du %1$s %4$s-%2$s-%3$s au %6$s %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %4$s-%2$s-%3$s – %10$s %6$s %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s-%3$s – %10$s %7$s-%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s-%3$s – %10$s %6$s %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">du %3$s %2$s au %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">du %1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr-rCH/donottranslate-cldr.xml b/core/res/res/values-fr-rCH/donottranslate-cldr.xml new file mode 100644 index 000000000000..48db6b8d4169 --- /dev/null +++ b/core/res/res/values-fr-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvier</string> + <string name="month_long_standalone_february">février</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">avril</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">juin</string> + <string name="month_long_standalone_july">juillet</string> + <string name="month_long_standalone_august">août</string> + <string name="month_long_standalone_september">septembre</string> + <string name="month_long_standalone_october">octobre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">décembre</string> + + <string name="month_long_january">janvier</string> + <string name="month_long_february">février</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">avril</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">juin</string> + <string name="month_long_july">juillet</string> + <string name="month_long_august">août</string> + <string name="month_long_september">septembre</string> + <string name="month_long_october">octobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">décembre</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">févr.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">avr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juin</string> + <string name="month_medium_july">juil.</string> + <string name="month_medium_august">août</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">déc.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">dimanche</string> + <string name="day_of_week_long_monday">lundi</string> + <string name="day_of_week_long_tuesday">mardi</string> + <string name="day_of_week_long_wednesday">mercredi</string> + <string name="day_of_week_long_thursday">jeudi</string> + <string name="day_of_week_long_friday">vendredi</string> + <string name="day_of_week_long_saturday">samedi</string> + + <string name="day_of_week_medium_sunday">dim.</string> + <string name="day_of_week_medium_monday">lun.</string> + <string name="day_of_week_medium_tuesday">mar.</string> + <string name="day_of_week_medium_wednesday">mer.</string> + <string name="day_of_week_medium_thursday">jeu.</string> + <string name="day_of_week_medium_friday">ven.</string> + <string name="day_of_week_medium_saturday">sam.</string> + + <string name="day_of_week_short_sunday">dim.</string> + <string name="day_of_week_short_monday">lun.</string> + <string name="day_of_week_short_tuesday">mar.</string> + <string name="day_of_week_short_wednesday">mer.</string> + <string name="day_of_week_short_thursday">jeu.</string> + <string name="day_of_week_short_friday">ven.</string> + <string name="day_of_week_short_saturday">sam.</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">matin</string> + <string name="pm">soir</string> + <string name="yesterday">hier</string> + <string name="today">aujourd’hui</string> + <string name="tomorrow">demain</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">du %1$s au %2$s</string> + <string name="date1_date2">du %2$s au %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s, %3$s.%2$s.%4$s au %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">du %5$s %3$s.%2$s au %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s, %3$s.%2$s au %10$s %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">du %5$s %3$s.%2$s.%4$s au %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">du %3$s %1$s, %2$s au %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">du %1$s, %2$s au %4$s, %5$s</string> + <string name="date1_time1_date2_time2">du %3$s %2$s au %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">du %3$s %2$s au %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">du %1$s, %3$s %2$s au %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">du %5$s %3$s %2$s au %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">du %5$s %3$s %2$s au %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s, %3$s %2$s au %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">du %5$s %1$s, %3$s %2$s au %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s, %3$s %2$s %4$s au %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">du %5$s %1$s, %3$s %2$s %4$s au %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">du %1$s, %3$s %2$s %4$s au %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">du %1$s, %3$s %2$s au %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s au %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr-rFR/donottranslate-cldr.xml b/core/res/res/values-fr-rFR/donottranslate-cldr.xml new file mode 100644 index 000000000000..f340e8384116 --- /dev/null +++ b/core/res/res/values-fr-rFR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvier</string> + <string name="month_long_standalone_february">février</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">avril</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">juin</string> + <string name="month_long_standalone_july">juillet</string> + <string name="month_long_standalone_august">août</string> + <string name="month_long_standalone_september">septembre</string> + <string name="month_long_standalone_october">octobre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">décembre</string> + + <string name="month_long_january">janvier</string> + <string name="month_long_february">février</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">avril</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">juin</string> + <string name="month_long_july">juillet</string> + <string name="month_long_august">août</string> + <string name="month_long_september">septembre</string> + <string name="month_long_october">octobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">décembre</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">févr.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">avr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juin</string> + <string name="month_medium_july">juil.</string> + <string name="month_medium_august">août</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">déc.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">dimanche</string> + <string name="day_of_week_long_monday">lundi</string> + <string name="day_of_week_long_tuesday">mardi</string> + <string name="day_of_week_long_wednesday">mercredi</string> + <string name="day_of_week_long_thursday">jeudi</string> + <string name="day_of_week_long_friday">vendredi</string> + <string name="day_of_week_long_saturday">samedi</string> + + <string name="day_of_week_medium_sunday">dim.</string> + <string name="day_of_week_medium_monday">lun.</string> + <string name="day_of_week_medium_tuesday">mar.</string> + <string name="day_of_week_medium_wednesday">mer.</string> + <string name="day_of_week_medium_thursday">jeu.</string> + <string name="day_of_week_medium_friday">ven.</string> + <string name="day_of_week_medium_saturday">sam.</string> + + <string name="day_of_week_short_sunday">dim.</string> + <string name="day_of_week_short_monday">lun.</string> + <string name="day_of_week_short_tuesday">mar.</string> + <string name="day_of_week_short_wednesday">mer.</string> + <string name="day_of_week_short_thursday">jeu.</string> + <string name="day_of_week_short_friday">ven.</string> + <string name="day_of_week_short_saturday">sam.</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">matin</string> + <string name="pm">soir</string> + <string name="yesterday">hier</string> + <string name="today">aujourd’hui</string> + <string name="tomorrow">demain</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s – %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml new file mode 100644 index 000000000000..f340e8384116 --- /dev/null +++ b/core/res/res/values-fr/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvier</string> + <string name="month_long_standalone_february">février</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">avril</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">juin</string> + <string name="month_long_standalone_july">juillet</string> + <string name="month_long_standalone_august">août</string> + <string name="month_long_standalone_september">septembre</string> + <string name="month_long_standalone_october">octobre</string> + <string name="month_long_standalone_november">novembre</string> + <string name="month_long_standalone_december">décembre</string> + + <string name="month_long_january">janvier</string> + <string name="month_long_february">février</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">avril</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">juin</string> + <string name="month_long_july">juillet</string> + <string name="month_long_august">août</string> + <string name="month_long_september">septembre</string> + <string name="month_long_october">octobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">décembre</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">févr.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">avr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juin</string> + <string name="month_medium_july">juil.</string> + <string name="month_medium_august">août</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">déc.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">dimanche</string> + <string name="day_of_week_long_monday">lundi</string> + <string name="day_of_week_long_tuesday">mardi</string> + <string name="day_of_week_long_wednesday">mercredi</string> + <string name="day_of_week_long_thursday">jeudi</string> + <string name="day_of_week_long_friday">vendredi</string> + <string name="day_of_week_long_saturday">samedi</string> + + <string name="day_of_week_medium_sunday">dim.</string> + <string name="day_of_week_medium_monday">lun.</string> + <string name="day_of_week_medium_tuesday">mar.</string> + <string name="day_of_week_medium_wednesday">mer.</string> + <string name="day_of_week_medium_thursday">jeu.</string> + <string name="day_of_week_medium_friday">ven.</string> + <string name="day_of_week_medium_saturday">sam.</string> + + <string name="day_of_week_short_sunday">dim.</string> + <string name="day_of_week_short_monday">lun.</string> + <string name="day_of_week_short_tuesday">mar.</string> + <string name="day_of_week_short_wednesday">mer.</string> + <string name="day_of_week_short_thursday">jeu.</string> + <string name="day_of_week_short_friday">ven.</string> + <string name="day_of_week_short_saturday">sam.</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">matin</string> + <string name="pm">soir</string> + <string name="yesterday">hier</string> + <string name="today">aujourd’hui</string> + <string name="tomorrow">demain</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s – %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index c664d1ae8dd9..ce650c128702 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -21,6 +21,7 @@ <string name="gigabyteShort">"Go"</string> <string name="terabyteShort">"To"</string> <string name="petabyteShort">"Po"</string> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g></string> <string name="untitled">"<sans titre>"</string> <string name="ellipsis">"…"</string> <string name="emptyPhoneNumber">"(Aucun numéro de téléphone)"</string> @@ -161,14 +162,10 @@ <string name="permdesc_changeConfiguration">"Permet à une application de modifier la configuration actuelle (par ex. : la taille de la police générale ou des paramètres régionaux)."</string> <string name="permlab_restartPackages">"Démarrage d\'autres applications"</string> <string name="permdesc_restartPackages">"Permet à une application de forcer le lancement d\'autres applications."</string> - <string name="permlab_setProcessForeground">"Non-possibilité d\'interruption"</string> - <string name="permdesc_setProcessForeground">"Permet à une application d\'exécuter tout processus au premier plan afin qu\'il ne puisse pas être interrompu. Les applications normales ne devraient jamais nécessiter cette fonctionnalité."</string> <string name="permlab_forceBack">"Fermeture forcée de l\'application"</string> <string name="permdesc_forceBack">"Permet à une application de forcer une autre application exécutée au premier plan à se fermer et à passer en arrière-plan. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string> <string name="permlab_dump">"Vérification de l\'état interne du système"</string> <string name="permdesc_dump">"Permet à l\'application de récupérer l\'état interne du système. Des applications malveillantes peuvent obtenir de nombreuses informations personnelles et sécurisées auxquelles elles ne devraient pas avoir accès."</string> - <string name="permlab_addSystemService">"Éditer des services à faible niveau"</string> - <string name="permdesc_addSystemService">"Permet à l\'application de publier ses propres services de système de niveau inférieur. Des applications malveillantes peuvent prendre le contrôle du système et subtiliser ou endommager ses données."</string> <string name="permlab_runSetActivityWatcher">"Contrôle du lancement des applications"</string> <string name="permdesc_runSetActivityWatcher">"Permet à une application de suivre et de contrôler la façon dont le système lance des activités. Des applications malveillantes peuvent entièrement déstabiliser le système. Cette autorisation est uniquement nécessaire au développement et non pour l\'utilisation normale du téléphone."</string> <string name="permlab_broadcastPackageRemoved">"Envoyer une diffusion sans paquet"</string> @@ -181,8 +178,6 @@ <string name="permdesc_setProcessLimit">"Permet à une application de contrôler le nombre de processus maximal exécutés en même temps. Les applications normales n\'ont jamais recours à cette fonctionnalité."</string> <string name="permlab_setAlwaysFinish">"Fermeture de toutes les applications en tâche de fond"</string> <string name="permdesc_setAlwaysFinish">"Permet à une application de vérifier si des activités sont systématiquement interrompues lorsqu\'elles sont placées en tâche de fond. Cette fonctionnalité n\'est jamais utilisée par les applications normales."</string> - <string name="permlab_fotaUpdate">"Installation des mises à jour du système"</string> - <string name="permdesc_fotaUpdate">"Permet à une application de recevoir des notifications sur des mises à jour système en cours et de lancer leur installation. Des applications malveillantes peuvent utiliser cette fonctionnalité pour endommager le système avec des mises à jour non autorisées ou interférer avec le processus de mise à jour."</string> <string name="permlab_batteryStats">"Modification des statistiques de la batterie"</string> <string name="permdesc_batteryStats">"Autoriser la modification des statistiques de la batterie. Les applications normales n\'utilisent pas cette fonctionnalité."</string> <string name="permlab_internalSystemWindow">"Affichage de fenêtres non autorisées"</string> @@ -418,9 +413,6 @@ <string name="lockscreen_glogin_password_hint">"Mot de passe"</string> <string name="lockscreen_glogin_submit_button">"Se connecter"</string> <string name="lockscreen_glogin_invalid_input">"Nom d\'utilisateur ou mot de passe incorrect."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Effacer les notifications"</string> @@ -452,9 +444,6 @@ <string name="menu_enter_shortcut_label">"entrée"</string> <string name="menu_delete_shortcut_label">"supprimer"</string> <string name="search_go">"Rechercher"</string> - <string name="today">"Aujourd\'hui"</string> - <string name="yesterday">"Hier"</string> - <string name="tomorrow">"Demain"</string> <string name="oneMonthDurationPast">"Il y a 1 mois"</string> <string name="beforeOneMonthDurationPast">"Il y a plus d\'un mois"</string> <plurals name="num_seconds_ago"> @@ -536,13 +525,6 @@ <string name="weeks">"semaines"</string> <string name="year">"année"</string> <string name="years">"années"</string> - <string name="sunday">"dimanche"</string> - <string name="monday">"lundi"</string> - <string name="tuesday">"mardi"</string> - <string name="wednesday">"mercredi"</string> - <string name="thursday">"jeudi"</string> - <string name="friday">"vendredi"</string> - <string name="saturday">"samedi"</string> <string name="every_weekday">"Tous les jours ouvrés (lun.- ven.)"</string> <string name="daily">"Tous les jours"</string> <string name="weekly">"Toutes les semaines le <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +534,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Désolé, cette vidéo ne peut être lue sur cet appareil."</string> <string name="VideoView_error_text_unknown">"Désolé, impossible de lire cette vidéo."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"midi"</string> <string name="Noon">"Midi"</string> <string name="midnight">"minuit"</string> <string name="Midnight">"Minuit"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"dimanche"</string> - <string name="day_of_week_long_monday">"lundi"</string> - <string name="day_of_week_long_tuesday">"mardi"</string> - <string name="day_of_week_long_wednesday">"mercredi"</string> - <string name="day_of_week_long_thursday">"jeudi"</string> - <string name="day_of_week_long_friday">"vendredi"</string> - <string name="day_of_week_long_saturday">"samedi"</string> - <string name="day_of_week_medium_sunday">"dim."</string> - <string name="day_of_week_medium_monday">"Lun"</string> - <string name="day_of_week_medium_tuesday">"Mar"</string> - <string name="day_of_week_medium_wednesday">"Mer"</string> - <string name="day_of_week_medium_thursday">"Jeu"</string> - <string name="day_of_week_medium_friday">"Ven"</string> - <string name="day_of_week_medium_saturday">"Sam"</string> - <string name="day_of_week_short_sunday">"Dim"</string> - <string name="day_of_week_short_monday">"Lun"</string> - <string name="day_of_week_short_tuesday">"Mar"</string> - <string name="day_of_week_short_wednesday">"Mer"</string> - <string name="day_of_week_short_thursday">"Jeu"</string> - <string name="day_of_week_short_friday">"Ven"</string> - <string name="day_of_week_short_saturday">"Sam"</string> - <string name="day_of_week_shorter_sunday">"Dim"</string> - <string name="day_of_week_shorter_monday">"Lun"</string> - <string name="day_of_week_shorter_tuesday">"Mar"</string> - <string name="day_of_week_shorter_wednesday">"Mer"</string> - <string name="day_of_week_shorter_thursday">"Jeu"</string> - <string name="day_of_week_shorter_friday">"Ven"</string> - <string name="day_of_week_shorter_saturday">"sam."</string> - <string name="day_of_week_shortest_sunday">"Dim"</string> - <string name="day_of_week_shortest_monday">"Lun"</string> - <string name="day_of_week_shortest_tuesday">"Mar"</string> - <string name="day_of_week_shortest_wednesday">"Mer"</string> - <string name="day_of_week_shortest_thursday">"Jeu"</string> - <string name="day_of_week_shortest_friday">"Ven"</string> - <string name="day_of_week_shortest_saturday">"Sam"</string> - <string name="month_long_january">"janvier"</string> - <string name="month_long_february">"février"</string> - <string name="month_long_march">"mars"</string> - <string name="month_long_april">"avril"</string> - <string name="month_long_may">"mai"</string> - <string name="month_long_june">"juin"</string> - <string name="month_long_july">"juillet"</string> - <string name="month_long_august">"août"</string> - <string name="month_long_september">"septembre"</string> - <string name="month_long_october">"octobre"</string> - <string name="month_long_november">"novembre"</string> - <string name="month_long_december">"décembre"</string> - <string name="month_medium_january">"janv."</string> - <string name="month_medium_february">"févr."</string> - <string name="month_medium_march">"mars"</string> - <string name="month_medium_april">"avr."</string> - <string name="month_medium_may">"mai"</string> - <string name="month_medium_june">"juin"</string> - <string name="month_medium_july">"juil."</string> - <string name="month_medium_august">"août"</string> - <string name="month_medium_september">"sept."</string> - <string name="month_medium_october">"oct."</string> - <string name="month_medium_november">"nov."</string> - <string name="month_medium_december">"déc."</string> - <string name="month_shortest_january">"jan."</string> - <string name="month_shortest_february">"Ven"</string> - <string name="month_shortest_march">"mars"</string> - <string name="month_shortest_april">"avr."</string> - <string name="month_shortest_may">"mai"</string> - <string name="month_shortest_june">"juin"</string> - <string name="month_shortest_july">"juil."</string> - <string name="month_shortest_august">"août"</string> - <string name="month_shortest_september">"sept."</string> - <string name="month_shortest_october">"oct."</string> - <string name="month_shortest_november">"nov."</string> - <string name="month_shortest_december">"déc."</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Tout sélectionner"</string> diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml new file mode 100644 index 000000000000..e3feb1e33146 --- /dev/null +++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">ינואר</string> + <string name="month_long_standalone_february">פברואר</string> + <string name="month_long_standalone_march">מרס</string> + <string name="month_long_standalone_april">אפריל</string> + <string name="month_long_standalone_may">מאי</string> + <string name="month_long_standalone_june">יוני</string> + <string name="month_long_standalone_july">יולי</string> + <string name="month_long_standalone_august">אוגוסט</string> + <string name="month_long_standalone_september">ספטמבר</string> + <string name="month_long_standalone_october">אוקטובר</string> + <string name="month_long_standalone_november">נובמבר</string> + <string name="month_long_standalone_december">דצמבר</string> + + <string name="month_long_january">ינואר</string> + <string name="month_long_february">פברואר</string> + <string name="month_long_march">מרס</string> + <string name="month_long_april">אפריל</string> + <string name="month_long_may">מאי</string> + <string name="month_long_june">יוני</string> + <string name="month_long_july">יולי</string> + <string name="month_long_august">אוגוסט</string> + <string name="month_long_september">ספטמבר</string> + <string name="month_long_october">אוקטובר</string> + <string name="month_long_november">נובמבר</string> + <string name="month_long_december">דצמבר</string> + + <string name="month_medium_january">ינו</string> + <string name="month_medium_february">פבר</string> + <string name="month_medium_march">מרס</string> + <string name="month_medium_april">אפר</string> + <string name="month_medium_may">מאי</string> + <string name="month_medium_june">יונ</string> + <string name="month_medium_july">יול</string> + <string name="month_medium_august">אוג</string> + <string name="month_medium_september">ספט</string> + <string name="month_medium_october">אוק</string> + <string name="month_medium_november">נוב</string> + <string name="month_medium_december">דצמ</string> + + <string name="month_shortest_january">1</string> + <string name="month_shortest_february">2</string> + <string name="month_shortest_march">3</string> + <string name="month_shortest_april">4</string> + <string name="month_shortest_may">5</string> + <string name="month_shortest_june">6</string> + <string name="month_shortest_july">7</string> + <string name="month_shortest_august">8</string> + <string name="month_shortest_september">9</string> + <string name="month_shortest_october">10</string> + <string name="month_shortest_november">11</string> + <string name="month_shortest_december">12</string> + + <string name="day_of_week_long_sunday">יום ראשון</string> + <string name="day_of_week_long_monday">יום שני</string> + <string name="day_of_week_long_tuesday">יום שלישי</string> + <string name="day_of_week_long_wednesday">יום רביעי</string> + <string name="day_of_week_long_thursday">יום חמישי</string> + <string name="day_of_week_long_friday">יום שישי</string> + <string name="day_of_week_long_saturday">יום שבת</string> + + <string name="day_of_week_medium_sunday">יום א'</string> + <string name="day_of_week_medium_monday">יום ב'</string> + <string name="day_of_week_medium_tuesday">יום ג'</string> + <string name="day_of_week_medium_wednesday">יום ד'</string> + <string name="day_of_week_medium_thursday">יום ה'</string> + <string name="day_of_week_medium_friday">יום ו'</string> + <string name="day_of_week_medium_saturday">שבת</string> + + <string name="day_of_week_short_sunday">יום א'</string> + <string name="day_of_week_short_monday">יום ב'</string> + <string name="day_of_week_short_tuesday">יום ג'</string> + <string name="day_of_week_short_wednesday">יום ד'</string> + <string name="day_of_week_short_thursday">יום ה'</string> + <string name="day_of_week_short_friday">יום ו'</string> + <string name="day_of_week_short_saturday">שבת</string> + + <string name="day_of_week_shortest_sunday">א</string> + <string name="day_of_week_shortest_monday">ב</string> + <string name="day_of_week_shortest_tuesday">ג</string> + <string name="day_of_week_shortest_wednesday">ד</string> + <string name="day_of_week_shortest_thursday">ה</string> + <string name="day_of_week_shortest_friday">ו</string> + <string name="day_of_week_shortest_saturday">ש</string> + + <string name="am">לפנה"צ</string> + <string name="pm">אחה"צ</string> + <string name="yesterday">אתמול</string> + <string name="today">היום</string> + <string name="tomorrow">מחר</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e ב%B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e.%-m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e.%-m.%Y</string> + <string name="month_day">%-e ב%B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s – %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s – %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s ב%2$s – %8$s ב%7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s ב%2$s – %10$s %8$s ב%7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s ב%2$s – %10$s %8$s ב%7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s – %6$s, %9$s %7$s %8$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml new file mode 100644 index 000000000000..2a19da4c482c --- /dev/null +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">जनवरी</string> + <string name="month_long_standalone_february">फरवरी</string> + <string name="month_long_standalone_march">मार्च</string> + <string name="month_long_standalone_april">अप्रैल</string> + <string name="month_long_standalone_may">मई</string> + <string name="month_long_standalone_june">जून</string> + <string name="month_long_standalone_july">जुलाई</string> + <string name="month_long_standalone_august">अगस्त</string> + <string name="month_long_standalone_september">सितम्बर</string> + <string name="month_long_standalone_october">अक्तूबर</string> + <string name="month_long_standalone_november">नवम्बर</string> + <string name="month_long_standalone_december">दिसम्बर</string> + + <string name="month_long_january">जनवरी</string> + <string name="month_long_february">फरवरी</string> + <string name="month_long_march">मार्च</string> + <string name="month_long_april">अप्रैल</string> + <string name="month_long_may">मई</string> + <string name="month_long_june">जून</string> + <string name="month_long_july">जुलाई</string> + <string name="month_long_august">अगस्त</string> + <string name="month_long_september">सितम्बर</string> + <string name="month_long_october">अक्तूबर</string> + <string name="month_long_november">नवम्बर</string> + <string name="month_long_december">दिसम्बर</string> + + <string name="month_medium_january">जनवरी</string> + <string name="month_medium_february">फरवरी</string> + <string name="month_medium_march">मार्च</string> + <string name="month_medium_april">अप्रैल</string> + <string name="month_medium_may">मई</string> + <string name="month_medium_june">जून</string> + <string name="month_medium_july">जुलाई</string> + <string name="month_medium_august">अगस्त</string> + <string name="month_medium_september">सितम्बर</string> + <string name="month_medium_october">अक्तूबर</string> + <string name="month_medium_november">नवम्बर</string> + <string name="month_medium_december">दिसम्बर</string> + + <string name="month_shortest_january">ज</string> + <string name="month_shortest_february">फ़</string> + <string name="month_shortest_march">मा</string> + <string name="month_shortest_april">अ</string> + <string name="month_shortest_may">म</string> + <string name="month_shortest_june">जू</string> + <string name="month_shortest_july">जु</string> + <string name="month_shortest_august">अ</string> + <string name="month_shortest_september">सि</string> + <string name="month_shortest_october">अ</string> + <string name="month_shortest_november">न</string> + <string name="month_shortest_december">दि</string> + + <string name="day_of_week_long_sunday">रविवार</string> + <string name="day_of_week_long_monday">सोमवार</string> + <string name="day_of_week_long_tuesday">मंगलवार</string> + <string name="day_of_week_long_wednesday">बुधवार</string> + <string name="day_of_week_long_thursday">गुरुवार</string> + <string name="day_of_week_long_friday">शुक्रवार</string> + <string name="day_of_week_long_saturday">शनिवार</string> + + <string name="day_of_week_medium_sunday">रवि</string> + <string name="day_of_week_medium_monday">सोम</string> + <string name="day_of_week_medium_tuesday">मंगल</string> + <string name="day_of_week_medium_wednesday">बुध</string> + <string name="day_of_week_medium_thursday">गुरु</string> + <string name="day_of_week_medium_friday">शुक्र</string> + <string name="day_of_week_medium_saturday">शनि</string> + + <string name="day_of_week_short_sunday">रवि</string> + <string name="day_of_week_short_monday">सोम</string> + <string name="day_of_week_short_tuesday">मंगल</string> + <string name="day_of_week_short_wednesday">बुध</string> + <string name="day_of_week_short_thursday">गुरु</string> + <string name="day_of_week_short_friday">शुक्र</string> + <string name="day_of_week_short_saturday">शनि</string> + + <string name="day_of_week_shortest_sunday">र</string> + <string name="day_of_week_shortest_monday">सो</string> + <string name="day_of_week_shortest_tuesday">मं</string> + <string name="day_of_week_shortest_wednesday">बु</string> + <string name="day_of_week_shortest_thursday">गु</string> + <string name="day_of_week_shortest_friday">शु</string> + <string name="day_of_week_shortest_saturday">श</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e-%-m-%Y</string> + <string name="numeric_date_format">d-M-yyyy</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%-l:%M:%S %p %d-%m-%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d-%m-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s – %7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s-%3$s – %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s – %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s-%2$s-%4$s – %10$s %8$s-%7$s-%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s – %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s – %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%2$s-%3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s – %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string> + <string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml new file mode 100644 index 000000000000..08a70b81f7b4 --- /dev/null +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">január</string> + <string name="month_long_standalone_february">február</string> + <string name="month_long_standalone_march">március</string> + <string name="month_long_standalone_april">április</string> + <string name="month_long_standalone_may">május</string> + <string name="month_long_standalone_june">június</string> + <string name="month_long_standalone_july">július</string> + <string name="month_long_standalone_august">augusztus</string> + <string name="month_long_standalone_september">szeptember</string> + <string name="month_long_standalone_october">október</string> + <string name="month_long_standalone_november">november</string> + <string name="month_long_standalone_december">december</string> + + <string name="month_long_january">január</string> + <string name="month_long_february">február</string> + <string name="month_long_march">március</string> + <string name="month_long_april">április</string> + <string name="month_long_may">május</string> + <string name="month_long_june">június</string> + <string name="month_long_july">július</string> + <string name="month_long_august">augusztus</string> + <string name="month_long_september">szeptember</string> + <string name="month_long_october">október</string> + <string name="month_long_november">november</string> + <string name="month_long_december">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">febr.</string> + <string name="month_medium_march">márc.</string> + <string name="month_medium_april">ápr.</string> + <string name="month_medium_may">máj.</string> + <string name="month_medium_june">jún.</string> + <string name="month_medium_july">júl.</string> + <string name="month_medium_august">aug.</string> + <string name="month_medium_september">szept.</string> + <string name="month_medium_october">okt.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">Á</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">vasárnap</string> + <string name="day_of_week_long_monday">hétfő</string> + <string name="day_of_week_long_tuesday">kedd</string> + <string name="day_of_week_long_wednesday">szerda</string> + <string name="day_of_week_long_thursday">csütörtök</string> + <string name="day_of_week_long_friday">péntek</string> + <string name="day_of_week_long_saturday">szombat</string> + + <string name="day_of_week_medium_sunday">V</string> + <string name="day_of_week_medium_monday">H</string> + <string name="day_of_week_medium_tuesday">K</string> + <string name="day_of_week_medium_wednesday">Sze</string> + <string name="day_of_week_medium_thursday">Cs</string> + <string name="day_of_week_medium_friday">P</string> + <string name="day_of_week_medium_saturday">Szo</string> + + <string name="day_of_week_short_sunday">V</string> + <string name="day_of_week_short_monday">H</string> + <string name="day_of_week_short_tuesday">K</string> + <string name="day_of_week_short_wednesday">Sze</string> + <string name="day_of_week_short_thursday">Cs</string> + <string name="day_of_week_short_friday">P</string> + <string name="day_of_week_short_saturday">Szo</string> + + <string name="day_of_week_shortest_sunday">V</string> + <string name="day_of_week_shortest_monday">H</string> + <string name="day_of_week_shortest_tuesday">K</string> + <string name="day_of_week_shortest_wednesday">S</string> + <string name="day_of_week_shortest_thursday">C</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">de.</string> + <string name="pm">du.</string> + <string name="yesterday">tegnap</string> + <string name="today">ma</string> + <string name="tomorrow">holnap</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%Y.%m.%d.</string> + <string name="numeric_date_format">yyyy.MM.dd.</string> + <string name="numeric_date_template">"%s.%s.%s."</string> + <string name="month_day_year">%Y. %B %-e.</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %Y.%m.%d.</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y.%m.%d.</string> + <string name="month_day">%B %-e.</string> + <string name="month">%-B</string> + <string name="month_year">%Y. %B</string> + <string name="abbrev_month_day">%b %-e.</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y. %b</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%2$s.%3$s. - %7$s.%8$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s.%3$s., %1$s - %7$s.%8$s., %6$s</string> + <string name="numeric_mdy1_mdy2">%4$s.%2$s.%3$s. - %9$s.%7$s.%8$s.</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s.%2$s.%3$s., %1$s - %9$s.%7$s.%8$s., %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s.%2$s.%3$s., %1$s - %10$s %9$s.%7$s.%8$s., %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s. %3$s. - %10$s %7$s. %8$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s. %3$s., %1$s - %10$s %7$s. %8$s., %6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s.%2$s.%3$s. - %10$s %9$s.%7$s.%8$s.</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s, %1$s - %6$s %5$s, %4$s</string> + <string name="wday1_date1_wday2_date2">%2$s, %1$s - %5$s, %4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s, %2$s</string> + <string name="wday_date">%3$s, %2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s. - %7$s %8$s.</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s %3$s., %1$s - %7$s %8$s., %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s. - %10$s %7$s %8$s.</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s. - %10$s %7$s %8$s.</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s., %1$s - %10$s %7$s %8$s., %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s., %1$s - %10$s %7$s %8$s., %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s. %2$s %3$s. - %10$s %9$s. %7$s %8$s.</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s. %2$s %3$s. - %10$s %9$s. %7$s %8$s.</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s. %2$s %3$s., %1$s - %10$s %9$s. %7$s %8$s., %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s. %2$s %3$s., %1$s - %10$s %9$s. %7$s %8$s., %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s. %2$s %3$s., %1$s - %9$s. %7$s %8$s., %6$s</string> + <string name="same_month_md1_md2">%2$s %3$s-%8$s.</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s %3$s., %1$s - %7$s %8$s., %6$s</string> + <string name="same_year_mdy1_mdy2">%9$s. %2$s %3$s. - %7$s %8$s.</string> + <string name="same_month_mdy1_mdy2">%9$s. %2$s %3$s-%8$s.</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-id-rID/donottranslate-cldr.xml new file mode 100644 index 000000000000..6adec84b8ab8 --- /dev/null +++ b/core/res/res/values-id-rID/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Januari</string> + <string name="month_long_standalone_february">Februari</string> + <string name="month_long_standalone_march">Maret</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">Mei</string> + <string name="month_long_standalone_june">Juni</string> + <string name="month_long_standalone_july">Juli</string> + <string name="month_long_standalone_august">Agustus</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">Desember</string> + + <string name="month_long_january">Januari</string> + <string name="month_long_february">Februari</string> + <string name="month_long_march">Maret</string> + <string name="month_long_april">April</string> + <string name="month_long_may">Mei</string> + <string name="month_long_june">Juni</string> + <string name="month_long_july">Juli</string> + <string name="month_long_august">Agustus</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">Desember</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">Mei</string> + <string name="month_medium_june">Jun</string> + <string name="month_medium_july">Jul</string> + <string name="month_medium_august">Agu</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">Des</string> + + <string name="month_shortest_january">1</string> + <string name="month_shortest_february">2</string> + <string name="month_shortest_march">3</string> + <string name="month_shortest_april">4</string> + <string name="month_shortest_may">5</string> + <string name="month_shortest_june">6</string> + <string name="month_shortest_july">7</string> + <string name="month_shortest_august">8</string> + <string name="month_shortest_september">9</string> + <string name="month_shortest_october">10</string> + <string name="month_shortest_november">11</string> + <string name="month_shortest_december">12</string> + + <string name="day_of_week_long_sunday">Minggu</string> + <string name="day_of_week_long_monday">Senin</string> + <string name="day_of_week_long_tuesday">Selasa</string> + <string name="day_of_week_long_wednesday">Rabu</string> + <string name="day_of_week_long_thursday">Kamis</string> + <string name="day_of_week_long_friday">Jumat</string> + <string name="day_of_week_long_saturday">Sabtu</string> + + <string name="day_of_week_medium_sunday">Min</string> + <string name="day_of_week_medium_monday">Sen</string> + <string name="day_of_week_medium_tuesday">Sel</string> + <string name="day_of_week_medium_wednesday">Rab</string> + <string name="day_of_week_medium_thursday">Kam</string> + <string name="day_of_week_medium_friday">Jum</string> + <string name="day_of_week_medium_saturday">Sab</string> + + <string name="day_of_week_short_sunday">Min</string> + <string name="day_of_week_short_monday">Sen</string> + <string name="day_of_week_short_tuesday">Sel</string> + <string name="day_of_week_short_wednesday">Rab</string> + <string name="day_of_week_short_thursday">Kam</string> + <string name="day_of_week_short_friday">Jum</string> + <string name="day_of_week_short_saturday">Sab</string> + + <string name="day_of_week_shortest_sunday">1</string> + <string name="day_of_week_shortest_monday">2</string> + <string name="day_of_week_shortest_tuesday">3</string> + <string name="day_of_week_shortest_wednesday">4</string> + <string name="day_of_week_shortest_thursday">5</string> + <string name="day_of_week_shortest_friday">6</string> + <string name="day_of_week_shortest_saturday">7</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s – %7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s-%3$s – %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s-%3$s – %10$s %7$s-%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s – %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s – %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s – %6$s %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s – %10$s %7$s %8$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s – %10$s %7$s %8$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s – %10$s %6$s %7$s %8$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s – %10$s %6$s %7$s %8$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s – %6$s, %9$s %7$s %8$s</string> + <string name="same_month_md1_md2">%2$s-%3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s – %6$s %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string> + <string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-it-rCH/donottranslate-cldr.xml b/core/res/res/values-it-rCH/donottranslate-cldr.xml new file mode 100644 index 000000000000..12170d6c4159 --- /dev/null +++ b/core/res/res/values-it-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Gennaio</string> + <string name="month_long_standalone_february">Febbraio</string> + <string name="month_long_standalone_march">Marzo</string> + <string name="month_long_standalone_april">Aprile</string> + <string name="month_long_standalone_may">Maggio</string> + <string name="month_long_standalone_june">Giugno</string> + <string name="month_long_standalone_july">Luglio</string> + <string name="month_long_standalone_august">Agosto</string> + <string name="month_long_standalone_september">Settembre</string> + <string name="month_long_standalone_october">Ottobre</string> + <string name="month_long_standalone_november">Novembre</string> + <string name="month_long_standalone_december">Dicembre</string> + + <string name="month_long_january">gennaio</string> + <string name="month_long_february">febbraio</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">aprile</string> + <string name="month_long_may">maggio</string> + <string name="month_long_june">giugno</string> + <string name="month_long_july">luglio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">settembre</string> + <string name="month_long_october">ottobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">dicembre</string> + + <string name="month_medium_january">gen</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">mag</string> + <string name="month_medium_june">giu</string> + <string name="month_medium_july">lug</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">set</string> + <string name="month_medium_october">ott</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">G</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">G</string> + <string name="month_shortest_july">L</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domenica</string> + <string name="day_of_week_long_monday">lunedì</string> + <string name="day_of_week_long_tuesday">martedì</string> + <string name="day_of_week_long_wednesday">mercoledì</string> + <string name="day_of_week_long_thursday">giovedì</string> + <string name="day_of_week_long_friday">venerdì</string> + <string name="day_of_week_long_saturday">sabato</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mer</string> + <string name="day_of_week_medium_thursday">gio</string> + <string name="day_of_week_medium_friday">ven</string> + <string name="day_of_week_medium_saturday">sab</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mer</string> + <string name="day_of_week_short_thursday">gio</string> + <string name="day_of_week_short_friday">ven</string> + <string name="day_of_week_short_saturday">sab</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">G</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">m.</string> + <string name="pm">p.</string> + <string name="yesterday">ieri</string> + <string name="today">oggi</string> + <string name="tomorrow">domani</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %-e-%b-%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e-%b-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s - %10$s %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-it-rIT/donottranslate-cldr.xml b/core/res/res/values-it-rIT/donottranslate-cldr.xml new file mode 100644 index 000000000000..2178bbea2481 --- /dev/null +++ b/core/res/res/values-it-rIT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Gennaio</string> + <string name="month_long_standalone_february">Febbraio</string> + <string name="month_long_standalone_march">Marzo</string> + <string name="month_long_standalone_april">Aprile</string> + <string name="month_long_standalone_may">Maggio</string> + <string name="month_long_standalone_june">Giugno</string> + <string name="month_long_standalone_july">Luglio</string> + <string name="month_long_standalone_august">Agosto</string> + <string name="month_long_standalone_september">Settembre</string> + <string name="month_long_standalone_october">Ottobre</string> + <string name="month_long_standalone_november">Novembre</string> + <string name="month_long_standalone_december">Dicembre</string> + + <string name="month_long_january">gennaio</string> + <string name="month_long_february">febbraio</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">aprile</string> + <string name="month_long_may">maggio</string> + <string name="month_long_june">giugno</string> + <string name="month_long_july">luglio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">settembre</string> + <string name="month_long_october">ottobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">dicembre</string> + + <string name="month_medium_january">gen</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">mag</string> + <string name="month_medium_june">giu</string> + <string name="month_medium_july">lug</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">set</string> + <string name="month_medium_october">ott</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">G</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">G</string> + <string name="month_shortest_july">L</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domenica</string> + <string name="day_of_week_long_monday">lunedì</string> + <string name="day_of_week_long_tuesday">martedì</string> + <string name="day_of_week_long_wednesday">mercoledì</string> + <string name="day_of_week_long_thursday">giovedì</string> + <string name="day_of_week_long_friday">venerdì</string> + <string name="day_of_week_long_saturday">sabato</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mer</string> + <string name="day_of_week_medium_thursday">gio</string> + <string name="day_of_week_medium_friday">ven</string> + <string name="day_of_week_medium_saturday">sab</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mer</string> + <string name="day_of_week_short_thursday">gio</string> + <string name="day_of_week_short_friday">ven</string> + <string name="day_of_week_short_saturday">sab</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">G</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">m.</string> + <string name="pm">p.</string> + <string name="yesterday">ieri</string> + <string name="today">oggi</string> + <string name="tomorrow">domani</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %d/%b/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%b/%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml new file mode 100644 index 000000000000..2178bbea2481 --- /dev/null +++ b/core/res/res/values-it/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Gennaio</string> + <string name="month_long_standalone_february">Febbraio</string> + <string name="month_long_standalone_march">Marzo</string> + <string name="month_long_standalone_april">Aprile</string> + <string name="month_long_standalone_may">Maggio</string> + <string name="month_long_standalone_june">Giugno</string> + <string name="month_long_standalone_july">Luglio</string> + <string name="month_long_standalone_august">Agosto</string> + <string name="month_long_standalone_september">Settembre</string> + <string name="month_long_standalone_october">Ottobre</string> + <string name="month_long_standalone_november">Novembre</string> + <string name="month_long_standalone_december">Dicembre</string> + + <string name="month_long_january">gennaio</string> + <string name="month_long_february">febbraio</string> + <string name="month_long_march">marzo</string> + <string name="month_long_april">aprile</string> + <string name="month_long_may">maggio</string> + <string name="month_long_june">giugno</string> + <string name="month_long_july">luglio</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">settembre</string> + <string name="month_long_october">ottobre</string> + <string name="month_long_november">novembre</string> + <string name="month_long_december">dicembre</string> + + <string name="month_medium_january">gen</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">mag</string> + <string name="month_medium_june">giu</string> + <string name="month_medium_july">lug</string> + <string name="month_medium_august">ago</string> + <string name="month_medium_september">set</string> + <string name="month_medium_october">ott</string> + <string name="month_medium_november">nov</string> + <string name="month_medium_december">dic</string> + + <string name="month_shortest_january">G</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">G</string> + <string name="month_shortest_july">L</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domenica</string> + <string name="day_of_week_long_monday">lunedì</string> + <string name="day_of_week_long_tuesday">martedì</string> + <string name="day_of_week_long_wednesday">mercoledì</string> + <string name="day_of_week_long_thursday">giovedì</string> + <string name="day_of_week_long_friday">venerdì</string> + <string name="day_of_week_long_saturday">sabato</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">lun</string> + <string name="day_of_week_medium_tuesday">mar</string> + <string name="day_of_week_medium_wednesday">mer</string> + <string name="day_of_week_medium_thursday">gio</string> + <string name="day_of_week_medium_friday">ven</string> + <string name="day_of_week_medium_saturday">sab</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">lun</string> + <string name="day_of_week_short_tuesday">mar</string> + <string name="day_of_week_short_wednesday">mer</string> + <string name="day_of_week_short_thursday">gio</string> + <string name="day_of_week_short_friday">ven</string> + <string name="day_of_week_short_saturday">sab</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">G</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">m.</string> + <string name="pm">p.</string> + <string name="yesterday">ieri</string> + <string name="today">oggi</string> + <string name="tomorrow">domani</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %d/%b/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%b/%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index f80a4f4cd156..5bfbc4943143 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Consente a un\'applicazione di modificare la configurazione corrente, come le dimensioni dei caratteri locali o complessive."</string> <string name="permlab_restartPackages">"riavvio altre applicazioni"</string> <string name="permdesc_restartPackages">"Consente a un\'applicazione di riavviare forzatamente altre applicazioni."</string> - <string name="permlab_setProcessForeground">"impedire l\'interruzione"</string> - <string name="permdesc_setProcessForeground">"Consente a un\'applicazione di eseguire i processi in primo piano in modo che non possano essere interrotti. Non dovrebbe essere mai necessario per le normali applicazioni."</string> <string name="permlab_forceBack">"chiusura forzata dell\'applicazione"</string> <string name="permdesc_forceBack">"Consente a un\'applicazione di forzare la chiusura di attività in primo piano. Non dovrebbe essere mai necessario per le normali applicazioni."</string> <string name="permlab_dump">"recupero stato interno del sistema"</string> <string name="permdesc_dump">"Consente all\'applicazione di recuperare lo stato interno del sistema. Le applicazioni dannose potrebbero recuperare molte informazioni riservate e protette di cui non dovrebbero avere mai bisogno."</string> - <string name="permlab_addSystemService">"pubblicaz. servizi di basso livello"</string> - <string name="permdesc_addSystemService">"Consente a un\'applicazione di pubblicare i suoi servizi di sistema di basso livello. Le applicazioni dannose potrebbero assumere il controllo del sistema e impossessarsi di dati o danneggiarli."</string> <string name="permlab_runSetActivityWatcher">"monitoraggio e controllo avvio applicazioni"</string> <string name="permdesc_runSetActivityWatcher">"Consente a un\'applicazione di monitorare e controllare la modalità di avvio delle attività nel sistema. Le applicazioni dannose potrebbero compromettere totalmente il sistema. Questa autorizzazione è necessaria soltanto per lo sviluppo, mai per il normale utilizzo del telefono."</string> <string name="permlab_broadcastPackageRemoved">"invio broadcast rimossi dal pacchetto"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Consente a un\'applicazione di stabilire il numero massimo di processi in esecuzione. Mai necessario per le normali applicazioni."</string> <string name="permlab_setAlwaysFinish">"chiusura applicazioni in background"</string> <string name="permdesc_setAlwaysFinish">"Consente a un\'applicazione di controllare se le attività sono sempre completate quando vengono messe in secondo piano. Mai necessario per le normali applicazioni."</string> - <string name="permlab_fotaUpdate">"installazione autom. aggiornamenti di sistema"</string> - <string name="permdesc_fotaUpdate">"Consente a un\'applicazione di ricevere notifiche sugli aggiornamenti del sistema in sospeso e di attivarne l\'installazione. Le applicazioni dannose possono sfruttare questa possibilità per danneggiare il sistema con aggiornamenti non autorizzati, o interferire con il processo di aggiornamento."</string> <string name="permlab_batteryStats">"modifica statistiche batteria"</string> <string name="permdesc_batteryStats">"Consente la modifica delle statistiche sulla batteria raccolte. Da non usare per normali applicazioni."</string> <string name="permlab_internalSystemWindow">"visualizzazione finestre non autorizzate"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Password"</string> <string name="lockscreen_glogin_submit_button">"Accedi"</string> <string name="lockscreen_glogin_invalid_input">"Password o nome utente non valido."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Cancella notifiche"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"Invio"</string> <string name="menu_delete_shortcut_label">"Canc"</string> <string name="search_go">"Cerca"</string> - <string name="today">"Oggi"</string> - <string name="yesterday">"Ieri"</string> - <string name="tomorrow">"Domani"</string> <string name="oneMonthDurationPast">"1 mese fa"</string> <string name="beforeOneMonthDurationPast">"Oltre 1 mese fa"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"settimane"</string> <string name="year">"anno"</string> <string name="years">"anni"</string> - <string name="sunday">"Domenica"</string> - <string name="monday">"Lunedì"</string> - <string name="tuesday">"Martedì"</string> - <string name="wednesday">"Mercoledì"</string> - <string name="thursday">"Giovedì"</string> - <string name="friday">"Venerdì"</string> - <string name="saturday">"Sabato"</string> <string name="every_weekday">"Ogni giorno feriale (lun-ven)"</string> <string name="daily">"Quotidianamente"</string> <string name="weekly">"Ogni settimana il <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Spiacenti, questo video non è valido per lo streaming su questo dispositivo."</string> <string name="VideoView_error_text_unknown">"Spiacenti. Impossibile riprodurre il video."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"mezzogiorno"</string> <string name="Noon">"Mezzogiorno"</string> <string name="midnight">"mezzanotte"</string> <string name="Midnight">"Mezzanotte"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"Domenica"</string> - <string name="day_of_week_long_monday">"Lunedì"</string> - <string name="day_of_week_long_tuesday">"Martedì"</string> - <string name="day_of_week_long_wednesday">"Mercoledì"</string> - <string name="day_of_week_long_thursday">"Giovedì"</string> - <string name="day_of_week_long_friday">"Venerdì"</string> - <string name="day_of_week_long_saturday">"Sabato"</string> - <string name="day_of_week_medium_sunday">"Dom"</string> - <string name="day_of_week_medium_monday">"Lun"</string> - <string name="day_of_week_medium_tuesday">"Mar"</string> - <string name="day_of_week_medium_wednesday">"Mer"</string> - <string name="day_of_week_medium_thursday">"Gio"</string> - <string name="day_of_week_medium_friday">"Ven"</string> - <string name="day_of_week_medium_saturday">"Sab"</string> - <string name="day_of_week_short_sunday">"Do"</string> - <string name="day_of_week_short_monday">"Lu"</string> - <string name="day_of_week_short_tuesday">"Ma"</string> - <string name="day_of_week_short_wednesday">"Me"</string> - <string name="day_of_week_short_thursday">"Gi"</string> - <string name="day_of_week_short_friday">"Ve"</string> - <string name="day_of_week_short_saturday">"Sa"</string> - <string name="day_of_week_shorter_sunday">"Do"</string> - <string name="day_of_week_shorter_monday">"Lu"</string> - <string name="day_of_week_shorter_tuesday">"Ma"</string> - <string name="day_of_week_shorter_wednesday">"Me"</string> - <string name="day_of_week_shorter_thursday">"Gi"</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"Sa"</string> - <string name="day_of_week_shortest_sunday">"D"</string> - <string name="day_of_week_shortest_monday">"Lun"</string> - <string name="day_of_week_shortest_tuesday">"M"</string> - <string name="day_of_week_shortest_wednesday">"Me"</string> - <string name="day_of_week_shortest_thursday">"G"</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"Sa"</string> - <string name="month_long_january">"Gennaio"</string> - <string name="month_long_february">"Febbraio"</string> - <string name="month_long_march">"Marzo"</string> - <string name="month_long_april">"Aprile"</string> - <string name="month_long_may">"Maggio"</string> - <string name="month_long_june">"Giugno"</string> - <string name="month_long_july">"Luglio"</string> - <string name="month_long_august">"Agosto"</string> - <string name="month_long_september">"Settembre"</string> - <string name="month_long_october">"Ottobre"</string> - <string name="month_long_november">"Novembre"</string> - <string name="month_long_december">"Dicembre"</string> - <string name="month_medium_january">"Gen"</string> - <string name="month_medium_february">"Feb"</string> - <string name="month_medium_march">"Mar"</string> - <string name="month_medium_april">"Apr"</string> - <string name="month_medium_may">"Mag"</string> - <string name="month_medium_june">"Giu"</string> - <string name="month_medium_july">"Lug"</string> - <string name="month_medium_august">"Ago"</string> - <string name="month_medium_september">"Set"</string> - <string name="month_medium_october">"Ott"</string> - <string name="month_medium_november">"Nov"</string> - <string name="month_medium_december">"Dic"</string> - <string name="month_shortest_january">"G"</string> - <string name="month_shortest_february">"F"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"Ap"</string> - <string name="month_shortest_may">"Mag"</string> - <string name="month_shortest_june">"Gi"</string> - <string name="month_shortest_july">"Lug"</string> - <string name="month_shortest_august">"Ago"</string> - <string name="month_shortest_september">"Set"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"Di"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Seleziona tutto"</string> diff --git a/core/res/res/values-ja-rJP/donottranslate-cldr.xml b/core/res/res/values-ja-rJP/donottranslate-cldr.xml new file mode 100644 index 000000000000..d2510f60132a --- /dev/null +++ b/core/res/res/values-ja-rJP/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">1月</string> + <string name="month_long_standalone_february">2月</string> + <string name="month_long_standalone_march">3月</string> + <string name="month_long_standalone_april">4月</string> + <string name="month_long_standalone_may">5月</string> + <string name="month_long_standalone_june">6月</string> + <string name="month_long_standalone_july">7月</string> + <string name="month_long_standalone_august">8月</string> + <string name="month_long_standalone_september">9月</string> + <string name="month_long_standalone_october">10月</string> + <string name="month_long_standalone_november">11月</string> + <string name="month_long_standalone_december">12月</string> + + <string name="month_long_january">1月</string> + <string name="month_long_february">2月</string> + <string name="month_long_march">3月</string> + <string name="month_long_april">4月</string> + <string name="month_long_may">5月</string> + <string name="month_long_june">6月</string> + <string name="month_long_july">7月</string> + <string name="month_long_august">8月</string> + <string name="month_long_september">9月</string> + <string name="month_long_october">10月</string> + <string name="month_long_november">11月</string> + <string name="month_long_december">12月</string> + + <string name="month_medium_january">1月</string> + <string name="month_medium_february">2月</string> + <string name="month_medium_march">3月</string> + <string name="month_medium_april">4月</string> + <string name="month_medium_may">5月</string> + <string name="month_medium_june">6月</string> + <string name="month_medium_july">7月</string> + <string name="month_medium_august">8月</string> + <string name="month_medium_september">9月</string> + <string name="month_medium_october">10月</string> + <string name="month_medium_november">11月</string> + <string name="month_medium_december">12月</string> + + <string name="month_shortest_january">1</string> + <string name="month_shortest_february">2</string> + <string name="month_shortest_march">3</string> + <string name="month_shortest_april">4</string> + <string name="month_shortest_may">5</string> + <string name="month_shortest_june">6</string> + <string name="month_shortest_july">7</string> + <string name="month_shortest_august">8</string> + <string name="month_shortest_september">9</string> + <string name="month_shortest_october">10</string> + <string name="month_shortest_november">11</string> + <string name="month_shortest_december">12</string> + + <string name="day_of_week_long_sunday">日曜日</string> + <string name="day_of_week_long_monday">月曜日</string> + <string name="day_of_week_long_tuesday">火曜日</string> + <string name="day_of_week_long_wednesday">水曜日</string> + <string name="day_of_week_long_thursday">木曜日</string> + <string name="day_of_week_long_friday">金曜日</string> + <string name="day_of_week_long_saturday">土曜日</string> + + <string name="day_of_week_medium_sunday">日</string> + <string name="day_of_week_medium_monday">月</string> + <string name="day_of_week_medium_tuesday">火</string> + <string name="day_of_week_medium_wednesday">水</string> + <string name="day_of_week_medium_thursday">木</string> + <string name="day_of_week_medium_friday">金</string> + <string name="day_of_week_medium_saturday">土</string> + + <string name="day_of_week_short_sunday">日</string> + <string name="day_of_week_short_monday">月</string> + <string name="day_of_week_short_tuesday">火</string> + <string name="day_of_week_short_wednesday">水</string> + <string name="day_of_week_short_thursday">木</string> + <string name="day_of_week_short_friday">金</string> + <string name="day_of_week_short_saturday">土</string> + + <string name="day_of_week_shortest_sunday">日</string> + <string name="day_of_week_shortest_monday">月</string> + <string name="day_of_week_shortest_tuesday">火</string> + <string name="day_of_week_shortest_wednesday">水</string> + <string name="day_of_week_shortest_thursday">木</string> + <string name="day_of_week_shortest_friday">金</string> + <string name="day_of_week_shortest_saturday">土</string> + + <string name="am">午前</string> + <string name="pm">午後</string> + <string name="yesterday">昨日</string> + <string name="today">今日</string> + <string name="tomorrow">明日</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%p%-l:%M</string> + <string name="hour_minute_cap_ampm">%p%-l:%M</string> + <string name="twelve_hour_time_format">ah:mm</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y/%m/%d</string> + <string name="numeric_date_format">yyyy/MM/dd</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%Y年%-m月%-e日</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %Y/%m/%d</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y/%m/%d</string> + <string name="month_day">%-m月%-e日</string> + <string name="month">%-B</string> + <string name="month_year">%Y年%-m月</string> + <string name="abbrev_month_day">%-m月%-e日</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y年%-m月</string> + <string name="time1_time2">%1$s~%2$s</string> + <string name="date1_date2">%2$s~%5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s~%7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s/%3$s(%1$s)~%7$s/%8$s(%6$s)</string> + <string name="numeric_mdy1_mdy2">%4$s/%2$s/%3$s~%9$s/%7$s/%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s/%2$s/%3$s(%1$s)~%9$s/%7$s/%8$s(%6$s)</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s/%2$s/%3$s(%1$s)~%10$s %9$s/%7$s/%8$s(%6$s)</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s/%3$s~%10$s %7$s/%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s/%3$s(%1$s)~%10$s %7$s/%8$s(%6$s)</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s/%2$s/%3$s~%10$s %9$s/%7$s/%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s(%1$s)~%6$s %5$s(%4$s)</string> + <string name="wday1_date1_wday2_date2">%2$s(%1$s)~%5$s(%4$s)</string> + <string name="date1_time1_date2_time2">%3$s %2$s~%6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s(%2$s)</string> + <string name="wday_date">%3$s(%2$s)</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s%3$s日~%7$s%8$s日</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s%3$s日~%10$s %7$s%8$s日</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s%3$s日~%10$s %7$s%8$s日</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s)</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s年%2$s%3$s日(%1$s)~%9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_md1_md2">%2$s%3$s日~%8$s日</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s%8$s日</string> + <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml new file mode 100644 index 000000000000..d2510f60132a --- /dev/null +++ b/core/res/res/values-ja/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">1月</string> + <string name="month_long_standalone_february">2月</string> + <string name="month_long_standalone_march">3月</string> + <string name="month_long_standalone_april">4月</string> + <string name="month_long_standalone_may">5月</string> + <string name="month_long_standalone_june">6月</string> + <string name="month_long_standalone_july">7月</string> + <string name="month_long_standalone_august">8月</string> + <string name="month_long_standalone_september">9月</string> + <string name="month_long_standalone_october">10月</string> + <string name="month_long_standalone_november">11月</string> + <string name="month_long_standalone_december">12月</string> + + <string name="month_long_january">1月</string> + <string name="month_long_february">2月</string> + <string name="month_long_march">3月</string> + <string name="month_long_april">4月</string> + <string name="month_long_may">5月</string> + <string name="month_long_june">6月</string> + <string name="month_long_july">7月</string> + <string name="month_long_august">8月</string> + <string name="month_long_september">9月</string> + <string name="month_long_october">10月</string> + <string name="month_long_november">11月</string> + <string name="month_long_december">12月</string> + + <string name="month_medium_january">1月</string> + <string name="month_medium_february">2月</string> + <string name="month_medium_march">3月</string> + <string name="month_medium_april">4月</string> + <string name="month_medium_may">5月</string> + <string name="month_medium_june">6月</string> + <string name="month_medium_july">7月</string> + <string name="month_medium_august">8月</string> + <string name="month_medium_september">9月</string> + <string name="month_medium_october">10月</string> + <string name="month_medium_november">11月</string> + <string name="month_medium_december">12月</string> + + <string name="month_shortest_january">1</string> + <string name="month_shortest_february">2</string> + <string name="month_shortest_march">3</string> + <string name="month_shortest_april">4</string> + <string name="month_shortest_may">5</string> + <string name="month_shortest_june">6</string> + <string name="month_shortest_july">7</string> + <string name="month_shortest_august">8</string> + <string name="month_shortest_september">9</string> + <string name="month_shortest_october">10</string> + <string name="month_shortest_november">11</string> + <string name="month_shortest_december">12</string> + + <string name="day_of_week_long_sunday">日曜日</string> + <string name="day_of_week_long_monday">月曜日</string> + <string name="day_of_week_long_tuesday">火曜日</string> + <string name="day_of_week_long_wednesday">水曜日</string> + <string name="day_of_week_long_thursday">木曜日</string> + <string name="day_of_week_long_friday">金曜日</string> + <string name="day_of_week_long_saturday">土曜日</string> + + <string name="day_of_week_medium_sunday">日</string> + <string name="day_of_week_medium_monday">月</string> + <string name="day_of_week_medium_tuesday">火</string> + <string name="day_of_week_medium_wednesday">水</string> + <string name="day_of_week_medium_thursday">木</string> + <string name="day_of_week_medium_friday">金</string> + <string name="day_of_week_medium_saturday">土</string> + + <string name="day_of_week_short_sunday">日</string> + <string name="day_of_week_short_monday">月</string> + <string name="day_of_week_short_tuesday">火</string> + <string name="day_of_week_short_wednesday">水</string> + <string name="day_of_week_short_thursday">木</string> + <string name="day_of_week_short_friday">金</string> + <string name="day_of_week_short_saturday">土</string> + + <string name="day_of_week_shortest_sunday">日</string> + <string name="day_of_week_shortest_monday">月</string> + <string name="day_of_week_shortest_tuesday">火</string> + <string name="day_of_week_shortest_wednesday">水</string> + <string name="day_of_week_shortest_thursday">木</string> + <string name="day_of_week_shortest_friday">金</string> + <string name="day_of_week_shortest_saturday">土</string> + + <string name="am">午前</string> + <string name="pm">午後</string> + <string name="yesterday">昨日</string> + <string name="today">今日</string> + <string name="tomorrow">明日</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%p%-l:%M</string> + <string name="hour_minute_cap_ampm">%p%-l:%M</string> + <string name="twelve_hour_time_format">ah:mm</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y/%m/%d</string> + <string name="numeric_date_format">yyyy/MM/dd</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%Y年%-m月%-e日</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %Y/%m/%d</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y/%m/%d</string> + <string name="month_day">%-m月%-e日</string> + <string name="month">%-B</string> + <string name="month_year">%Y年%-m月</string> + <string name="abbrev_month_day">%-m月%-e日</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y年%-m月</string> + <string name="time1_time2">%1$s~%2$s</string> + <string name="date1_date2">%2$s~%5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s~%7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s/%3$s(%1$s)~%7$s/%8$s(%6$s)</string> + <string name="numeric_mdy1_mdy2">%4$s/%2$s/%3$s~%9$s/%7$s/%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s/%2$s/%3$s(%1$s)~%9$s/%7$s/%8$s(%6$s)</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s/%2$s/%3$s(%1$s)~%10$s %9$s/%7$s/%8$s(%6$s)</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s/%3$s~%10$s %7$s/%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s/%3$s(%1$s)~%10$s %7$s/%8$s(%6$s)</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s/%2$s/%3$s~%10$s %9$s/%7$s/%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s(%1$s)~%6$s %5$s(%4$s)</string> + <string name="wday1_date1_wday2_date2">%2$s(%1$s)~%5$s(%4$s)</string> + <string name="date1_time1_date2_time2">%3$s %2$s~%6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s(%2$s)</string> + <string name="wday_date">%3$s(%2$s)</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s%3$s日~%7$s%8$s日</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s%3$s日~%10$s %7$s%8$s日</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s%3$s日~%10$s %7$s%8$s日</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s)</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s年%2$s%3$s日(%1$s)~%9$s年%7$s%8$s日(%6$s)</string> + <string name="same_month_md1_md2">%2$s%3$s日~%8$s日</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s%8$s日</string> + <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s)</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ja/donottranslate.xml b/core/res/res/values-ja/donottranslate.xml new file mode 100644 index 000000000000..f7c3566f644c --- /dev/null +++ b/core/res/res/values-ja/donottranslate.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Default text encoding for WebSettings. --> + <string name="default_text_encoding">Shift_JIS</string> +</resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 8da040e410f6..a2e3e51846c4 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"地域/言語やフォントのサイズなど、現在の設定の変更をアプリケーションに許可します。"</string> <string name="permlab_restartPackages">"他のアプリケーションの再起動"</string> <string name="permdesc_restartPackages">"他のアプリケーションの強制的な再起動をアプリケーションに許可します。"</string> - <string name="permlab_setProcessForeground">"停止の阻止"</string> - <string name="permdesc_setProcessForeground">"フォアグラウンドでプロセスを実行して、強制終了できないようにすることをアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。"</string> <string name="permlab_forceBack">"アプリケーションの強制終了"</string> <string name="permdesc_forceBack">"フォアグラウンドで実行されている操作を強制終了して戻ることをアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。"</string> <string name="permlab_dump">"システムの内部状態の取得"</string> <string name="permdesc_dump">"システムの内部状態の取得をアプリケーションに許可します。悪意のあるアプリケーションが、通常は必要としない広範囲にわたる非公開の機密情報を取得する恐れがあります。"</string> - <string name="permlab_addSystemService">"低レベルサービスの公開"</string> - <string name="permdesc_addSystemService">"独自の低レベルのシステムサービスを公開することをアプリケーションに許可します。悪意のあるアプリケーションがシステムを乗っ取って、データの盗用や破壊をする恐れがあります。"</string> <string name="permlab_runSetActivityWatcher">"起動中のすべてのアプリケーションの監視と制御"</string> <string name="permdesc_runSetActivityWatcher">"システムが起動する操作の監視と制御をアプリケーションに許可します。悪意のあるアプリケーションがシステムを完全に破壊する恐れがあります。この許可は開発にのみ必要で、携帯電話の通常の使用にはまったく必要ありません。"</string> <string name="permlab_broadcastPackageRemoved">"パッケージ削除ブロードキャストの送信"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"実行するプロセス数の上限の制御をアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。"</string> <string name="permlab_setAlwaysFinish">"バックグラウンドアプリケーションをすべて終了する"</string> <string name="permdesc_setAlwaysFinish">"バックグラウンドになり次第必ず操作を終了させるかどうかの制御をアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。"</string> - <string name="permlab_fotaUpdate">"システムアップデートの自動インストール"</string> - <string name="permdesc_fotaUpdate">"保留中のシステムアップデートに関する通知の受信とインストールの開始をアプリケーションに許可します。悪意のあるアプリケーションが許可なく更新を行ってシステムを破壊したり、更新処理を妨害する恐れがあります。"</string> <string name="permlab_batteryStats">"電池統計情報の変国"</string> <string name="permdesc_batteryStats">"収集した電池統計情報の変更を許可します。通常のアプリケーションでは使用しません。"</string> <string name="permlab_internalSystemWindow">"未許可のウィンドウの表示"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"パスワード"</string> <string name="lockscreen_glogin_submit_button">"ログイン"</string> <string name="lockscreen_glogin_invalid_input">"ユーザー名またはパスワードが正しくありません。"</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"通知を消去"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"Enter"</string> <string name="menu_delete_shortcut_label">"Del"</string> <string name="search_go">"検索"</string> - <string name="today">"今日"</string> - <string name="yesterday">"昨日"</string> - <string name="tomorrow">"明日"</string> <string name="oneMonthDurationPast">"1か月前"</string> <string name="beforeOneMonthDurationPast">"1か月前"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"週間"</string> <string name="year">"年"</string> <string name="years">"年"</string> - <string name="sunday">"日曜日"</string> - <string name="monday">"月曜日"</string> - <string name="tuesday">"火曜日"</string> - <string name="wednesday">"水曜日"</string> - <string name="thursday">"木曜日"</string> - <string name="friday">"金曜日"</string> - <string name="saturday">"土曜日"</string> <string name="every_weekday">"平日(月~金)"</string> <string name="daily">"毎日"</string> <string name="weekly">"毎週<xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"この動画はご使用の端末でストリーミングできません。"</string> <string name="VideoView_error_text_unknown">"この動画は再生できません。"</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="YEAR">%Y</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%3$s</xliff:g>~<xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g><xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g>~<xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g>~<xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> - <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g><xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g>、<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g>、<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g><xliff:g id="DAY">d</xliff:g>'日 '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>'年'<xliff:g id="MONTH">MMMM</xliff:g>'月'<xliff:g id="DAY">d</xliff:g>'日'"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>'/'<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>'年'"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'/'<xliff:g id="MONTH">MMM</xliff:g>'/'<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"正午"</string> <string name="Noon">"正午"</string> <string name="midnight">"午前0時"</string> <string name="Midnight">"午前0時"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g><xliff:g id="DAY">%-d</xliff:g>日"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g>年<xliff:g id="MONTH">%B</xliff:g><xliff:g id="DAY">%-d</xliff:g>日"</string> - <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g>年<xliff:g id="MONTH">%B</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g>/<xliff:g id="MONTH">%B</xliff:g>/<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>月<xliff:g id="DAY1">%3$s</xliff:g>日~<xliff:g id="DAY2">%8$s</xliff:g>日"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> - <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g><xliff:g id="TIME1">%5$s</xliff:g>~<xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g><xliff:g id="WEEKDAY2">%6$s</xliff:g><xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g>/<xliff:g id="MONTH">%b</xliff:g>/<xliff:g id="DAY">%-d</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="YEAR">%Y</xliff:g>年<xliff:g id="MONTH">%b</xliff:g>月"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g>/<xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"日曜日"</string> - <string name="day_of_week_long_monday">"月曜日"</string> - <string name="day_of_week_long_tuesday">"火曜日"</string> - <string name="day_of_week_long_wednesday">"水曜日"</string> - <string name="day_of_week_long_thursday">"木曜日"</string> - <string name="day_of_week_long_friday">"金曜日"</string> - <string name="day_of_week_long_saturday">"土曜日"</string> - <string name="day_of_week_medium_sunday">"(日)"</string> - <string name="day_of_week_medium_monday">"(月)"</string> - <string name="day_of_week_medium_tuesday">"(火)"</string> - <string name="day_of_week_medium_wednesday">"(水)"</string> - <string name="day_of_week_medium_thursday">"(木)"</string> - <string name="day_of_week_medium_friday">"(金)"</string> - <string name="day_of_week_medium_saturday">"(土)"</string> - <string name="day_of_week_short_sunday">"日"</string> - <string name="day_of_week_short_monday">"月"</string> - <string name="day_of_week_short_tuesday">"火"</string> - <string name="day_of_week_short_wednesday">"水"</string> - <string name="day_of_week_short_thursday">"木"</string> - <string name="day_of_week_short_friday">"金"</string> - <string name="day_of_week_short_saturday">"土"</string> - <string name="day_of_week_shorter_sunday">"日"</string> - <string name="day_of_week_shorter_monday">"月"</string> - <string name="day_of_week_shorter_tuesday">"火"</string> - <string name="day_of_week_shorter_wednesday">"水"</string> - <string name="day_of_week_shorter_thursday">"木"</string> - <string name="day_of_week_shorter_friday">"金"</string> - <string name="day_of_week_shorter_saturday">"土"</string> - <string name="day_of_week_shortest_sunday">"日"</string> - <string name="day_of_week_shortest_monday">"月"</string> - <string name="day_of_week_shortest_tuesday">"火"</string> - <string name="day_of_week_shortest_wednesday">"水"</string> - <string name="day_of_week_shortest_thursday">"火"</string> - <string name="day_of_week_shortest_friday">"金"</string> - <string name="day_of_week_shortest_saturday">"土"</string> - <string name="month_long_january">"1月"</string> - <string name="month_long_february">"2月"</string> - <string name="month_long_march">"3月"</string> - <string name="month_long_april">"4月"</string> - <string name="month_long_may">"5月"</string> - <string name="month_long_june">"6月"</string> - <string name="month_long_july">"7月"</string> - <string name="month_long_august">"8月"</string> - <string name="month_long_september">"9月"</string> - <string name="month_long_october">"10月"</string> - <string name="month_long_november">"11月"</string> - <string name="month_long_december">"12月"</string> - <string name="month_medium_january">"1"</string> - <string name="month_medium_february">"2"</string> - <string name="month_medium_march">"3"</string> - <string name="month_medium_april">"4"</string> - <string name="month_medium_may">"5"</string> - <string name="month_medium_june">"6"</string> - <string name="month_medium_july">"7"</string> - <string name="month_medium_august">"8"</string> - <string name="month_medium_september">"9"</string> - <string name="month_medium_october">"10"</string> - <string name="month_medium_november">"11"</string> - <string name="month_medium_december">"12"</string> - <string name="month_shortest_january">"1"</string> - <string name="month_shortest_february">"2"</string> - <string name="month_shortest_march">"3"</string> - <string name="month_shortest_april">"4"</string> - <string name="month_shortest_may">"5"</string> - <string name="month_shortest_june">"6"</string> - <string name="month_shortest_july">"7"</string> - <string name="month_shortest_august">"8"</string> - <string name="month_shortest_september">"9"</string> - <string name="month_shortest_october">"10"</string> - <string name="month_shortest_november">"11"</string> - <string name="month_shortest_december">"12"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"すべて選択"</string> diff --git a/core/res/res/values-ko-rKR/donottranslate-cldr.xml b/core/res/res/values-ko-rKR/donottranslate-cldr.xml new file mode 100644 index 000000000000..57cd35692e36 --- /dev/null +++ b/core/res/res/values-ko-rKR/donottranslate-cldr.xml @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">1월</string> + <string name="month_long_standalone_february">2월</string> + <string name="month_long_standalone_march">3월</string> + <string name="month_long_standalone_april">4월</string> + <string name="month_long_standalone_may">5월</string> + <string name="month_long_standalone_june">6월</string> + <string name="month_long_standalone_july">7월</string> + <string name="month_long_standalone_august">8월</string> + <string name="month_long_standalone_september">9월</string> + <string name="month_long_standalone_october">10월</string> + <string name="month_long_standalone_november">11월</string> + <string name="month_long_standalone_december">12월</string> + + <string name="month_long_january">1월</string> + <string name="month_long_february">2월</string> + <string name="month_long_march">3월</string> + <string name="month_long_april">4월</string> + <string name="month_long_may">5월</string> + <string name="month_long_june">6월</string> + <string name="month_long_july">7월</string> + <string name="month_long_august">8월</string> + <string name="month_long_september">9월</string> + <string name="month_long_october">10월</string> + <string name="month_long_november">11월</string> + <string name="month_long_december">12월</string> + + + <string name="month_shortest_january">1월</string> + <string name="month_shortest_february">2월</string> + <string name="month_shortest_march">3월</string> + <string name="month_shortest_april">4월</string> + <string name="month_shortest_may">5월</string> + <string name="month_shortest_june">6월</string> + <string name="month_shortest_july">7월</string> + <string name="month_shortest_august">8월</string> + <string name="month_shortest_september">9월</string> + <string name="month_shortest_october">10월</string> + <string name="month_shortest_november">11월</string> + <string name="month_shortest_december">12월</string> + + <string name="day_of_week_long_sunday">일요일</string> + <string name="day_of_week_long_monday">월요일</string> + <string name="day_of_week_long_tuesday">화요일</string> + <string name="day_of_week_long_wednesday">수요일</string> + <string name="day_of_week_long_thursday">목요일</string> + <string name="day_of_week_long_friday">금요일</string> + <string name="day_of_week_long_saturday">토요일</string> + + <string name="day_of_week_medium_sunday">일</string> + <string name="day_of_week_medium_monday">월</string> + <string name="day_of_week_medium_tuesday">화</string> + <string name="day_of_week_medium_wednesday">수</string> + <string name="day_of_week_medium_thursday">목</string> + <string name="day_of_week_medium_friday">금</string> + <string name="day_of_week_medium_saturday">토</string> + + <string name="day_of_week_short_sunday">일</string> + <string name="day_of_week_short_monday">월</string> + <string name="day_of_week_short_tuesday">화</string> + <string name="day_of_week_short_wednesday">수</string> + <string name="day_of_week_short_thursday">목</string> + <string name="day_of_week_short_friday">금</string> + <string name="day_of_week_short_saturday">토</string> + + <string name="day_of_week_shortest_sunday">일</string> + <string name="day_of_week_shortest_monday">월</string> + <string name="day_of_week_shortest_tuesday">화</string> + <string name="day_of_week_shortest_wednesday">수</string> + <string name="day_of_week_shortest_thursday">목</string> + <string name="day_of_week_shortest_friday">금</string> + <string name="day_of_week_shortest_saturday">토</string> + + <string name="am">오전</string> + <string name="pm">오후</string> + <string name="yesterday">어제</string> + <string name="today">오늘</string> + <string name="tomorrow">내일</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y. %-m. %-e.</string> + <string name="numeric_date_format">yyyy. M. d.</string> + <string name="numeric_date_template">"%s. %s. %s."</string> + <string name="month_day_year">%Y년 %-m월 %-e일</string> + <string name="time_of_day">%p %-l:%M:%S</string> + <string name="date_and_time">%p %-l:%M:%S %Y. %-m. %-e.</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y. %-m. %-e.</string> + <string name="month_day">%B %-e일</string> + <string name="month">%-B</string> + <string name="month_year">%Y년 %B</string> + <string name="abbrev_month_day">%b %-e일</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y년 %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s. %3$s ~ %7$s. %8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s. %3$s %1$s ~ %7$s. %8$s %6$s</string> + <string name="numeric_mdy1_mdy2">%4$s. %2$s. %3$s. ~ %9$s. %7$s. %8$s.</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s. %2$s. %3$s. %1$s ~ %9$s. %7$s. %8$s. %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s. %2$s. %3$s. %1$s – %10$s %9$s. %7$s. %8$s. %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s. %3$s. – %10$s %7$s. %8$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s. %3$s. (%1$s) – %10$s %7$s. %8$s. (%6$s)</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s. %2$s. %3$s. – %10$s %9$s. %7$s. %8$s.</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s (%1$s) – %6$s %5$s (%4$s)</string> + <string name="wday1_date1_wday2_date2">%2$s (%1$s) – %5$s (%4$s)</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s (%2$s)</string> + <string name="wday_date">%3$s (%2$s)</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s일 – %7$s %8$s일</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s)</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s일 – %10$s %7$s %8$s일</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s일 – %10$s %7$s %8$s일</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s)</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s)</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s년 %2$s %3$s일 %1$s – %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_md1_md2">%2$s %3$s일 ~ %8$s일</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s)</string> + <string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s %8$s일</string> + <string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s %8$s일 %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml new file mode 100644 index 000000000000..57cd35692e36 --- /dev/null +++ b/core/res/res/values-ko/donottranslate-cldr.xml @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">1월</string> + <string name="month_long_standalone_february">2월</string> + <string name="month_long_standalone_march">3월</string> + <string name="month_long_standalone_april">4월</string> + <string name="month_long_standalone_may">5월</string> + <string name="month_long_standalone_june">6월</string> + <string name="month_long_standalone_july">7월</string> + <string name="month_long_standalone_august">8월</string> + <string name="month_long_standalone_september">9월</string> + <string name="month_long_standalone_october">10월</string> + <string name="month_long_standalone_november">11월</string> + <string name="month_long_standalone_december">12월</string> + + <string name="month_long_january">1월</string> + <string name="month_long_february">2월</string> + <string name="month_long_march">3월</string> + <string name="month_long_april">4월</string> + <string name="month_long_may">5월</string> + <string name="month_long_june">6월</string> + <string name="month_long_july">7월</string> + <string name="month_long_august">8월</string> + <string name="month_long_september">9월</string> + <string name="month_long_october">10월</string> + <string name="month_long_november">11월</string> + <string name="month_long_december">12월</string> + + + <string name="month_shortest_january">1월</string> + <string name="month_shortest_february">2월</string> + <string name="month_shortest_march">3월</string> + <string name="month_shortest_april">4월</string> + <string name="month_shortest_may">5월</string> + <string name="month_shortest_june">6월</string> + <string name="month_shortest_july">7월</string> + <string name="month_shortest_august">8월</string> + <string name="month_shortest_september">9월</string> + <string name="month_shortest_october">10월</string> + <string name="month_shortest_november">11월</string> + <string name="month_shortest_december">12월</string> + + <string name="day_of_week_long_sunday">일요일</string> + <string name="day_of_week_long_monday">월요일</string> + <string name="day_of_week_long_tuesday">화요일</string> + <string name="day_of_week_long_wednesday">수요일</string> + <string name="day_of_week_long_thursday">목요일</string> + <string name="day_of_week_long_friday">금요일</string> + <string name="day_of_week_long_saturday">토요일</string> + + <string name="day_of_week_medium_sunday">일</string> + <string name="day_of_week_medium_monday">월</string> + <string name="day_of_week_medium_tuesday">화</string> + <string name="day_of_week_medium_wednesday">수</string> + <string name="day_of_week_medium_thursday">목</string> + <string name="day_of_week_medium_friday">금</string> + <string name="day_of_week_medium_saturday">토</string> + + <string name="day_of_week_short_sunday">일</string> + <string name="day_of_week_short_monday">월</string> + <string name="day_of_week_short_tuesday">화</string> + <string name="day_of_week_short_wednesday">수</string> + <string name="day_of_week_short_thursday">목</string> + <string name="day_of_week_short_friday">금</string> + <string name="day_of_week_short_saturday">토</string> + + <string name="day_of_week_shortest_sunday">일</string> + <string name="day_of_week_shortest_monday">월</string> + <string name="day_of_week_shortest_tuesday">화</string> + <string name="day_of_week_shortest_wednesday">수</string> + <string name="day_of_week_shortest_thursday">목</string> + <string name="day_of_week_shortest_friday">금</string> + <string name="day_of_week_shortest_saturday">토</string> + + <string name="am">오전</string> + <string name="pm">오후</string> + <string name="yesterday">어제</string> + <string name="today">오늘</string> + <string name="tomorrow">내일</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y. %-m. %-e.</string> + <string name="numeric_date_format">yyyy. M. d.</string> + <string name="numeric_date_template">"%s. %s. %s."</string> + <string name="month_day_year">%Y년 %-m월 %-e일</string> + <string name="time_of_day">%p %-l:%M:%S</string> + <string name="date_and_time">%p %-l:%M:%S %Y. %-m. %-e.</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y. %-m. %-e.</string> + <string name="month_day">%B %-e일</string> + <string name="month">%-B</string> + <string name="month_year">%Y년 %B</string> + <string name="abbrev_month_day">%b %-e일</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y년 %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s. %3$s ~ %7$s. %8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s. %3$s %1$s ~ %7$s. %8$s %6$s</string> + <string name="numeric_mdy1_mdy2">%4$s. %2$s. %3$s. ~ %9$s. %7$s. %8$s.</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s. %2$s. %3$s. %1$s ~ %9$s. %7$s. %8$s. %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s. %2$s. %3$s. %1$s – %10$s %9$s. %7$s. %8$s. %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s. %3$s. – %10$s %7$s. %8$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s. %3$s. (%1$s) – %10$s %7$s. %8$s. (%6$s)</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s. %2$s. %3$s. – %10$s %9$s. %7$s. %8$s.</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s (%1$s) – %6$s %5$s (%4$s)</string> + <string name="wday1_date1_wday2_date2">%2$s (%1$s) – %5$s (%4$s)</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s (%2$s)</string> + <string name="wday_date">%3$s (%2$s)</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s일 – %7$s %8$s일</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s)</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s일 – %10$s %7$s %8$s일</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s일 – %10$s %7$s %8$s일</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s)</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s)</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s년 %2$s %3$s일 %1$s – %9$s년 %7$s %8$s일 %6$s</string> + <string name="same_month_md1_md2">%2$s %3$s일 ~ %8$s일</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s)</string> + <string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s %8$s일</string> + <string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s %8$s일 %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 96b897a495d5..e2c6e570fe27 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"응용프로그램이 로케일 또는 전체 글꼴 크기 같은 현재 구성을 변경할 수 있습니다."</string> <string name="permlab_restartPackages">"다른 응용프로그램 다시 시작"</string> <string name="permdesc_restartPackages">"응용프로그램이 다른 응용프로그램을 강제로 다시 시작할 수 있습니다."</string> - <string name="permlab_setProcessForeground">"중지되지 않도록 하기"</string> - <string name="permdesc_setProcessForeground">"응용프로그램이 프로세스를 포그라운드에서 실행되도록 하여 프로세스를 중지할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> <string name="permlab_forceBack">"강제로 응용프로그램 닫기"</string> <string name="permdesc_forceBack">"응용프로그램이 포그라운드에 있는 활동을 강제로 닫을 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> <string name="permlab_dump">"시스템 내부 상태 검색"</string> <string name="permdesc_dump">"응용프로그램이 시스템의 내부 상태를 검색할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 일반적으로 필요하지 않은 다양한 개인 정보와 보안 정보를 검색할 수 있습니다."</string> - <string name="permlab_addSystemService">"하위 수준 서비스 게시"</string> - <string name="permdesc_addSystemService">"응용프로그램이 자체 하위 수준 시스템 서비스를 게시할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 하이재킹하거나 시스템의 데이터를 도용 또는 손상시킬 수 있습니다."</string> <string name="permlab_runSetActivityWatcher">"실행 중인 모든 응용프로그램 모니터링 및 제어"</string> <string name="permdesc_runSetActivityWatcher">"응용프로그램이 시스템에서 활동이 시작되는 방식을 모니터링하고 제어할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 전화기 사용 시에는 필요하지 않습니다."</string> <string name="permlab_broadcastPackageRemoved">"패키지 제거 브로드캐스트 보내기"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"응용프로그램이 실행할 최대 프로세스 수를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> <string name="permlab_setAlwaysFinish">"모든 백그라운드 응용프로그램이 닫히도록 하기"</string> <string name="permdesc_setAlwaysFinish">"응용프로그램이 백그라운드로 이동한 활동을 항상 바로 마칠지 여부를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다."</string> - <string name="permlab_fotaUpdate">"시스템 업데이트 자동으로 설치"</string> - <string name="permdesc_fotaUpdate">"응용프로그램이 대기 중인 시스템 업데이트에 대한 알림을 받고 설치를 트리거할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 인증되지 않은 업데이트로 시스템을 손상시키거나 업데이트 절차를 방해할 수 있습니다."</string> <string name="permlab_batteryStats">"배터리 통계 수정"</string> <string name="permdesc_batteryStats">"수집된 배터리 통계를 수정할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string> <string name="permlab_internalSystemWindow">"인증되지 않은 창 표시"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"비밀번호"</string> <string name="lockscreen_glogin_submit_button">"로그인"</string> <string name="lockscreen_glogin_invalid_input">"사용자 이름 또는 비밀번호가 잘못되었습니다."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"알림 지우기"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"입력"</string> <string name="menu_delete_shortcut_label">"삭제"</string> <string name="search_go">"검색"</string> - <string name="today">"오늘"</string> - <string name="yesterday">"어제"</string> - <string name="tomorrow">"내일"</string> <string name="oneMonthDurationPast">"한 달 전"</string> <string name="beforeOneMonthDurationPast">"한 달 전"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"주"</string> <string name="year">"년"</string> <string name="years">"년"</string> - <string name="sunday">"일요일"</string> - <string name="monday">"월요일"</string> - <string name="tuesday">"화요일"</string> - <string name="wednesday">"수요일"</string> - <string name="thursday">"목요일"</string> - <string name="friday">"금요일"</string> - <string name="saturday">"토요일"</string> <string name="every_weekday">"주중 매일(월-금)"</string> <string name="daily">"매일"</string> <string name="weekly">"매주 <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"죄송합니다. 이 기기로의 스트리밍에 적합하지 않은 동영상입니다."</string> <string name="VideoView_error_text_unknown">"죄송합니다. 동영상을 재생할 수 없습니다."</string> <string name="VideoView_error_button">"확인"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="DAY">d</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="DAY">d</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"정오"</string> <string name="Noon">"정오"</string> <string name="midnight">"자정"</string> <string name="Midnight">"자정"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g>, <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g>, <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> <xliff:g id="MONTH">%b</xliff:g>, <xliff:g id="DAY">%-d</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"일요일"</string> - <string name="day_of_week_long_monday">"월요일"</string> - <string name="day_of_week_long_tuesday">"화요일"</string> - <string name="day_of_week_long_wednesday">"수요일"</string> - <string name="day_of_week_long_thursday">"목요일"</string> - <string name="day_of_week_long_friday">"금요일"</string> - <string name="day_of_week_long_saturday">"토요일"</string> - <string name="day_of_week_medium_sunday">"일요일"</string> - <string name="day_of_week_medium_monday">"월"</string> - <string name="day_of_week_medium_tuesday">"화"</string> - <string name="day_of_week_medium_wednesday">"수"</string> - <string name="day_of_week_medium_thursday">"목"</string> - <string name="day_of_week_medium_friday">"금"</string> - <string name="day_of_week_medium_saturday">"토"</string> - <string name="day_of_week_short_sunday">"일"</string> - <string name="day_of_week_short_monday">"월"</string> - <string name="day_of_week_short_tuesday">"화"</string> - <string name="day_of_week_short_wednesday">"수"</string> - <string name="day_of_week_short_thursday">"목"</string> - <string name="day_of_week_short_friday">"금"</string> - <string name="day_of_week_short_saturday">"토"</string> - <string name="day_of_week_shorter_sunday">"일"</string> - <string name="day_of_week_shorter_monday">"월"</string> - <string name="day_of_week_shorter_tuesday">"화"</string> - <string name="day_of_week_shorter_wednesday">"수"</string> - <string name="day_of_week_shorter_thursday">"목"</string> - <string name="day_of_week_shorter_friday">"금"</string> - <string name="day_of_week_shorter_saturday">"토"</string> - <string name="day_of_week_shortest_sunday">"일"</string> - <string name="day_of_week_shortest_monday">"3월"</string> - <string name="day_of_week_shortest_tuesday">"목"</string> - <string name="day_of_week_shortest_wednesday">"수"</string> - <string name="day_of_week_shortest_thursday">"목"</string> - <string name="day_of_week_shortest_friday">"금"</string> - <string name="day_of_week_shortest_saturday">"토"</string> - <string name="month_long_january">"1월"</string> - <string name="month_long_february">"2월"</string> - <string name="month_long_march">"3월"</string> - <string name="month_long_april">"4월"</string> - <string name="month_long_may">"5월"</string> - <string name="month_long_june">"6월"</string> - <string name="month_long_july">"7월"</string> - <string name="month_long_august">"8월"</string> - <string name="month_long_september">"9월"</string> - <string name="month_long_october">"10월"</string> - <string name="month_long_november">"11월"</string> - <string name="month_long_december">"12월"</string> - <string name="month_medium_january">"1월"</string> - <string name="month_medium_february">"2월"</string> - <string name="month_medium_march">"3월"</string> - <string name="month_medium_april">"4월"</string> - <string name="month_medium_may">"5월"</string> - <string name="month_medium_june">"6월"</string> - <string name="month_medium_july">"7월"</string> - <string name="month_medium_august">"8월"</string> - <string name="month_medium_september">"9월"</string> - <string name="month_medium_october">"10월"</string> - <string name="month_medium_november">"11월"</string> - <string name="month_medium_december">"12월"</string> - <string name="month_shortest_january">"1월"</string> - <string name="month_shortest_february">"금"</string> - <string name="month_shortest_march">"3월"</string> - <string name="month_shortest_april">"4월"</string> - <string name="month_shortest_may">"5월"</string> - <string name="month_shortest_june">"6월"</string> - <string name="month_shortest_july">"7월"</string> - <string name="month_shortest_august">"8월"</string> - <string name="month_shortest_september">"9월"</string> - <string name="month_shortest_october">"10월"</string> - <string name="month_shortest_november">"11월"</string> - <string name="month_shortest_december">"12월"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"모두 선택"</string> diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml new file mode 100644 index 000000000000..20d58e05fea5 --- /dev/null +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Sausis</string> + <string name="month_long_standalone_february">Vasaris</string> + <string name="month_long_standalone_march">Kovas</string> + <string name="month_long_standalone_april">Balandis</string> + <string name="month_long_standalone_may">Gegužė</string> + <string name="month_long_standalone_june">Birželis</string> + <string name="month_long_standalone_july">Liepa</string> + <string name="month_long_standalone_august">Rugpjūtis</string> + <string name="month_long_standalone_september">Rugsėjis</string> + <string name="month_long_standalone_october">Spalis</string> + <string name="month_long_standalone_november">Lapkritis</string> + <string name="month_long_standalone_december">Gruodis</string> + + <string name="month_long_january">sausio</string> + <string name="month_long_february">vasario</string> + <string name="month_long_march">kovo</string> + <string name="month_long_april">balandžio</string> + <string name="month_long_may">gegužės</string> + <string name="month_long_june">birželio</string> + <string name="month_long_july">liepos</string> + <string name="month_long_august">rugpjūčio</string> + <string name="month_long_september">rugsėjo</string> + <string name="month_long_october">spalio</string> + <string name="month_long_november">lapkričio</string> + <string name="month_long_december">gruodžio</string> + + <string name="month_medium_january">Sau</string> + <string name="month_medium_february">Vas</string> + <string name="month_medium_march">Kov</string> + <string name="month_medium_april">Bal</string> + <string name="month_medium_may">Geg</string> + <string name="month_medium_june">Bir</string> + <string name="month_medium_july">Lie</string> + <string name="month_medium_august">Rgp</string> + <string name="month_medium_september">Rgs</string> + <string name="month_medium_october">Spl</string> + <string name="month_medium_november">Lap</string> + <string name="month_medium_december">Grd</string> + + <string name="month_shortest_january">S</string> + <string name="month_shortest_february">V</string> + <string name="month_shortest_march">K</string> + <string name="month_shortest_april">B</string> + <string name="month_shortest_may">G</string> + <string name="month_shortest_june">B</string> + <string name="month_shortest_july">L</string> + <string name="month_shortest_august">R</string> + <string name="month_shortest_september">R</string> + <string name="month_shortest_october">S</string> + <string name="month_shortest_november">L</string> + <string name="month_shortest_december">G</string> + + <string name="day_of_week_long_sunday">sekmadienis</string> + <string name="day_of_week_long_monday">pirmadienis</string> + <string name="day_of_week_long_tuesday">antradienis</string> + <string name="day_of_week_long_wednesday">trečiadienis</string> + <string name="day_of_week_long_thursday">ketvirtadienis</string> + <string name="day_of_week_long_friday">penktadienis</string> + <string name="day_of_week_long_saturday">šeštadienis</string> + + <string name="day_of_week_medium_sunday">Sk</string> + <string name="day_of_week_medium_monday">Pr</string> + <string name="day_of_week_medium_tuesday">An</string> + <string name="day_of_week_medium_wednesday">Tr</string> + <string name="day_of_week_medium_thursday">Kt</string> + <string name="day_of_week_medium_friday">Pn</string> + <string name="day_of_week_medium_saturday">Št</string> + + <string name="day_of_week_short_sunday">Sk</string> + <string name="day_of_week_short_monday">Pr</string> + <string name="day_of_week_short_tuesday">An</string> + <string name="day_of_week_short_wednesday">Tr</string> + <string name="day_of_week_short_thursday">Kt</string> + <string name="day_of_week_short_friday">Pn</string> + <string name="day_of_week_short_saturday">Št</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">A</string> + <string name="day_of_week_shortest_wednesday">T</string> + <string name="day_of_week_shortest_thursday">K</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">Š</string> + + <string name="am">priešpiet</string> + <string name="pm">popiet</string> + <string name="yesterday">vakar</string> + <string name="today">šiandien</string> + <string name="tomorrow">rytoj</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y-%m-%d</string> + <string name="numeric_date_format">yyyy-MM-dd</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%Y m. %B %-e d.</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %Y.%m.%d</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y.%m.%d</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%Y %B</string> + <string name="abbrev_month_day">%b %-e d.</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%Y m. %b</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s - %7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s-%3$s%1$s - %7$s-%8$s%6$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s - %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s-%2$s-%3$s%1$s - %9$s-%7$s-%8$s%6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s-%2$s-%3$s%1$s - %10$s %9$s-%7$s-%8$s%6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s-%3$s - %10$s %7$s-%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s-%3$s%1$s - %10$s %7$s-%8$s%6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s - %10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s%1$s - %6$s %5$s%4$s</string> + <string name="wday1_date1_wday2_date2">%2$s%1$s - %5$s%4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s%2$s</string> + <string name="wday_date">%3$s%2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s - %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s %3$s d.%1$s - %7$s %8$s d.%6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s - %10$s %7$s %8$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s - %10$s %7$s %8$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s d.%1$s - %10$s %7$s %8$s d.%6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s %3$s d.%1$s - %10$s %7$s %8$s d.%6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s m. %2$s %3$s d. - %10$s %9$s m. %7$s %8$s d.</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s m. %2$s %3$s d. - %10$s %9$s m. %7$s %8$s d.</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s m. %2$s %3$s d.,%1$s - %10$s %9$s m. %7$s %8$s d.,%6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s m. %2$s %3$s d.,%1$s - %10$s %9$s m. %7$s %8$s d.,%6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s m. %2$s %3$s d.,%1$s - %9$s m. %7$s %8$s d.,%6$s</string> + <string name="same_month_md1_md2">%2$s %3$s d.-%8$s d.</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s %3$s d.%1$s - %7$s %8$s d.%6$s</string> + <string name="same_year_mdy1_mdy2">%9$s m. %2$s %3$s d. - %7$s %8$s d.</string> + <string name="same_month_mdy1_mdy2">%9$s m. %2$s %3$s d.-%8$s d.</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s m. %2$s %3$s d.,%1$s - %7$s %8$s d.,%6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml new file mode 100644 index 000000000000..3dec1d2e3726 --- /dev/null +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janvāris</string> + <string name="month_long_standalone_february">februāris</string> + <string name="month_long_standalone_march">marts</string> + <string name="month_long_standalone_april">aprīlis</string> + <string name="month_long_standalone_may">maijs</string> + <string name="month_long_standalone_june">jūnijs</string> + <string name="month_long_standalone_july">jūlijs</string> + <string name="month_long_standalone_august">augusts</string> + <string name="month_long_standalone_september">septembris</string> + <string name="month_long_standalone_october">oktobris</string> + <string name="month_long_standalone_november">novembris</string> + <string name="month_long_standalone_december">decembris</string> + + <string name="month_long_january">janvāris</string> + <string name="month_long_february">februāris</string> + <string name="month_long_march">marts</string> + <string name="month_long_april">aprīlis</string> + <string name="month_long_may">maijs</string> + <string name="month_long_june">jūnijs</string> + <string name="month_long_july">jūlijs</string> + <string name="month_long_august">augusts</string> + <string name="month_long_september">septembris</string> + <string name="month_long_october">oktobris</string> + <string name="month_long_november">novembris</string> + <string name="month_long_december">decembris</string> + + <string name="month_medium_january">janv.</string> + <string name="month_medium_february">febr.</string> + <string name="month_medium_march">marts</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">maijs</string> + <string name="month_medium_june">jūn.</string> + <string name="month_medium_july">jūl.</string> + <string name="month_medium_august">aug.</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">okt.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">svētdiena</string> + <string name="day_of_week_long_monday">pirmdiena</string> + <string name="day_of_week_long_tuesday">otrdiena</string> + <string name="day_of_week_long_wednesday">trešdiena</string> + <string name="day_of_week_long_thursday">ceturtdiena</string> + <string name="day_of_week_long_friday">piektdiena</string> + <string name="day_of_week_long_saturday">sestdiena</string> + + <string name="day_of_week_medium_sunday">Sv</string> + <string name="day_of_week_medium_monday">Pr</string> + <string name="day_of_week_medium_tuesday">Ot</string> + <string name="day_of_week_medium_wednesday">Tr</string> + <string name="day_of_week_medium_thursday">Ce</string> + <string name="day_of_week_medium_friday">Pk</string> + <string name="day_of_week_medium_saturday">Se</string> + + <string name="day_of_week_short_sunday">Sv</string> + <string name="day_of_week_short_monday">Pr</string> + <string name="day_of_week_short_tuesday">Ot</string> + <string name="day_of_week_short_wednesday">Tr</string> + <string name="day_of_week_short_thursday">Ce</string> + <string name="day_of_week_short_friday">Pk</string> + <string name="day_of_week_short_saturday">Se</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">O</string> + <string name="day_of_week_shortest_wednesday">T</string> + <string name="day_of_week_shortest_thursday">C</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">vakar</string> + <string name="today">šodien</string> + <string name="tomorrow">rīt</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%Y. gada %-e. %B</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %Y. gada %-e. %b</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y. gada %-e. %b</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%Y. g. %B</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y. g. %b</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s–%8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s.–%8$s.%7$s.%9$s.</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s. – %6$s, %8$s.%7$s.%9$s.</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s. - %10$s %6$s, %8$s.%7$s.%9$s.</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s. gada %3$s. %2$s - %10$s %9$s. gada %8$s. %7$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s. gada %3$s. %2$s - %10$s %9$s. gada %8$s. %7$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s. g. %3$s. %2$s - %10$s %6$s, %9$s. g. %8$s. %7$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s. g. %3$s. %2$s - %10$s %6$s, %9$s. g. %8$s. %7$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s. g. %3$s. %2$s - %6$s, %9$s. g. %8$s. %7$s</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%9$s. gada %3$s. %2$s - %8$s. %7$s</string> + <string name="same_month_mdy1_mdy2">%9$s. gada %3$s.-%8$s. %2$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s. gada %3$s. %2$s - %6$s, y. gada %8$s. %7$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-mcc204-pt/strings.xml b/core/res/res/values-mcc204-pt/strings.xml new file mode 100644 index 000000000000..7d962307a3de --- /dev/null +++ b/core/res/res/values-mcc204-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"nl_nl"</string> +</resources> diff --git a/core/res/res/values-mcc230-pt/strings.xml b/core/res/res/values-mcc230-pt/strings.xml new file mode 100644 index 000000000000..d3ecdbba28ed --- /dev/null +++ b/core/res/res/values-mcc230-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"cs_cz"</string> +</resources> diff --git a/core/res/res/values-mcc232-pt/strings.xml b/core/res/res/values-mcc232-pt/strings.xml new file mode 100644 index 000000000000..4773838f9c47 --- /dev/null +++ b/core/res/res/values-mcc232-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"de_at"</string> +</resources> diff --git a/core/res/res/values-mcc234-pt/strings.xml b/core/res/res/values-mcc234-pt/strings.xml new file mode 100644 index 000000000000..2538b7329239 --- /dev/null +++ b/core/res/res/values-mcc234-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"en_gb"</string> +</resources> diff --git a/core/res/res/values-mcc260-pt/strings.xml b/core/res/res/values-mcc260-pt/strings.xml new file mode 100644 index 000000000000..1161f9a6356f --- /dev/null +++ b/core/res/res/values-mcc260-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"pl_pl"</string> +</resources> diff --git a/core/res/res/values-mcc262-pt/strings.xml b/core/res/res/values-mcc262-pt/strings.xml new file mode 100644 index 000000000000..9505cf4b4226 --- /dev/null +++ b/core/res/res/values-mcc262-pt/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="locale_replacement">"de_de"</string> +</resources> diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml new file mode 100644 index 000000000000..ecf01118298f --- /dev/null +++ b/core/res/res/values-nb/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januar</string> + <string name="month_long_standalone_february">februar</string> + <string name="month_long_standalone_march">mars</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">desember</string> + + <string name="month_long_january">januar</string> + <string name="month_long_february">februar</string> + <string name="month_long_march">mars</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">desember</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mars</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">juni</string> + <string name="month_medium_july">juli</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">des.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">søndag</string> + <string name="day_of_week_long_monday">mandag</string> + <string name="day_of_week_long_tuesday">tirsdag</string> + <string name="day_of_week_long_wednesday">onsdag</string> + <string name="day_of_week_long_thursday">torsdag</string> + <string name="day_of_week_long_friday">fredag</string> + <string name="day_of_week_long_saturday">lørdag</string> + + <string name="day_of_week_medium_sunday">søn.</string> + <string name="day_of_week_medium_monday">man.</string> + <string name="day_of_week_medium_tuesday">tir.</string> + <string name="day_of_week_medium_wednesday">ons.</string> + <string name="day_of_week_medium_thursday">tor.</string> + <string name="day_of_week_medium_friday">fre.</string> + <string name="day_of_week_medium_saturday">lør.</string> + + <string name="day_of_week_short_sunday">søn.</string> + <string name="day_of_week_short_monday">man.</string> + <string name="day_of_week_short_tuesday">tir.</string> + <string name="day_of_week_short_wednesday">ons.</string> + <string name="day_of_week_short_thursday">tor.</string> + <string name="day_of_week_short_friday">fre.</string> + <string name="day_of_week_short_saturday">lør.</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">O</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">i går</string> + <string name="today">i dag</string> + <string name="tomorrow">i morgen</string> + + <string name="hour_minute_24">%H.%M</string> + <string name="hour_minute_ampm">%-l.%M %p</string> + <string name="hour_minute_cap_ampm">%-l.%M %^p</string> + <string name="twelve_hour_time_format">h.mm a</string> + <string name="twenty_four_hour_time_format">HH.mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %-e. %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e. %b %Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s–%2$s</string> + <string name="date1_date2">%2$s–%5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s.–%8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s.%2$s.–%6$s %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s–%8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s.%2$s.%4$s–%6$s %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s.%2$s.%4$s–%10$s %6$s %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s.–%10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s.%2$s–%10$s %6$s %8$s.%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s–%10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s–%6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s–%4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s–%6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s–%8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s–%6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s–%10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s–%10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s %4$s–%6$s %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s.–%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s–%6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s–%8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s–%6$s %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 7bed159c51ca..33d015997b34 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -166,14 +166,10 @@ <string name="permdesc_changeConfiguration">"Tillater applikasjonen å endre gjeldende innstillinger, slik som språk eller skriftstørrelse."</string> <string name="permlab_restartPackages">"omstarte andre applikasjoner"</string> <string name="permdesc_restartPackages">"Lar applikasjonen tvinge andre applikasjoner til å starte på nytt."</string> - <string name="permlab_setProcessForeground">"unngå å bli stoppet"</string> - <string name="permdesc_setProcessForeground">"Lar applikasjonen sette en vilkårlig prosess i forgrunnen, så den ikke kan bli drept. Vanlige applikasjoner bør aldri trenge dette."</string> <string name="permlab_forceBack">"tvinge applikasjoner til å lukkes"</string> <string name="permdesc_forceBack">"Lar applikasjonen tvinge enhver aktivitet som er i forgrunnen til å lukkes og gå tilbake. Vanlige applikasjoner bør aldri trenge dette."</string> <string name="permlab_dump">"hente intern systemtilstand"</string> <string name="permdesc_dump">"Lar applikasjonen hente intern tilstand fra systemet. Onsdinnede applikasjoner kan hente et bredt spekter av privat og sikker informasjon som de vanligvis aldri burde ha behov for."</string> - <string name="permlab_addSystemService">"publisere lavnivåtjenester"</string> - <string name="permdesc_addSystemService">"Lar applikasjonen publisere sine egne lavnivås systemtjenester. Ondsinnede applikasjoner kan kapre systemet, og stjele eller ødelegge alle data på det."</string> <string name="permlab_runSetActivityWatcher">"overvåke og kontrollere all applikasjonsoppstart"</string> <string name="permdesc_runSetActivityWatcher">"Lar applikasjonen overvåke og kontrollere hvordan systemet starter applikasjoner. Ondsinnede applikasjoner kan ta over systemet helt. Denne rettigheten behøves bare for utvikling, aldri for vanlig bruk av telefonen."</string> <string name="permlab_broadcastPackageRemoved">"kringkaste melding om fjernet pakke"</string> @@ -186,8 +182,6 @@ <string name="permdesc_setProcessLimit">"Lar applikasjonen kontrollere maksimalt antall kjørende prosesser. Behøves aldri for vanlige applikasjoner."</string> <string name="permlab_setAlwaysFinish">"få alle bakgrunnsapplikasjoner til å lukkes"</string> <string name="permdesc_setAlwaysFinish">"Lar applikasjonen kontrollere om aktiviteter alltid avsluttes når de sendes til bakgrunnen. Behøves aldri for vanlige applikasjoner."</string> - <string name="permlab_fotaUpdate">"installere systemoppdateringer automatisk"</string> - <string name="permdesc_fotaUpdate">"Lar applikasjonen motta meldinger om pågående systemoppdateringer, og starte installeringen av dem. Ondsinnede applikasjoner kan bruke dette for å skade systemet med uautoriserte oppdateringer, eller generelt forstyrre oppdateringsprosessen."</string> <string name="permlab_batteryStats">"endre batteristatistikk"</string> <string name="permdesc_batteryStats">"Lar applikasjonen endre på innsamlet batteristatistikk. Ikke ment for vanlige applikasjoner."</string> <string name="permlab_internalSystemWindow">"vis uautoriserte vinduer"</string> @@ -424,9 +418,6 @@ <string name="lockscreen_glogin_password_hint">"Password"</string> <string name="lockscreen_glogin_submit_button">"Sign in"</string> <string name="lockscreen_glogin_invalid_input">"Invalid username or password."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Fjern varslinger"</string> @@ -458,9 +449,6 @@ <string name="menu_enter_shortcut_label">"enter"</string> <string name="menu_delete_shortcut_label">"slett"</string> <string name="search_go">"Søk"</string> - <string name="today">"I dag"</string> - <string name="yesterday">"I går"</string> - <string name="tomorrow">"I morgen"</string> <string name="oneMonthDurationPast">"For en måned siden"</string> <string name="beforeOneMonthDurationPast">"For over en måned siden"</string> <plurals name="num_seconds_ago"> @@ -542,13 +530,6 @@ <string name="weeks">"uker"</string> <string name="year">"år"</string> <string name="years">"år"</string> - <string name="sunday">"søndag"</string> - <string name="monday">"mandag"</string> - <string name="tuesday">"tirsdag"</string> - <string name="wednesday">"onsdag"</string> - <string name="thursday">"torsdag"</string> - <string name="friday">"fredag"</string> - <string name="saturday">"lørdag"</string> <string name="every_weekday">"Hverdager (man–fre)"</string> <string name="daily">"Hver dag"</string> <string name="weekly">"Hver <xliff:g id="DAY">%s</xliff:g>"</string> @@ -558,26 +539,7 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Beklager, denne videoen er ikke gyldig for streaming til denne enheten."</string> <string name="VideoView_error_text_unknown">"Beklager, kan ikke spille denne videoen."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="YEAR">%Y</xliff:g>-<xliff:g id="MONTH">%m</xliff:g>-<xliff:g id="DAY">%d</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>PLACEHOLDERplaceholder"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>'., '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>'. '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"middag"</string> <string name="Noon">"Middag"</string> <string name="midnight">"midnatt"</string> @@ -586,111 +548,10 @@ <skip /> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>."</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>."</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>. <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>. <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>. <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>.<xliff:g id="MONTH1">%2$s</xliff:g>.<xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>.<xliff:g id="MONTH2">%7$s</xliff:g>.<xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>.–<xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>.–<xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>. <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>. <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g>. <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> <!-- no translation found for abbrev_month_day (3156047263406783231) --> <skip /> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"søndag"</string> - <string name="day_of_week_long_monday">"mandag"</string> - <string name="day_of_week_long_tuesday">"tirsdag"</string> - <string name="day_of_week_long_wednesday">"onsdag"</string> - <string name="day_of_week_long_thursday">"torsdag"</string> - <string name="day_of_week_long_friday">"fredag"</string> - <string name="day_of_week_long_saturday">"lørdag"</string> - <string name="day_of_week_medium_sunday">"søn"</string> - <string name="day_of_week_medium_monday">"man"</string> - <string name="day_of_week_medium_tuesday">"tir"</string> - <string name="day_of_week_medium_wednesday">"ons"</string> - <string name="day_of_week_medium_thursday">"tor"</string> - <string name="day_of_week_medium_friday">"fre"</string> - <string name="day_of_week_medium_saturday">"lør"</string> - <string name="day_of_week_short_sunday">"sø"</string> - <string name="day_of_week_short_monday">"ma"</string> - <string name="day_of_week_short_tuesday">"ti"</string> - <string name="day_of_week_short_wednesday">"on"</string> - <string name="day_of_week_short_thursday">"to"</string> - <string name="day_of_week_short_friday">"fr"</string> - <string name="day_of_week_short_saturday">"lø"</string> - <string name="day_of_week_shorter_sunday">"S"</string> - <string name="day_of_week_shorter_monday">"M"</string> - <string name="day_of_week_shorter_tuesday">"Ti"</string> - <string name="day_of_week_shorter_wednesday">"O"</string> - <string name="day_of_week_shorter_thursday">"To"</string> - <string name="day_of_week_shorter_friday">"F"</string> - <string name="day_of_week_shorter_saturday">"L"</string> - <string name="day_of_week_shortest_sunday">"S"</string> - <string name="day_of_week_shortest_monday">"M"</string> - <string name="day_of_week_shortest_tuesday">"T"</string> - <string name="day_of_week_shortest_wednesday">"O"</string> - <string name="day_of_week_shortest_thursday">"T"</string> - <string name="day_of_week_shortest_friday">"F"</string> - <string name="day_of_week_shortest_saturday">"L"</string> - <string name="month_long_january">"januar"</string> - <string name="month_long_february">"februar"</string> - <string name="month_long_march">"mars"</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">"desember"</string> - <string name="month_medium_january">"jan"</string> - <string name="month_medium_february">"feb"</string> - <string name="month_medium_march">"mar"</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">"des"</string> - <string name="month_shortest_january">"J"</string> - <string name="month_shortest_february">"F"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"J"</string> - <string name="month_shortest_july">"J"</string> - <string name="month_shortest_august">"A"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Merk alt"</string> diff --git a/core/res/res/values-nl-rBE/donottranslate-cldr.xml b/core/res/res/values-nl-rBE/donottranslate-cldr.xml new file mode 100644 index 000000000000..680a39286357 --- /dev/null +++ b/core/res/res/values-nl-rBE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januari</string> + <string name="month_long_standalone_february">februari</string> + <string name="month_long_standalone_march">maart</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">mei</string> + <string name="month_long_standalone_june">juni</string> + <string name="month_long_standalone_july">juli</string> + <string name="month_long_standalone_august">augustus</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">december</string> + + <string name="month_long_january">januari</string> + <string name="month_long_february">februari</string> + <string name="month_long_march">maart</string> + <string name="month_long_april">april</string> + <string name="month_long_may">mei</string> + <string name="month_long_june">juni</string> + <string name="month_long_july">juli</string> + <string name="month_long_august">augustus</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">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mrt.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">mei</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">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">zondag</string> + <string name="day_of_week_long_monday">maandag</string> + <string name="day_of_week_long_tuesday">dinsdag</string> + <string name="day_of_week_long_wednesday">woensdag</string> + <string name="day_of_week_long_thursday">donderdag</string> + <string name="day_of_week_long_friday">vrijdag</string> + <string name="day_of_week_long_saturday">zaterdag</string> + + <string name="day_of_week_medium_sunday">zo</string> + <string name="day_of_week_medium_monday">ma</string> + <string name="day_of_week_medium_tuesday">di</string> + <string name="day_of_week_medium_wednesday">wo</string> + <string name="day_of_week_medium_thursday">do</string> + <string name="day_of_week_medium_friday">vr</string> + <string name="day_of_week_medium_saturday">za</string> + + <string name="day_of_week_short_sunday">zo</string> + <string name="day_of_week_short_monday">ma</string> + <string name="day_of_week_short_tuesday">di</string> + <string name="day_of_week_short_wednesday">wo</string> + <string name="day_of_week_short_thursday">do</string> + <string name="day_of_week_short_friday">vr</string> + <string name="day_of_week_short_saturday">za</string> + + <string name="day_of_week_shortest_sunday">Z</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">Z</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Gisteren</string> + <string name="today">Vandaag</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-e/%m/%Y</string> + <string name="numeric_date_format">d/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e-%b-%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e-%b-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e-%b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s/%2$s/%4$s - %10$s %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-nl-rNL/donottranslate-cldr.xml b/core/res/res/values-nl-rNL/donottranslate-cldr.xml new file mode 100644 index 000000000000..b6231b622790 --- /dev/null +++ b/core/res/res/values-nl-rNL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januari</string> + <string name="month_long_standalone_february">februari</string> + <string name="month_long_standalone_march">maart</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">mei</string> + <string name="month_long_standalone_june">juni</string> + <string name="month_long_standalone_july">juli</string> + <string name="month_long_standalone_august">augustus</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">december</string> + + <string name="month_long_january">januari</string> + <string name="month_long_february">februari</string> + <string name="month_long_march">maart</string> + <string name="month_long_april">april</string> + <string name="month_long_may">mei</string> + <string name="month_long_june">juni</string> + <string name="month_long_july">juli</string> + <string name="month_long_august">augustus</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">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mrt.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">mei</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">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">zondag</string> + <string name="day_of_week_long_monday">maandag</string> + <string name="day_of_week_long_tuesday">dinsdag</string> + <string name="day_of_week_long_wednesday">woensdag</string> + <string name="day_of_week_long_thursday">donderdag</string> + <string name="day_of_week_long_friday">vrijdag</string> + <string name="day_of_week_long_saturday">zaterdag</string> + + <string name="day_of_week_medium_sunday">zo</string> + <string name="day_of_week_medium_monday">ma</string> + <string name="day_of_week_medium_tuesday">di</string> + <string name="day_of_week_medium_wednesday">wo</string> + <string name="day_of_week_medium_thursday">do</string> + <string name="day_of_week_medium_friday">vr</string> + <string name="day_of_week_medium_saturday">za</string> + + <string name="day_of_week_short_sunday">zo</string> + <string name="day_of_week_short_monday">ma</string> + <string name="day_of_week_short_tuesday">di</string> + <string name="day_of_week_short_wednesday">wo</string> + <string name="day_of_week_short_thursday">do</string> + <string name="day_of_week_short_friday">vr</string> + <string name="day_of_week_short_saturday">za</string> + + <string name="day_of_week_shortest_sunday">Z</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">Z</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Gisteren</string> + <string name="today">Vandaag</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d-%m-%Y</string> + <string name="numeric_date_format">dd-MM-yyyy</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e-%b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s-%2$s - %8$s-%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s-%2$s - %6$s %8$s-%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s-%2$s-%4$s - %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s-%2$s-%4$s - %6$s %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s-%2$s-%4$s - %10$s %6$s %8$s-%7$s-%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s-%2$s - %10$s %8$s-%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s-%2$s - %10$s %6$s %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml new file mode 100644 index 000000000000..b6231b622790 --- /dev/null +++ b/core/res/res/values-nl/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januari</string> + <string name="month_long_standalone_february">februari</string> + <string name="month_long_standalone_march">maart</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">mei</string> + <string name="month_long_standalone_june">juni</string> + <string name="month_long_standalone_july">juli</string> + <string name="month_long_standalone_august">augustus</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">december</string> + + <string name="month_long_january">januari</string> + <string name="month_long_february">februari</string> + <string name="month_long_march">maart</string> + <string name="month_long_april">april</string> + <string name="month_long_may">mei</string> + <string name="month_long_june">juni</string> + <string name="month_long_july">juli</string> + <string name="month_long_august">augustus</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">december</string> + + <string name="month_medium_january">jan.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mrt.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">mei</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">dec.</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">zondag</string> + <string name="day_of_week_long_monday">maandag</string> + <string name="day_of_week_long_tuesday">dinsdag</string> + <string name="day_of_week_long_wednesday">woensdag</string> + <string name="day_of_week_long_thursday">donderdag</string> + <string name="day_of_week_long_friday">vrijdag</string> + <string name="day_of_week_long_saturday">zaterdag</string> + + <string name="day_of_week_medium_sunday">zo</string> + <string name="day_of_week_medium_monday">ma</string> + <string name="day_of_week_medium_tuesday">di</string> + <string name="day_of_week_medium_wednesday">wo</string> + <string name="day_of_week_medium_thursday">do</string> + <string name="day_of_week_medium_friday">vr</string> + <string name="day_of_week_medium_saturday">za</string> + + <string name="day_of_week_short_sunday">zo</string> + <string name="day_of_week_short_monday">ma</string> + <string name="day_of_week_short_tuesday">di</string> + <string name="day_of_week_short_wednesday">wo</string> + <string name="day_of_week_short_thursday">do</string> + <string name="day_of_week_short_friday">vr</string> + <string name="day_of_week_short_saturday">za</string> + + <string name="day_of_week_shortest_sunday">Z</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">D</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">D</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">Z</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Gisteren</string> + <string name="today">Vandaag</string> + <string name="tomorrow">Morgen</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d-%m-%Y</string> + <string name="numeric_date_format">dd-MM-yyyy</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e-%b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s-%2$s - %8$s-%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s-%2$s - %6$s %8$s-%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s-%2$s-%4$s - %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s-%2$s-%4$s - %6$s %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s-%2$s-%4$s - %10$s %6$s %8$s-%7$s-%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s-%2$s - %10$s %8$s-%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s-%2$s - %10$s %6$s %8$s-%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 3a9e32c52471..a418d72a882f 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Hiermee kan een toepassing de huidige configuratie, zoals de landinstelling of de algemene lettergrootte, wijzigen."</string> <string name="permlab_restartPackages">"andere toepassingen opnieuw starten"</string> <string name="permdesc_restartPackages">"Hiermee kan een toepassing andere toepassingen opnieuw starten."</string> - <string name="permlab_setProcessForeground">"stoppen voorkomen"</string> - <string name="permdesc_setProcessForeground">"Hiermee kan een toepassing ervoor zorgen dat elk willekeurig proces op de voorgrond wordt uitgevoerd en dus niet kan worden afgesloten. Nooit vereist voor normale toepassingen."</string> <string name="permlab_forceBack">"toepassing nu sluiten"</string> <string name="permdesc_forceBack">"Hiermee kan een toepassing elke willekeurige activiteit die op de voorgrond wordt uitgevoerd, sluiten en naar de achtergrond verplaatsen. Nooit vereist voor normale toepassingen."</string> <string name="permlab_dump">"interne systeemstatus ophalen"</string> <string name="permdesc_dump">"Hiermee kan een toepassing de interne status van het systeem ophalen. Schadelijke toepassingen kunnen privé- of veiligheidsgegevens ophalen die ze normaal niet nodig hebben."</string> - <string name="permlab_addSystemService">"services op laag niveau publiceren"</string> - <string name="permdesc_addSystemService">"Hiermee kunnen toepassingen hun eigen systeemservices op laag niveau publiceren. Schadelijke toepassingen kunnen het systeem mogelijk kapen en willekeurige gegevens van het systeem stelen of beschadigen."</string> <string name="permlab_runSetActivityWatcher">"alle startende toepassingen bijhouden en beheren"</string> <string name="permdesc_runSetActivityWatcher">"Hiermee kan een toepassing de manier waarop het systeem activiteiten start, bijhouden en beheren. Schadelijke toepassingen kunnen het systeem volledig in gevaar brengen. Deze machtiging is alleen voor ontwikkeling vereist, nooit voor normaal telefoongebruik."</string> <string name="permlab_broadcastPackageRemoved">"melding verzenden dat pakket is verwijderd"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Hiermee kan een toepassing het maximum aantal processen bepalen dat wordt uitgevoerd. Nooit vereist voor normale toepassingen."</string> <string name="permlab_setAlwaysFinish">"alle achtergrondtoepassingen sluiten"</string> <string name="permdesc_setAlwaysFinish">"Hiermee kan een toepassing bepalen of activiteiten altijd worden afgesloten zodra deze naar de achtergrond gaan. Nooit nodig voor normale toepassingen."</string> - <string name="permlab_fotaUpdate">"systeemupdates automatisch installeren"</string> - <string name="permdesc_fotaUpdate">"Hiermee ontvangt een toepassing meldingen over beschikbare systeemupdates en kan hun installatie starten. Schadelijke toepassingen kunnen hiervan gebruik maken om het systeem met ongeautoriseerde updates te beschadigen of het updateproces in het algemeen te verstoren."</string> <string name="permlab_batteryStats">"accustatistieken aanpassen"</string> <string name="permdesc_batteryStats">"Hiermee kunnen verzamelde accustatistieken worden gewijzigd. Niet voor gebruik door normale toepassingen."</string> <string name="permlab_internalSystemWindow">"niet-geautoriseerde vensters weergeven"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Wachtwoord"</string> <string name="lockscreen_glogin_submit_button">"Aanmelden"</string> <string name="lockscreen_glogin_invalid_input">"Gebruikersnaam of wachtwoord ongeldig."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Meldingen wissen"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"invoeren"</string> <string name="menu_delete_shortcut_label">"verwijderen"</string> <string name="search_go">"Zoeken"</string> - <string name="today">"Vandaag"</string> - <string name="yesterday">"Gisteren"</string> - <string name="tomorrow">"Morgen"</string> <string name="oneMonthDurationPast">"1 maand geleden"</string> <string name="beforeOneMonthDurationPast">"Meer dan 1 maand geleden"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"weken"</string> <string name="year">"jaar"</string> <string name="years">"jaren"</string> - <string name="sunday">"Zondag"</string> - <string name="monday">"Maandag"</string> - <string name="tuesday">"Dinsdag"</string> - <string name="wednesday">"Woensdag"</string> - <string name="thursday">"Donderdag"</string> - <string name="friday">"Vrijdag"</string> - <string name="saturday">"Zaterdag"</string> <string name="every_weekday">"Elke weekdag (ma-vr)"</string> <string name="daily">"Dagelijks"</string> <string name="weekly">"Wekelijks op <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Deze video kan helaas niet worden gestreamd naar dit apparaat."</string> <string name="VideoView_error_text_unknown">"Deze video kan niet worden afgespeeld."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"am"</string> - <string name="pm">"pm"</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>-<xliff:g id="MONTH">%m</xliff:g>-<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"twaalf uur \'s middags"</string> <string name="Noon">"Twaalf uur \'s middags"</string> <string name="midnight">"middernacht"</string> <string name="Midnight">"Middernacht"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>-<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>-<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>-<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>-<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>-<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>-<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>-<xliff:g id="MONTH1">%2$s</xliff:g>-<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>-<xliff:g id="MONTH2">%7$s</xliff:g>-<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"Zondag"</string> - <string name="day_of_week_long_monday">"Maandag"</string> - <string name="day_of_week_long_tuesday">"Dinsdag"</string> - <string name="day_of_week_long_wednesday">"Woensdag"</string> - <string name="day_of_week_long_thursday">"Donderdag"</string> - <string name="day_of_week_long_friday">"Vrijdag"</string> - <string name="day_of_week_long_saturday">"Zaterdag"</string> - <string name="day_of_week_medium_sunday">"Zo"</string> - <string name="day_of_week_medium_monday">"Ma"</string> - <string name="day_of_week_medium_tuesday">"Di"</string> - <string name="day_of_week_medium_wednesday">"Wo"</string> - <string name="day_of_week_medium_thursday">"Do"</string> - <string name="day_of_week_medium_friday">"Vr"</string> - <string name="day_of_week_medium_saturday">"Za"</string> - <string name="day_of_week_short_sunday">"Zo"</string> - <string name="day_of_week_short_monday">"Ma"</string> - <string name="day_of_week_short_tuesday">"Di"</string> - <string name="day_of_week_short_wednesday">"Wo"</string> - <string name="day_of_week_short_thursday">"Do"</string> - <string name="day_of_week_short_friday">"Vr"</string> - <string name="day_of_week_short_saturday">"Za"</string> - <string name="day_of_week_shorter_sunday">"Zo"</string> - <string name="day_of_week_shorter_monday">"M"</string> - <string name="day_of_week_shorter_tuesday">"Di"</string> - <string name="day_of_week_shorter_wednesday">"W"</string> - <string name="day_of_week_shorter_thursday">"Do"</string> - <string name="day_of_week_shorter_friday">"V"</string> - <string name="day_of_week_shorter_saturday">"Za"</string> - <string name="day_of_week_shortest_sunday">"Z"</string> - <string name="day_of_week_shortest_monday">"M"</string> - <string name="day_of_week_shortest_tuesday">"D"</string> - <string name="day_of_week_shortest_wednesday">"W"</string> - <string name="day_of_week_shortest_thursday">"D"</string> - <string name="day_of_week_shortest_friday">"V"</string> - <string name="day_of_week_shortest_saturday">"Z"</string> - <string name="month_long_january">"Januari"</string> - <string name="month_long_february">"Februari"</string> - <string name="month_long_march">"Maart"</string> - <string name="month_long_april">"April"</string> - <string name="month_long_may">"Mei"</string> - <string name="month_long_june">"Juni"</string> - <string name="month_long_july">"Juli"</string> - <string name="month_long_august">"Augustus"</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">"December"</string> - <string name="month_medium_january">"Jan"</string> - <string name="month_medium_february">"Feb"</string> - <string name="month_medium_march">"Mrt"</string> - <string name="month_medium_april">"Apr"</string> - <string name="month_medium_may">"Mei"</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">"Dec"</string> - <string name="month_shortest_january">"J"</string> - <string name="month_shortest_february">"V"</string> - <string name="month_shortest_march">"M"</string> - <string name="month_shortest_april">"A"</string> - <string name="month_shortest_may">"M"</string> - <string name="month_shortest_june">"J"</string> - <string name="month_shortest_july">"J"</string> - <string name="month_shortest_august">"A"</string> - <string name="month_shortest_september">"S"</string> - <string name="month_shortest_october">"O"</string> - <string name="month_shortest_november">"N"</string> - <string name="month_shortest_december">"D"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Alles selecteren"</string> diff --git a/core/res/res/values-pl-rPL/donottranslate-cldr.xml b/core/res/res/values-pl-rPL/donottranslate-cldr.xml new file mode 100644 index 000000000000..4ad17bf11c46 --- /dev/null +++ b/core/res/res/values-pl-rPL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">styczeń</string> + <string name="month_long_standalone_february">luty</string> + <string name="month_long_standalone_march">marzec</string> + <string name="month_long_standalone_april">kwiecień</string> + <string name="month_long_standalone_may">maj</string> + <string name="month_long_standalone_june">czerwiec</string> + <string name="month_long_standalone_july">lipiec</string> + <string name="month_long_standalone_august">sierpień</string> + <string name="month_long_standalone_september">wrzesień</string> + <string name="month_long_standalone_october">październik</string> + <string name="month_long_standalone_november">listopad</string> + <string name="month_long_standalone_december">grudzień</string> + + <string name="month_long_january">stycznia</string> + <string name="month_long_february">lutego</string> + <string name="month_long_march">marca</string> + <string name="month_long_april">kwietnia</string> + <string name="month_long_may">maja</string> + <string name="month_long_june">czerwca</string> + <string name="month_long_july">lipca</string> + <string name="month_long_august">sierpnia</string> + <string name="month_long_september">września</string> + <string name="month_long_october">października</string> + <string name="month_long_november">listopada</string> + <string name="month_long_december">grudnia</string> + + <string name="month_medium_january">sty</string> + <string name="month_medium_february">lut</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">kwi</string> + <string name="month_medium_may">maj</string> + <string name="month_medium_june">cze</string> + <string name="month_medium_july">lip</string> + <string name="month_medium_august">sie</string> + <string name="month_medium_september">wrz</string> + <string name="month_medium_october">paź</string> + <string name="month_medium_november">lis</string> + <string name="month_medium_december">gru</string> + + <string name="month_shortest_january">s</string> + <string name="month_shortest_february">l</string> + <string name="month_shortest_march">m</string> + <string name="month_shortest_april">k</string> + <string name="month_shortest_may">m</string> + <string name="month_shortest_june">c</string> + <string name="month_shortest_july">l</string> + <string name="month_shortest_august">s</string> + <string name="month_shortest_september">w</string> + <string name="month_shortest_october">p</string> + <string name="month_shortest_november">l</string> + <string name="month_shortest_december">g</string> + + <string name="day_of_week_long_sunday">niedziela</string> + <string name="day_of_week_long_monday">poniedziałek</string> + <string name="day_of_week_long_tuesday">wtorek</string> + <string name="day_of_week_long_wednesday">środa</string> + <string name="day_of_week_long_thursday">czwartek</string> + <string name="day_of_week_long_friday">piątek</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">niedz.</string> + <string name="day_of_week_medium_monday">pon.</string> + <string name="day_of_week_medium_tuesday">wt.</string> + <string name="day_of_week_medium_wednesday">śr.</string> + <string name="day_of_week_medium_thursday">czw.</string> + <string name="day_of_week_medium_friday">pt.</string> + <string name="day_of_week_medium_saturday">sob.</string> + + <string name="day_of_week_short_sunday">niedz.</string> + <string name="day_of_week_short_monday">pon.</string> + <string name="day_of_week_short_tuesday">wt.</string> + <string name="day_of_week_short_wednesday">śr.</string> + <string name="day_of_week_short_thursday">czw.</string> + <string name="day_of_week_short_friday">pt.</string> + <string name="day_of_week_short_saturday">sob.</string> + + <string name="day_of_week_shortest_sunday">N</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">W</string> + <string name="day_of_week_shortest_wednesday">Ś</string> + <string name="day_of_week_shortest_thursday">C</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Wczoraj</string> + <string name="today">Dzisiaj</string> + <string name="tomorrow">Jutro</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d-%m-%Y</string> + <string name="numeric_date_format">dd-MM-yyyy</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d-%m-%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d-%m-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s-%8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s-%8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s-%2$s-%4$s-%6$s, %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml new file mode 100644 index 000000000000..4ad17bf11c46 --- /dev/null +++ b/core/res/res/values-pl/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">styczeń</string> + <string name="month_long_standalone_february">luty</string> + <string name="month_long_standalone_march">marzec</string> + <string name="month_long_standalone_april">kwiecień</string> + <string name="month_long_standalone_may">maj</string> + <string name="month_long_standalone_june">czerwiec</string> + <string name="month_long_standalone_july">lipiec</string> + <string name="month_long_standalone_august">sierpień</string> + <string name="month_long_standalone_september">wrzesień</string> + <string name="month_long_standalone_october">październik</string> + <string name="month_long_standalone_november">listopad</string> + <string name="month_long_standalone_december">grudzień</string> + + <string name="month_long_january">stycznia</string> + <string name="month_long_february">lutego</string> + <string name="month_long_march">marca</string> + <string name="month_long_april">kwietnia</string> + <string name="month_long_may">maja</string> + <string name="month_long_june">czerwca</string> + <string name="month_long_july">lipca</string> + <string name="month_long_august">sierpnia</string> + <string name="month_long_september">września</string> + <string name="month_long_october">października</string> + <string name="month_long_november">listopada</string> + <string name="month_long_december">grudnia</string> + + <string name="month_medium_january">sty</string> + <string name="month_medium_february">lut</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">kwi</string> + <string name="month_medium_may">maj</string> + <string name="month_medium_june">cze</string> + <string name="month_medium_july">lip</string> + <string name="month_medium_august">sie</string> + <string name="month_medium_september">wrz</string> + <string name="month_medium_october">paź</string> + <string name="month_medium_november">lis</string> + <string name="month_medium_december">gru</string> + + <string name="month_shortest_january">s</string> + <string name="month_shortest_february">l</string> + <string name="month_shortest_march">m</string> + <string name="month_shortest_april">k</string> + <string name="month_shortest_may">m</string> + <string name="month_shortest_june">c</string> + <string name="month_shortest_july">l</string> + <string name="month_shortest_august">s</string> + <string name="month_shortest_september">w</string> + <string name="month_shortest_october">p</string> + <string name="month_shortest_november">l</string> + <string name="month_shortest_december">g</string> + + <string name="day_of_week_long_sunday">niedziela</string> + <string name="day_of_week_long_monday">poniedziałek</string> + <string name="day_of_week_long_tuesday">wtorek</string> + <string name="day_of_week_long_wednesday">środa</string> + <string name="day_of_week_long_thursday">czwartek</string> + <string name="day_of_week_long_friday">piątek</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">niedz.</string> + <string name="day_of_week_medium_monday">pon.</string> + <string name="day_of_week_medium_tuesday">wt.</string> + <string name="day_of_week_medium_wednesday">śr.</string> + <string name="day_of_week_medium_thursday">czw.</string> + <string name="day_of_week_medium_friday">pt.</string> + <string name="day_of_week_medium_saturday">sob.</string> + + <string name="day_of_week_short_sunday">niedz.</string> + <string name="day_of_week_short_monday">pon.</string> + <string name="day_of_week_short_tuesday">wt.</string> + <string name="day_of_week_short_wednesday">śr.</string> + <string name="day_of_week_short_thursday">czw.</string> + <string name="day_of_week_short_friday">pt.</string> + <string name="day_of_week_short_saturday">sob.</string> + + <string name="day_of_week_shortest_sunday">N</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">W</string> + <string name="day_of_week_shortest_wednesday">Ś</string> + <string name="day_of_week_shortest_thursday">C</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Wczoraj</string> + <string name="today">Dzisiaj</string> + <string name="tomorrow">Jutro</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d-%m-%Y</string> + <string name="numeric_date_format">dd-MM-yyyy</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d-%m-%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d-%m-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s-%8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s-%8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s-%2$s-%4$s-%6$s, %8$s-%7$s-%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 50d245b5d8e1..c6c9bd07dec2 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Pozwala aplikacji zmieniać bieżącą konfigurację, na przykład lokalny lub globalny rozmiar czcionki."</string> <string name="permlab_restartPackages">"resetowanie innych aplikacji"</string> <string name="permdesc_restartPackages">"Pozwala aplikacji na wymuszenie ponownego uruchomienia innych aplikacji."</string> - <string name="permlab_setProcessForeground">"zapobieganie zatrzymaniu"</string> - <string name="permdesc_setProcessForeground">"Pozwala aplikacji na uruchamianie dowolnego procesu na pierwszym planie tak, że nie można go wyłączyć. Nigdy nie powinno być potrzebne normalnym aplikacjom."</string> <string name="permlab_forceBack">"wymuszanie zamknięcia aplikacji"</string> <string name="permdesc_forceBack">"Pozwala aplikacji na wymuszenie zamknięcia i cofnięcia dowolnej operacji działającej na pierwszym planie. Nigdy nie powinno być potrzebne normalnym aplikacjom."</string> <string name="permlab_dump">"pobieranie informacji o wewnętrznym stanie systemu"</string> <string name="permdesc_dump">"Pozwala aplikacjom na pobieranie informacji o wewnętrznym stanie systemu. Szkodliwe aplikacje mogą pobrać szeroką gamę osobistych i zabezpieczonych informacji, które normalnie nie powinny im być nigdy potrzebne."</string> - <string name="permlab_addSystemService">"publikowanie usług niskiego poziomu"</string> - <string name="permdesc_addSystemService">"Pozwala aplikacji na publikowanie własnych usług systemowych niskiego poziomu. Szkodliwe aplikacje mogą przejąć kontrolę nad systemem oraz wykraść lub uszkodzić znajdujące się w nim dane."</string> <string name="permlab_runSetActivityWatcher">"monitorowanie i kontrolowanie wszystkich uruchamianych aplikacji"</string> <string name="permdesc_runSetActivityWatcher">"Pozwala aplikacji na monitorowanie i kontrolowanie sposobu, w jaki w systemie uruchamiane są różne działania. Szkodliwe aplikacje mogą całkowicie przejąć system. Te uprawnienia potrzebne są tylko programistom, nigdy w przypadku normalnego wykorzystywania telefonu."</string> <string name="permlab_broadcastPackageRemoved">"wysyłanie transmisji informującej o usuniętym pakiecie"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Pozwala aplikacji na kontrolowanie maksymalnej liczby uruchamianych procesów. Nigdy nie wykorzystywane przez normalne aplikacje."</string> <string name="permlab_setAlwaysFinish">"zamykanie wszystkich aplikacji działających w tle"</string> <string name="permdesc_setAlwaysFinish">"Pozwala aplikacji na kontrolowanie, czy czynności są zawsze kończone, kiedy zaczynają działać w tle. Nigdy nie jest potrzebne normalnym aplikacjom."</string> - <string name="permlab_fotaUpdate">"automatyczne instalowanie aktualizacji systemu"</string> - <string name="permdesc_fotaUpdate">"Pozwala aplikacji na otrzymywanie powiadomień o oczekujących aktualizacjach systemu i uruchamianie ich instalacji. Szkodliwe aplikacje mogą to wykorzystać do uszkodzenia systemu za pomocą nieuwierzytelnionych aktualizacji lub ogólnie wpłynąć na proces aktualizowania."</string> <string name="permlab_batteryStats">"zmienianie statystyk dotyczących baterii"</string> <string name="permdesc_batteryStats">"Pozwala na zmianę zebranych statystyk dotyczących baterii. Nie do wykorzystania przez normalne aplikacje."</string> <string name="permlab_internalSystemWindow">"wyświetlanie nieuwierzytelnionych okien"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Hasło"</string> <string name="lockscreen_glogin_submit_button">"Zaloguj"</string> <string name="lockscreen_glogin_invalid_input">"Błędna nazwa użytkownika lub hasło."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Wyczyść powiadomienia"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"enter"</string> <string name="menu_delete_shortcut_label">"usuń"</string> <string name="search_go">"Szukaj"</string> - <string name="today">"Dzisiaj"</string> - <string name="yesterday">"Wczoraj"</string> - <string name="tomorrow">"Jutro"</string> <string name="oneMonthDurationPast">"1 miesiąc temu"</string> <string name="beforeOneMonthDurationPast">"Ponad 1 miesiąc temu"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"tygodni"</string> <string name="year">"rok"</string> <string name="years">"lat"</string> - <string name="sunday">"niedziela"</string> - <string name="monday">"poniedziałek"</string> - <string name="tuesday">"wtorek"</string> - <string name="wednesday">"środa"</string> - <string name="thursday">"czwartek"</string> - <string name="friday">"piątek"</string> - <string name="saturday">"sobota"</string> <string name="every_weekday">"W każdy dzień roboczy (pon–pt)"</string> <string name="daily">"Codziennie"</string> <string name="weekly">"Co tydzień w <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"Przepraszamy, ten film wideo nie nadaje się do przesyłania strumieniowego do tego urządzenia."</string> <string name="VideoView_error_text_unknown">"Niestety, nie można odtworzyć tego filmu wideo."</string> <string name="VideoView_error_button">"OK"</string> - <string name="am">"rano"</string> - <string name="pm">"po południu"</string> - <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>', '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"południe"</string> <string name="Noon">"Południe"</string> <string name="midnight">"północ"</string> <string name="Midnight">"Północ"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g>, <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>, <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"niedziela"</string> - <string name="day_of_week_long_monday">"poniedziałek"</string> - <string name="day_of_week_long_tuesday">"wtorek"</string> - <string name="day_of_week_long_wednesday">"środa"</string> - <string name="day_of_week_long_thursday">"czwartek"</string> - <string name="day_of_week_long_friday">"piątek"</string> - <string name="day_of_week_long_saturday">"sobota"</string> - <string name="day_of_week_medium_sunday">"Nie"</string> - <string name="day_of_week_medium_monday">"Pon"</string> - <string name="day_of_week_medium_tuesday">"Wt"</string> - <string name="day_of_week_medium_wednesday">"Śro"</string> - <string name="day_of_week_medium_thursday">"Czw"</string> - <string name="day_of_week_medium_friday">"Pią"</string> - <string name="day_of_week_medium_saturday">"Sob"</string> - <string name="day_of_week_short_sunday">"Nd"</string> - <string name="day_of_week_short_monday">"Pn"</string> - <string name="day_of_week_short_tuesday">"Wt"</string> - <string name="day_of_week_short_wednesday">"Śr"</string> - <string name="day_of_week_short_thursday">"Czw"</string> - <string name="day_of_week_short_friday">"Pt"</string> - <string name="day_of_week_short_saturday">"So"</string> - <string name="day_of_week_shorter_sunday">"Nd"</string> - <string name="day_of_week_shorter_monday">"Pon"</string> - <string name="day_of_week_shorter_tuesday">"Wt"</string> - <string name="day_of_week_shorter_wednesday">"Śr"</string> - <string name="day_of_week_shorter_thursday">"Czw"</string> - <string name="day_of_week_shorter_friday">"Pt"</string> - <string name="day_of_week_shorter_saturday">"So"</string> - <string name="day_of_week_shortest_sunday">"Nd"</string> - <string name="day_of_week_shortest_monday">"Pon"</string> - <string name="day_of_week_shortest_tuesday">"Czw"</string> - <string name="day_of_week_shortest_wednesday">"Śr"</string> - <string name="day_of_week_shortest_thursday">"Czw"</string> - <string name="day_of_week_shortest_friday">"Pt"</string> - <string name="day_of_week_shortest_saturday">"Sob"</string> - <string name="month_long_january">"Styczeń"</string> - <string name="month_long_february">"Luty"</string> - <string name="month_long_march">"Marzec"</string> - <string name="month_long_april">"Kwiecień"</string> - <string name="month_long_may">"Maj"</string> - <string name="month_long_june">"Czerwiec"</string> - <string name="month_long_july">"Lipiec"</string> - <string name="month_long_august">"Sierpień"</string> - <string name="month_long_september">"Wrzesień"</string> - <string name="month_long_october">"Październik"</string> - <string name="month_long_november">"Listopad"</string> - <string name="month_long_december">"Grudzień"</string> - <string name="month_medium_january">"Sty"</string> - <string name="month_medium_february">"Lut"</string> - <string name="month_medium_march">"Mar"</string> - <string name="month_medium_april">"Kwi"</string> - <string name="month_medium_may">"Maj"</string> - <string name="month_medium_june">"Cze"</string> - <string name="month_medium_july">"Lip"</string> - <string name="month_medium_august">"Sie"</string> - <string name="month_medium_september">"Wrz"</string> - <string name="month_medium_october">"Paź"</string> - <string name="month_medium_november">"Lis"</string> - <string name="month_medium_december">"Gru"</string> - <string name="month_shortest_january">"Sty"</string> - <string name="month_shortest_february">"Lut"</string> - <string name="month_shortest_march">"Pon"</string> - <string name="month_shortest_april">"Kwi"</string> - <string name="month_shortest_may">"Maj"</string> - <string name="month_shortest_june">"Cze"</string> - <string name="month_shortest_july">"Lip"</string> - <string name="month_shortest_august">"Sie"</string> - <string name="month_shortest_september">"Wrz"</string> - <string name="month_shortest_october">"Paź"</string> - <string name="month_shortest_november">"Lis"</string> - <string name="month_shortest_december">"Gru"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Zaznacz wszystko"</string> diff --git a/core/res/res/values-pt-rBR/donottranslate-cldr.xml b/core/res/res/values-pt-rBR/donottranslate-cldr.xml new file mode 100644 index 000000000000..47290552dd2e --- /dev/null +++ b/core/res/res/values-pt-rBR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janeiro</string> + <string name="month_long_standalone_february">fevereiro</string> + <string name="month_long_standalone_march">março</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">maio</string> + <string name="month_long_standalone_june">junho</string> + <string name="month_long_standalone_july">julho</string> + <string name="month_long_standalone_august">agosto</string> + <string name="month_long_standalone_september">setembro</string> + <string name="month_long_standalone_october">outubro</string> + <string name="month_long_standalone_november">novembro</string> + <string name="month_long_standalone_december">dezembro</string> + + <string name="month_long_january">janeiro</string> + <string name="month_long_february">fevereiro</string> + <string name="month_long_march">março</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">maio</string> + <string name="month_long_june">junho</string> + <string name="month_long_july">julho</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">setembro</string> + <string name="month_long_october">outubro</string> + <string name="month_long_november">novembro</string> + <string name="month_long_december">dezembro</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">fev</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">abr</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">ago</string> + <string name="month_medium_september">set</string> + <string name="month_medium_october">out</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">segunda-feira</string> + <string name="day_of_week_long_tuesday">terça-feira</string> + <string name="day_of_week_long_wednesday">quarta-feira</string> + <string name="day_of_week_long_thursday">quinta-feira</string> + <string name="day_of_week_long_friday">sexta-feira</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">seg</string> + <string name="day_of_week_medium_tuesday">ter</string> + <string name="day_of_week_medium_wednesday">qua</string> + <string name="day_of_week_medium_thursday">qui</string> + <string name="day_of_week_medium_friday">sex</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">seg</string> + <string name="day_of_week_short_tuesday">ter</string> + <string name="day_of_week_short_wednesday">qua</string> + <string name="day_of_week_short_thursday">qui</string> + <string name="day_of_week_short_friday">sex</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">S</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">Q</string> + <string name="day_of_week_shortest_thursday">Q</string> + <string name="day_of_week_shortest_friday">S</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Ontem</string> + <string name="today">Hoje</string> + <string name="tomorrow">Amanhã</string> + + <string name="hour_minute_24">%-kh%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H'h'mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e de %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b de %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s - %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s de %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml new file mode 100644 index 000000000000..f38a2d0499d7 --- /dev/null +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Janeiro</string> + <string name="month_long_standalone_february">Fevereiro</string> + <string name="month_long_standalone_march">Março</string> + <string name="month_long_standalone_april">Abril</string> + <string name="month_long_standalone_may">Maio</string> + <string name="month_long_standalone_june">Junho</string> + <string name="month_long_standalone_july">Julho</string> + <string name="month_long_standalone_august">Agosto</string> + <string name="month_long_standalone_september">Setembro</string> + <string name="month_long_standalone_october">Outubro</string> + <string name="month_long_standalone_november">Novembro</string> + <string name="month_long_standalone_december">Dezembro</string> + + <string name="month_long_january">Janeiro</string> + <string name="month_long_february">Fevereiro</string> + <string name="month_long_march">Março</string> + <string name="month_long_april">Abril</string> + <string name="month_long_may">Maio</string> + <string name="month_long_june">Junho</string> + <string name="month_long_july">Julho</string> + <string name="month_long_august">Agosto</string> + <string name="month_long_september">Setembro</string> + <string name="month_long_october">Outubro</string> + <string name="month_long_november">Novembro</string> + <string name="month_long_december">Dezembro</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Fev</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Abr</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">Ago</string> + <string name="month_medium_september">Set</string> + <string name="month_medium_october">Out</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">segunda-feira</string> + <string name="day_of_week_long_tuesday">terça-feira</string> + <string name="day_of_week_long_wednesday">quarta-feira</string> + <string name="day_of_week_long_thursday">quinta-feira</string> + <string name="day_of_week_long_friday">sexta-feira</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">seg</string> + <string name="day_of_week_medium_tuesday">ter</string> + <string name="day_of_week_medium_wednesday">qua</string> + <string name="day_of_week_medium_thursday">qui</string> + <string name="day_of_week_medium_friday">sex</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">seg</string> + <string name="day_of_week_short_tuesday">ter</string> + <string name="day_of_week_short_wednesday">qua</string> + <string name="day_of_week_short_thursday">qui</string> + <string name="day_of_week_short_friday">sex</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">S</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">Q</string> + <string name="day_of_week_shortest_thursday">Q</string> + <string name="day_of_week_shortest_friday">S</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">Antes do meio-dia</string> + <string name="pm">Depois do meio-dia</string> + <string name="yesterday">Ontem</string> + <string name="today">Hoje</string> + <string name="tomorrow">Amanhã</string> + + <string name="hour_minute_24">%-kh%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H'h'mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e de %b de %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e de %b de %Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e de %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b de %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s - %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s de %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml new file mode 100644 index 000000000000..47290552dd2e --- /dev/null +++ b/core/res/res/values-pt/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">janeiro</string> + <string name="month_long_standalone_february">fevereiro</string> + <string name="month_long_standalone_march">março</string> + <string name="month_long_standalone_april">abril</string> + <string name="month_long_standalone_may">maio</string> + <string name="month_long_standalone_june">junho</string> + <string name="month_long_standalone_july">julho</string> + <string name="month_long_standalone_august">agosto</string> + <string name="month_long_standalone_september">setembro</string> + <string name="month_long_standalone_october">outubro</string> + <string name="month_long_standalone_november">novembro</string> + <string name="month_long_standalone_december">dezembro</string> + + <string name="month_long_january">janeiro</string> + <string name="month_long_february">fevereiro</string> + <string name="month_long_march">março</string> + <string name="month_long_april">abril</string> + <string name="month_long_may">maio</string> + <string name="month_long_june">junho</string> + <string name="month_long_july">julho</string> + <string name="month_long_august">agosto</string> + <string name="month_long_september">setembro</string> + <string name="month_long_october">outubro</string> + <string name="month_long_november">novembro</string> + <string name="month_long_december">dezembro</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">fev</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">abr</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">ago</string> + <string name="month_medium_september">set</string> + <string name="month_medium_october">out</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> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">domingo</string> + <string name="day_of_week_long_monday">segunda-feira</string> + <string name="day_of_week_long_tuesday">terça-feira</string> + <string name="day_of_week_long_wednesday">quarta-feira</string> + <string name="day_of_week_long_thursday">quinta-feira</string> + <string name="day_of_week_long_friday">sexta-feira</string> + <string name="day_of_week_long_saturday">sábado</string> + + <string name="day_of_week_medium_sunday">dom</string> + <string name="day_of_week_medium_monday">seg</string> + <string name="day_of_week_medium_tuesday">ter</string> + <string name="day_of_week_medium_wednesday">qua</string> + <string name="day_of_week_medium_thursday">qui</string> + <string name="day_of_week_medium_friday">sex</string> + <string name="day_of_week_medium_saturday">sáb</string> + + <string name="day_of_week_short_sunday">dom</string> + <string name="day_of_week_short_monday">seg</string> + <string name="day_of_week_short_tuesday">ter</string> + <string name="day_of_week_short_wednesday">qua</string> + <string name="day_of_week_short_thursday">qui</string> + <string name="day_of_week_short_friday">sex</string> + <string name="day_of_week_short_saturday">sáb</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">S</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">Q</string> + <string name="day_of_week_shortest_thursday">Q</string> + <string name="day_of_week_shortest_friday">S</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Ontem</string> + <string name="today">Hoje</string> + <string name="tomorrow">Amanhã</string> + + <string name="hour_minute_24">%-kh%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H'h'mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e de %B de %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d/%m/%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d/%m/%Y</string> + <string name="month_day">%-e de %B</string> + <string name="month">%-B</string> + <string name="month_year">%B de %Y</string> + <string name="abbrev_month_day">%-e de %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b de %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s de %2$s - %8$s de %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s de %2$s - %10$s %8$s de %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s de %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml new file mode 100644 index 000000000000..c5c5bbbdf66e --- /dev/null +++ b/core/res/res/values-pt/strings.xml @@ -0,0 +1,793 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="byteShort">"B"</string> + <string name="kilobyteShort">"KB"</string> + <string name="megabyteShort">"MB"</string> + <string name="gigabyteShort">"GB"</string> + <string name="terabyteShort">"TB"</string> + <string name="petabyteShort">"PB"</string> + <string name="untitled">"<sem título>"</string> + <string name="ellipsis">"…"</string> + <string name="emptyPhoneNumber">"(Nenhum número de telefone)"</string> + <string name="unknownName">"(Desconhecido)"</string> + <string name="defaultVoiceMailAlphaTag">"Correio de voz"</string> + <string name="defaultMsisdnAlphaTag">"MSISDN1"</string> + <string name="mmiError">"Problema de conexão ou código MMI inválido."</string> + <string name="serviceEnabled">"O serviço foi ativado."</string> + <string name="serviceEnabledFor">"O serviço foi ativado para:"</string> + <string name="serviceDisabled">"O serviço foi desativado."</string> + <string name="serviceRegistered">"O registro foi bem-sucedido."</string> + <string name="serviceErased">"Exclusão bem-sucedida."</string> + <string name="passwordIncorrect">"Senha incorreta"</string> + <string name="mmiComplete">"MMI completo."</string> + <string name="badPin">"O PIN antigo digitado não está correto."</string> + <string name="badPuk">"O PUK digitado não está correto."</string> + <string name="mismatchPin">"Os PINs digitados não correspondem."</string> + <string name="invalidPin">"Digite um PIN com 4 a 8 números."</string> + <string name="needPuk">"Seu cartão SIM está bloqueado pelo código PUK. Digite o PUK para desbloqueá-lo."</string> + <string name="needPuk2">"Digite PUK2 para desbloquear cartão SIM."</string> + <string name="ClipMmi">"ID do chamador"</string> + <string name="ClirMmi">"ID de quem realiza a chamada"</string> + <string name="CfMmi">"Transferência de chamada"</string> + <string name="CwMmi">"Chamada em espera"</string> + <string name="BaMmi">"Bloqueio de chamada"</string> + <string name="PwdMmi">"Alteração da senha"</string> + <string name="PinMmi">"Alteração de PIN"</string> + <!-- no translation found for CnipMmi (3110534680557857162) --> + <skip /> + <!-- no translation found for CnirMmi (3062102121430548731) --> + <skip /> + <!-- no translation found for ThreeWCMmi (9051047170321190368) --> + <skip /> + <!-- no translation found for RuacMmi (7827887459138308886) --> + <skip /> + <!-- no translation found for CndMmi (3116446237081575808) --> + <skip /> + <!-- no translation found for DndMmi (1265478932418334331) --> + <skip /> + <string name="CLIRDefaultOnNextCallOn">"ID do chamador assume o padrão de restrito. Próxima chamada: restrita"</string> + <string name="CLIRDefaultOnNextCallOff">"ID do chamador assume o padrão de restrito. Próxima chamada: não restrita"</string> + <string name="CLIRDefaultOffNextCallOn">"ID do chamador assume o padrão de não restrito. Próxima chamada: restrita"</string> + <string name="CLIRDefaultOffNextCallOff">"ID do chamador assume o padrão de não restrito. Próxima chamada: não restrita"</string> + <string name="serviceNotProvisioned">"Serviço não fornecido"</string> + <string name="CLIRPermanent">"A configuração da ID do chamador não pode ser alterada."</string> + <!-- no translation found for RestrictedChangedTitle (5592189398956187498) --> + <skip /> + <!-- no translation found for RestrictedOnData (8653794784690065540) --> + <skip /> + <!-- no translation found for RestrictedOnEmergency (6581163779072833665) --> + <skip /> + <!-- no translation found for RestrictedOnNormal (2045364908281990708) --> + <skip /> + <!-- no translation found for RestrictedOnAll (4923139582141626159) --> + <skip /> + <string name="serviceClassVoice">"Voz"</string> + <string name="serviceClassData">"Dados"</string> + <string name="serviceClassFAX">"FAX"</string> + <string name="serviceClassSMS">"SMS"</string> + <string name="serviceClassDataAsync">"Assíncrono"</string> + <string name="serviceClassDataSync">"Sincronizar"</string> + <string name="serviceClassPacket">"Pacote"</string> + <string name="serviceClassPAD">"PAD"</string> + <!-- no translation found for roamingText0 (7170335472198694945) --> + <skip /> + <!-- no translation found for roamingText1 (5314861519752538922) --> + <skip /> + <!-- no translation found for roamingText2 (8969929049081268115) --> + <skip /> + <!-- no translation found for roamingText3 (5148255027043943317) --> + <skip /> + <!-- no translation found for roamingText4 (8808456682550796530) --> + <skip /> + <!-- no translation found for roamingText5 (7604063252850354350) --> + <skip /> + <!-- no translation found for roamingText6 (2059440825782871513) --> + <skip /> + <!-- no translation found for roamingText7 (7112078724097233605) --> + <skip /> + <!-- no translation found for roamingText8 (5989569778604089291) --> + <skip /> + <!-- no translation found for roamingText9 (7969296811355152491) --> + <skip /> + <!-- no translation found for roamingText10 (3992906999815316417) --> + <skip /> + <!-- no translation found for roamingText11 (4154476854426920970) --> + <skip /> + <!-- no translation found for roamingText12 (1189071119992726320) --> + <skip /> + <!-- no translation found for roamingTextSearching (8360141885972279963) --> + <skip /> + <string name="cfTemplateNotForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não transferido"</string> + <string name="cfTemplateForwarded">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> + <string name="cfTemplateForwardedTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string> + <string name="cfTemplateRegistered">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não transferido"</string> + <string name="cfTemplateRegisteredTime">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não transferido"</string> + <!-- no translation found for fcComplete (3118848230966886575) --> + <skip /> + <!-- no translation found for fcError (3327560126588500777) --> + <skip /> + <string name="httpErrorOk">"OK"</string> + <string name="httpError">"A página da web contém um erro."</string> + <string name="httpErrorLookup">"Não foi possível encontrar o URL."</string> + <string name="httpErrorUnsupportedAuthScheme">"O esquema de autenticação não é suportado."</string> + <string name="httpErrorAuth">"Falha na autenticação."</string> + <string name="httpErrorProxyAuth">"Falha na autenticação pelo servidor proxy."</string> + <string name="httpErrorConnect">"Falha na conexão com o servidor."</string> + <string name="httpErrorIO">"Falha de comunicação com o servidor. Tente novamente mais tarde."</string> + <string name="httpErrorTimeout">"Tempo limite da conexão com o servidor esgotado."</string> + <string name="httpErrorRedirectLoop">"A página contém muitos redirecionamentos do servidor."</string> + <string name="httpErrorUnsupportedScheme">"O protocolo não é suportado."</string> + <string name="httpErrorFailedSslHandshake">"Não foi possível estabelecer uma conexão segura."</string> + <string name="httpErrorBadUrl">"A página não pode ser aberta, pois o URL é inválido."</string> + <string name="httpErrorFile">"Não foi possível acessar o arquivo."</string> + <string name="httpErrorFileNotFound">"O arquivo solicitado não foi encontrado."</string> + <string name="httpErrorTooManyRequests">"Muitas solicitações sendo processadas. Tente novamente mais tarde."</string> + <string name="contentServiceSync">"Sincronizar"</string> + <string name="contentServiceSyncNotificationTitle">"Sincronizar"</string> + <string name="contentServiceTooManyDeletesNotificationDesc">"Muitas exclusões do <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string> + <string name="low_memory">"O armazenamento do telefone está cheio! Exclua alguns arquivos para liberar espaço."</string> + <string name="me">"Eu"</string> + <string name="power_dialog">"Opções do telefone"</string> + <string name="silent_mode">"Modo silencioso"</string> + <string name="turn_on_radio">"Ativar rede sem fio"</string> + <string name="turn_off_radio">"Desativar a rede sem fio"</string> + <string name="screen_lock">"Bloqueio de tela"</string> + <string name="power_off">"Desligar"</string> + <string name="shutdown_progress">"Desligando…"</string> + <string name="shutdown_confirm">"Seu telefone desligará"</string> + <string name="no_recent_tasks">"Nenhum aplicativo recente."</string> + <string name="global_actions">"Opções do telefone"</string> + <string name="global_action_lock">"Bloqueio de tela"</string> + <string name="global_action_power_off">"Desligar"</string> + <string name="global_action_toggle_silent_mode">"Modo silencioso"</string> + <string name="global_action_silent_mode_on_status">"O som está DESLIGADO"</string> + <string name="global_action_silent_mode_off_status">"O som está ATIVADO"</string> + <!-- no translation found for global_actions_toggle_airplane_mode (5884330306926307456) --> + <skip /> + <!-- no translation found for global_actions_airplane_mode_on_status (2719557982608919750) --> + <skip /> + <!-- no translation found for global_actions_airplane_mode_off_status (5075070442854490296) --> + <skip /> + <string name="safeMode">"Modo de segurança"</string> + <!-- no translation found for android_system_label (6577375335728551336) --> + <skip /> + <string name="permgrouplab_costMoney">"Serviços que custam dinheiro"</string> + <string name="permgroupdesc_costMoney">"Permite que os aplicativos façam coisas que podem custar dinheiro."</string> + <string name="permgrouplab_messages">"Suas mensagens"</string> + <string name="permgroupdesc_messages">"Ler e gravar suas mensagens SMS, e-mail e outras mensagens."</string> + <string name="permgrouplab_personalInfo">"Suas informações pessoais"</string> + <string name="permgroupdesc_personalInfo">"Acesso direto aos seus contatos e calendário armazenados no telefone."</string> + <string name="permgrouplab_location">"Sua localização"</string> + <string name="permgroupdesc_location">"Monitore seu local físico"</string> + <string name="permgrouplab_network">"Comunicação de rede"</string> + <string name="permgroupdesc_network">"Permite que os aplicativos acessem diversos recursos de rede."</string> + <string name="permgrouplab_accounts">"Suas contas do Google"</string> + <string name="permgroupdesc_accounts">"Acesse as contas do Google disponíveis."</string> + <string name="permgrouplab_hardwareControls">"Controles de hardware"</string> + <string name="permgroupdesc_hardwareControls">"Acesso direto ao hardware no handset."</string> + <string name="permgrouplab_phoneCalls">"Chamadas telefônicas"</string> + <string name="permgroupdesc_phoneCalls">"Monitorar, registrar e processar chamadas telefônicas."</string> + <string name="permgrouplab_systemTools">"Ferramentas do sistema"</string> + <string name="permgroupdesc_systemTools">"Acesso de nível inferior e controle do sistema."</string> + <string name="permgrouplab_developmentTools">"Ferramentas de desenvolvimento"</string> + <string name="permgroupdesc_developmentTools">"Recursos necessários apenas aos desenvolvedores de aplicativo."</string> + <!-- no translation found for permgrouplab_storage (1971118770546336966) --> + <skip /> + <!-- no translation found for permgroupdesc_storage (9203302214915355774) --> + <skip /> + <string name="permlab_statusBar">"desativar ou modificar a barra de status"</string> + <string name="permdesc_statusBar">"Permite que os aplicativos desativem a barra de status ou adicionem e removam ícones do sistema."</string> + <string name="permlab_expandStatusBar">"expandir/recolher barra de status"</string> + <string name="permdesc_expandStatusBar">"Permite que um aplicativo expanda ou recolha a barra de status."</string> + <string name="permlab_processOutgoingCalls">"Interceptar chamadas realizadas"</string> + <string name="permdesc_processOutgoingCalls">"Permite que aplicativos processem chamadas realizadas e alterem o número a ser discado. Aplicativos maliciosos podem monitorar, redirecionar ou impedir chamadas realizadas."</string> + <string name="permlab_receiveSms">"receber SMS"</string> + <string name="permdesc_receiveSms">"Permite que o aplicativo receba e processe mensagens SMS. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você."</string> + <string name="permlab_receiveMms">"receber MMS"</string> + <string name="permdesc_receiveMms">"Permite que o aplicativo receba e processe mensagens MMS. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você."</string> + <string name="permlab_sendSms">"enviar mensagens SMS"</string> + <string name="permdesc_sendSms">"Permite que os aplicativos enviem mensagens SMS. Os aplicativos maliciosos podem causar prejuízo financeiro a você ao enviar mensagens sem a sua confirmação."</string> + <string name="permlab_readSms">"ler SMS ou MMS"</string> + <string name="permdesc_readSms">"Permite que um aplicativo leia mensagens SMS armazenadas no seu telefone ou cartão SIM. Aplicativos maliciosos podem ler suas mensagens confidenciais."</string> + <string name="permlab_writeSms">"editar SMS ou MMS"</string> + <string name="permdesc_writeSms">"Permite que um aplicativo grave mensagens SMS armazenadas no seu telefone ou cartão SIM. Aplicativos maliciosos podem excluir suas mensagens."</string> + <string name="permlab_receiveWapPush">"receber WAP"</string> + <string name="permdesc_receiveWapPush">"Permite que o aplicativo receba e processe mensagens WAP. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você."</string> + <string name="permlab_getTasks">"recuperar aplicativos em execução"</string> + <string name="permdesc_getTasks">"Permite que os aplicativos recuperem informações sobre as tarefas em execução no momento ou recentemente. Pode permitir que aplicativos maliciosos descubram informações particulares sobre outros aplicativos."</string> + <string name="permlab_reorderTasks">"reorganizar os aplicativos em execução"</string> + <string name="permdesc_reorderTasks">"Permite que um aplicativo mova as tarefas para o primeiro ou segundo plano. Os aplicativos maliciosos podem forçar sua permanência no primeiro plano sem o seu controle."</string> + <string name="permlab_setDebugApp">"ativar depuração do aplicativo"</string> + <string name="permdesc_setDebugApp">"Permite que um aplicativo ative a depuração de outro aplicativo. Aplicativos maliciosos podem usar isso para encerrar outros aplicativos."</string> + <string name="permlab_changeConfiguration">"alterar as configurações da sua IU"</string> + <string name="permdesc_changeConfiguration">"Permite que um aplicativo mude a configuração atual, como a localidade ou o tamanho geral de fonte."</string> + <string name="permlab_restartPackages">"reiniciar outros aplicativos"</string> + <string name="permdesc_restartPackages">"Permite que um aplicativo reinicie outros aplicativos forçosamente."</string> + <string name="permlab_forceBack">"forçar fechamento do aplicativo"</string> + <string name="permdesc_forceBack">"Permite que um aplicativo force qualquer atividade que esteja em primeiro plano a fechar e voltar. Normalmente não é necessário para aplicativos normais."</string> + <string name="permlab_dump">"recuperar estado interno do sistema"</string> + <string name="permdesc_dump">"Permite que um aplicativo recupere o estado interno do sistema. Aplicativos maliciosos podem recuperar um ampla variedade de informações privadas e seguras, as quais não deveriam precisar normalmente."</string> + <!-- no translation found for permlab_shutdown (7185747824038909016) --> + <skip /> + <!-- no translation found for permdesc_shutdown (7046500838746291775) --> + <skip /> + <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) --> + <skip /> + <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) --> + <skip /> + <string name="permlab_runSetActivityWatcher">"monitorar e controle toda inicialização de aplicativo"</string> + <string name="permdesc_runSetActivityWatcher">"Permite que um aplicativo monitore e controle como o sistema inicia as atividades. Os aplicativos maliciosos podem comprometer completamente o sistema. Esta permissão é necessária apenas para desenvolvimento, nunca para uso normal do telefone."</string> + <string name="permlab_broadcastPackageRemoved">"enviar transmissão de pacote removido"</string> + <string name="permdesc_broadcastPackageRemoved">"Permite que um aplicativo transmita uma notificação de que o pacote de um aplicativo foi removido. Aplicativos maliciosos podem usar isso para encerrar outro aplicativo em execução."</string> + <string name="permlab_broadcastSmsReceived">"enviar transmissão de SMS recebido"</string> + <string name="permdesc_broadcastSmsReceived">"Permite que um aplicativo transmita uma notificação de que uma mensagem SMS foi recebida. Aplicativos maliciosos podem usar isso para forjar o recebimento de mensagens SMS."</string> + <string name="permlab_broadcastWapPush">"enviar transmissão de WAP-PUSH recebido"</string> + <string name="permdesc_broadcastWapPush">"Permite que um aplicativo transmita uma notificação de que uma mensagem WAP PUSH foi recebida. Aplicativos maliciosos podem usar isso para forjar o recebimento de uma mensagem MMS ou substituir silenciosamente o conteúdo de qualquer página da web por variantes maliciosas."</string> + <string name="permlab_setProcessLimit">"limitar o número de processos em execução"</string> + <string name="permdesc_setProcessLimit">"Permite que um aplicativo controle o número máximo de processos que serão executados. Nunca é necessário para aplicativos normais."</string> + <string name="permlab_setAlwaysFinish">"fazer todos os aplicativos em segundo plano fechar"</string> + <string name="permdesc_setAlwaysFinish">"Permite que um aplicativo controle se as atividades são sempre concluídas assim que vão para o segundo plano. Nunca é necessário para aplicativos normais."</string> + <string name="permlab_batteryStats">"Modificar as estatísticas da bateria"</string> + <string name="permdesc_batteryStats">"Permite a modificação das estatísticas coletadas sobre a bateria. Não deve ser usado em aplicativos normais."</string> + <!-- no translation found for permlab_backup (470013022865453920) --> + <skip /> + <!-- no translation found for permdesc_backup (2305432853944929371) --> + <skip /> + <string name="permlab_internalSystemWindow">"exibir janelas não autorizadas"</string> + <string name="permdesc_internalSystemWindow">"Permite a criação de janelas que devem ser usadas pela interface de usuário do sistema interno. Normalmente não é necessário para aplicativos normais."</string> + <string name="permlab_systemAlertWindow">"exibir alertas do nível do sistema"</string> + <string name="permdesc_systemAlertWindow">"Permite que um aplicativo mostre janelas de alerta do sistema. Aplicativos maliciosos podem assumir o controle de toda a tela do telefone."</string> + <string name="permlab_setAnimationScale">"modificar a velocidade de animação global"</string> + <string name="permdesc_setAnimationScale">"Permite que um aplicativo altere a velocidade de animação global (animações mais rápidas ou mais lentas) a qualquer momento."</string> + <string name="permlab_manageAppTokens">"gerenciar os símbolos do aplicativo"</string> + <string name="permdesc_manageAppTokens">"Permite que um aplicativo crie e gerencie seus próprio símbolos, ignorando a ordem-Z (Z-ordering). Normalmente não é necessário para aplicativos normais."</string> + <string name="permlab_injectEvents">"pressionar as teclas e os botões de controle"</string> + <string name="permdesc_injectEvents">"Permite que um aplicativo proporcione seus próprios eventos de entrada (pressionamentos de tecla etc.) a outros aplicativos. Aplicativos maliciosos podem usar isso para assumir o controle do telefone."</string> + <string name="permlab_readInputState">"registrar o que você digita e as ações que executa"</string> + <string name="permdesc_readInputState">"Permite que os aplicativos observem as teclas que você pressiona ao interagir com outro aplicativo (como ao digitar uma senha). Normalmente não é necessário para aplicativos normais."</string> + <string name="permlab_bindInputMethod">"aderir a um método de entrada"</string> + <string name="permdesc_bindInputMethod">"Permite que o portador se vincule à interface de nível superior de um método de entrada. Normalmente não é necessário em aplicativos normais."</string> + <string name="permlab_setOrientation">"alterar orientação da tela"</string> + <string name="permdesc_setOrientation">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Normalmente não é necessário para aplicativos normais."</string> + <string name="permlab_signalPersistentProcesses">"enviar sinais de Linux aos aplicativos"</string> + <string name="permdesc_signalPersistentProcesses">"Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes."</string> + <string name="permlab_persistentActivity">"fazer com que o aplicativo execute sempre"</string> + <string name="permdesc_persistentActivity">"Permite que um aplicativo torne partes dele mesmo persistentes, para que o sistema não possa usá-lo para outros aplicativos."</string> + <string name="permlab_deletePackages">"excluir aplicativos"</string> + <string name="permdesc_deletePackages">"Permite que um aplicativo exclua pacotes do Android. Aplicativos maliciosos podem usar isso para excluir aplicativos importantes."</string> + <string name="permlab_clearAppUserData">"excluir os dados de outros aplicativos"</string> + <string name="permdesc_clearAppUserData">"Permite que um aplicativo limpe os dados do usuário."</string> + <string name="permlab_deleteCacheFiles">"excluir o cache de outros aplicativos"</string> + <string name="permdesc_deleteCacheFiles">"Permite que um aplicativo exclua arquivos armazenados em cache."</string> + <string name="permlab_getPackageSize">"medir o espaço de armazenamento do aplicativo"</string> + <string name="permdesc_getPackageSize">"Permite que um aplicativo recupere seu código, dados e tamanho de cache"</string> + <string name="permlab_installPackages">"instalar os aplicativos diretamente"</string> + <string name="permdesc_installPackages">"Permite que um aplicativo instale pacotes novos ou atualizados do Android. Aplicativos maliciosos podem usar isso para adicionar novos aplicativos com permissões aleatórias avançadas."</string> + <string name="permlab_clearAppCache">"excluir todos os dados do cache do aplicativo"</string> + <string name="permdesc_clearAppCache">"Permite que um aplicativo libere espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente restrito ao processo do sistema."</string> + <string name="permlab_readLogs">"ler arquivos do registro do sistema"</string> + <string name="permdesc_readLogs">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, mas esses arquivos não devem conter informações pessoais ou privadas."</string> + <string name="permlab_diagnostic">"ler/gravar em recursos que pertencem ao diagnóstico"</string> + <string name="permdesc_diagnostic">"Permite que um aplicativo leia e grave em qualquer recurso que pertença ao grupo diag; por exemplo, arquivos em /dev. Isso poderia afetar a estabilidade e a segurança do sistema. Por isso, SÓ deve ser usado para diagnósticos específicos do hardware pelo fabricante ou operador."</string> + <string name="permlab_changeComponentState">"ativar ou desativar componentes do aplicativo"</string> + <string name="permdesc_changeComponentState">"Permite que um aplicativo altere a ativação ou desativação de um componente de outro aplicativo. Aplicativos maliciosos podem usar isso para desativar recursos importantes do telefone. É preciso ter permissão e cuidado no uso, pois é possível deixar os componentes do aplicativo em um estado inutilizável, inconsistente ou instável."</string> + <string name="permlab_setPreferredApplications">"definir aplicativos preferidos"</string> + <string name="permdesc_setPreferredApplications">"Permite que um aplicativo modifique seus aplicativos preferidos. Isso pode permitir que aplicativos maliciosos alterem silenciosamente os aplicativos em execução, falsificando seus aplicativos existentes para coletar seus dados privados."</string> + <string name="permlab_writeSettings">"modificar configurações globais do sistema"</string> + <string name="permdesc_writeSettings">"Permite que um aplicativo modifique os dados da configuração do sistema. Aplicativos maliciosos podem corromper a configuração do sistema."</string> + <string name="permlab_writeSecureSettings">"modificar configurações de segurança do sistema"</string> + <string name="permdesc_writeSecureSettings">"Permite que um aplicativo modifique os dados das configurações de segurança dos sistemas. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_writeGservices">"modificar o mapa de serviços do Google"</string> + <string name="permdesc_writeGservices">"Permite que um aplicativo modifique o mapa de serviços do Google. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_receiveBootCompleted">"iniciar automaticamente na inicialização"</string> + <string name="permdesc_receiveBootCompleted">"Permite que um aplicativo se inicie assim que o sistema termina de inicializar. Isso pode causar uma demora na inicialização do telefone e faz com que todo o telefone fique mais lento pela execução contínua do aplicativo."</string> + <string name="permlab_broadcastSticky">"enviar transmissão complexa"</string> + <string name="permdesc_broadcastSticky">"Permite que um aplicativo envie transmissões persistentes, as quais permanecem após o término da transmissão. Aplicativos maliciosos podem tornar o telefone lento ou instável fazendo com que use muita memória."</string> + <string name="permlab_readContacts">"ler dados de contato"</string> + <string name="permdesc_readContacts">"Permite que um aplicativo leia todos os dados de contato (endereço) armazenados no telefone. Aplicativos maliciosos podem usar isso para enviar seus dados a outras pessoas."</string> + <string name="permlab_writeContacts">"gravar dados de contato"</string> + <string name="permdesc_writeContacts">"Permite que um aplicativo modifique os dados de contato (endereço) armazenados no telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar seus dados de contato."</string> + <string name="permlab_writeOwnerData">"gravar dados do proprietário"</string> + <string name="permdesc_writeOwnerData">"Permite que um aplicativo modifique os dados do proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar os dados do proprietário."</string> + <string name="permlab_readOwnerData">"ler dados do proprietário"</string> + <string name="permdesc_readOwnerData">"Permite que um aplicativo leia os dados do proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para ler os dados do proprietário."</string> + <string name="permlab_readCalendar">"ler os dados do calendário"</string> + <string name="permdesc_readCalendar">"Permite que um aplicativo leia todos os eventos de calendário armazenados no seu telefone. Aplicativos maliciosos podem usar isso para enviar os eventos do seu calendário a outras pessoas."</string> + <string name="permlab_writeCalendar">"gravar dados do calendário"</string> + <string name="permdesc_writeCalendar">"Permite que um aplicativo modifique os eventos do calendário armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar seus dados de contato."</string> + <string name="permlab_accessMockLocation">"imitar fontes de localização para teste"</string> + <string name="permdesc_accessMockLocation">"Criar imitação de fontes de localização para teste. Os aplicativos maliciosos podem usar isso para sobrescrever o local e/ou status retornado pelas fontes de localização reais como GPS ou provedores de rede."</string> + <string name="permlab_accessLocationExtraCommands">"acessar comandos extra do provedor de localização"</string> + <string name="permdesc_accessLocationExtraCommands">"Acessar comandos extra de fornecedor de localização. Aplicativos maliciosos podem usar isso para interferir com a operação do GPS ou com outras fontes de localização."</string> + <!-- no translation found for permlab_installLocationProvider (6578101199825193873) --> + <skip /> + <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) --> + <skip /> + <string name="permlab_accessFineLocation">"Localização precisa (GPS)"</string> + <string name="permdesc_accessFineLocation">"Acesse fontes de localização precisa como o sistema GPS (Global Positioning System) no telefone, quando estiver disponível. Aplicativos maliciosos podem usar isso para determinar onde você está e também pode consumir energia da bateria."</string> + <string name="permlab_accessCoarseLocation">"Local inadequado (com base na rede)"</string> + <string name="permdesc_accessCoarseLocation">"Acessar fontes de localização aproximada como o banco de dados de rede de celular para determinar a localização aproximada de um telefone, quando houver disponibilidade. Aplicativos maliciosos podem usar isso para determinar sua localização aproximada."</string> + <string name="permlab_accessSurfaceFlinger">"acessar SurfaceFlinger"</string> + <string name="permdesc_accessSurfaceFlinger">"Permite que o aplicativo use os recursos de nível inferior do SurfaceFlinger."</string> + <string name="permlab_readFrameBuffer">"ler buffer do quadro"</string> + <string name="permdesc_readFrameBuffer">"Permite que o aplicativo leia o conteúdo do buffer do quadro."</string> + <string name="permlab_modifyAudioSettings">"alterar as configurações do seu áudio"</string> + <string name="permdesc_modifyAudioSettings">"Permite que o aplicativo modifique as configurações de áudio globais como volume e roteamento."</string> + <string name="permlab_recordAudio">"gravar áudio"</string> + <string name="permdesc_recordAudio">"Permite que o aplicativo acesso o caminho do registro de áudio."</string> + <string name="permlab_camera">"tirar fotos"</string> + <string name="permdesc_camera">"Permite que o aplicativo tire fotos com a câmera. Isso permite que o aplicativo colete imagens exibidas pela câmera a qualquer momento."</string> + <string name="permlab_brick">"desativar permanentemente o telefone"</string> + <string name="permdesc_brick">"Permite que o aplicativo desative todo o telefone permanentemente. Isso é muito perigoso."</string> + <string name="permlab_reboot">"forçar reinicializarão do telefone"</string> + <string name="permdesc_reboot">"Permite que o aplicativo force a reinicialização do telefone."</string> + <string name="permlab_mount_unmount_filesystems">"montar e desmontar sistemas de arquivos"</string> + <string name="permdesc_mount_unmount_filesystems">"Permite que o aplicativo monte e desmonte sistemas de arquivos para armazenamento removível."</string> + <!-- no translation found for permlab_mount_format_filesystems (5523285143576718981) --> + <skip /> + <!-- no translation found for permdesc_mount_format_filesystems (574060044906047386) --> + <skip /> + <string name="permlab_vibrate">"controlar vibrador"</string> + <string name="permdesc_vibrate">"Permite que o aplicativo controle o vibrador."</string> + <string name="permlab_flashlight">"controlar lanterna"</string> + <string name="permdesc_flashlight">"Permite que o aplicativo controle a lanterna."</string> + <string name="permlab_hardware_test">"testar hardware"</string> + <string name="permdesc_hardware_test">"Permite que o aplicativo controle diversos periféricos para teste de hardware."</string> + <string name="permlab_callPhone">"chamar números de telefone diretamente"</string> + <string name="permdesc_callPhone">"Permite que o aplicativo chame números de telefone sem sua intervenção. Aplicativos maliciosos podem causar a aparição de chamadas inesperadas na conta do seu telefone. Observe que isso não permite que o aplicativo ligue para números de emergência."</string> + <string name="permlab_callPrivileged">"chamar quaisquer números de telefone diretamente"</string> + <string name="permdesc_callPrivileged">"Permite que o aplicativo chame qualquer número de telefone, incluindo números de emergência, sem sua intervenção. Aplicativos maliciosos podem fazer chamadas desnecessárias e ilegais para serviços de emergência."</string> + <string name="permlab_locationUpdates">"controlar notificações de atualização de localização"</string> + <string name="permdesc_locationUpdates">"Permite a ativação/desativação das notificações sobre atualização de localização pelo rádio. Não deve ser usado em aplicativos normais."</string> + <string name="permlab_checkinProperties">"acessar propriedades de verificação"</string> + <string name="permdesc_checkinProperties">"Permite acesso de leitura/gravação às propriedades enviadas pelo serviço de verificação. Não deve ser usado em aplicativos normais."</string> + <!-- no translation found for permlab_bindGadget (776905339015863471) --> + <skip /> + <!-- no translation found for permdesc_bindGadget (2098697834497452046) --> + <skip /> + <string name="permlab_modifyPhoneState">"modificar estado do telefone"</string> + <string name="permdesc_modifyPhoneState">"Permite que o aplicativo controle os recursos do telefone do dispositivo. Um aplicativo com essa permissão pode alternar entre redes, ligar e desligar o rádio e executar ações parecidas sem o notificar."</string> + <string name="permlab_readPhoneState">"ler estado do telefone"</string> + <string name="permdesc_readPhoneState">"Permite que o aplicativo acesse os recursos do telefone do aparelho. Um aplicativo com essa permissão pode determinar o número deste telefone, se uma chamada está ativa, o número com o qual está chamada está conectada e outras coisas semelhantes."</string> + <string name="permlab_wakeLock">"impedir que o telefone entre em repouso"</string> + <string name="permdesc_wakeLock">"Permite que um aplicativo impeça o telefone de entrar em repouso."</string> + <string name="permlab_devicePower">"ligar ou desligar o telefone"</string> + <string name="permdesc_devicePower">"Permite que o aplicativo ligue ou desligue o telefone."</string> + <string name="permlab_factoryTest">"executar no modo de teste de fábrica"</string> + <string name="permdesc_factoryTest">"Executar como um teste de fabricante de nível inferior, permitindo o acesso completo ao hardware do telefone. Disponível apenas quando um telefone está executando no modo de teste de fábrica."</string> + <string name="permlab_setWallpaper">"definir papel de parede"</string> + <string name="permdesc_setWallpaper">"Permite que o aplicativo defina o papel de parede do sistema."</string> + <string name="permlab_setWallpaperHints">"definir dicas de tamanho de papel de parede"</string> + <string name="permdesc_setWallpaperHints">"Permite que o aplicativo defina as dicas de tamanho do papel de parede do sistema."</string> + <string name="permlab_masterClear">"reiniciar o sistema com o padrão de fábrica"</string> + <string name="permdesc_masterClear">"Permite que um aplicativo reinicie completamente o sistema com suas configurações de fábrica, apagando todos os dados, configuração e aplicativos instalados."</string> + <string name="permlab_setTimeZone">"definir fuso horário"</string> + <string name="permdesc_setTimeZone">"Permite que um aplicativo altere o fuso horário do telefone."</string> + <string name="permlab_getAccounts">"descobrir contas conhecidas"</string> + <string name="permdesc_getAccounts">"Permite que um aplicativo obtenha a lista de contas conhecidas pelo telefone."</string> + <string name="permlab_accessNetworkState">"exibir estado da rede"</string> + <string name="permdesc_accessNetworkState">"Permite que um aplicativo exiba o estado de todas as redes."</string> + <string name="permlab_createNetworkSockets">"acesso total à Internet"</string> + <string name="permdesc_createNetworkSockets">"Permite que um aplicativo crie soquetes de rede."</string> + <string name="permlab_writeApnSettings">"gravar configurações de Nome do ponto de acesso"</string> + <string name="permdesc_writeApnSettings">"Permite que um aplicativo modifique as configurações de APN, como Proxy e a Porta de qualquer APN."</string> + <string name="permlab_changeNetworkState">"alterar conectividade da rede"</string> + <string name="permdesc_changeNetworkState">"Permite que um aplicativo mude o estado da conectividade da rede."</string> + <!-- no translation found for permlab_changeBackgroundDataSetting (1400666012671648741) --> + <skip /> + <!-- no translation found for permdesc_changeBackgroundDataSetting (1001482853266638864) --> + <skip /> + <string name="permlab_accessWifiState">"exibir estado da rede Wi-Fi"</string> + <string name="permdesc_accessWifiState">"Permite que um aplicativo exiba as informações sobre o estado da rede Wi-Fi."</string> + <string name="permlab_changeWifiState">"Alterar estado de Wi-Fi"</string> + <string name="permdesc_changeWifiState">"Permite que um aplicativo se conecte e desconecte dos pontos de acesso Wi-Fi e faça alterações nas redes Wi-Fi configuradas."</string> + <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) --> + <skip /> + <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) --> + <skip /> + <string name="permlab_bluetoothAdmin">"administração do bluetooth"</string> + <string name="permdesc_bluetoothAdmin">"Permite que um aplicativo configure o telefone Bluetooth local, além de descobrir e parear com dispositivos remotos."</string> + <string name="permlab_bluetooth">"criar conexões Bluetooth"</string> + <string name="permdesc_bluetooth">"Permite que um aplicativo exiba a configuração do telefone Bluetooth local e faça e aceite conexões com os dispositivos pareados."</string> + <string name="permlab_disableKeyguard">"desativar bloqueio de teclado"</string> + <string name="permdesc_disableKeyguard">"Permite que um aplicativo desative o bloqueio do teclado e qualquer segurança de senha associada. Um exemplo legítimo disso é o telefone desativando o bloqueio do teclado ao receber uma chamada e reativando o bloqueio ao final da chamada."</string> + <string name="permlab_readSyncSettings">"ler configurações de sincronização"</string> + <string name="permdesc_readSyncSettings">"Permite que um aplicativo leia as configurações de sincronização, por exemplo se a sincronização está ativada para Contatos."</string> + <string name="permlab_writeSyncSettings">"gravar configurações de sincronização"</string> + <string name="permdesc_writeSyncSettings">"Permite que um aplicativo modifique as configurações de sincronização, por exemplo se a sincronização está ativada para Contatos."</string> + <string name="permlab_readSyncStats">"ler estatísticas de sincronização"</string> + <string name="permdesc_readSyncStats">"Permite que um aplicativo leia as estatísticas de sincronização; por exemplo, o histórico de sincronizações realizadas."</string> + <string name="permlab_subscribedFeedsRead">"ler feeds inscritos"</string> + <string name="permdesc_subscribedFeedsRead">"Permite que um aplicativo obtenha detalhes sobre os feeds sincronizados atualmente."</string> + <string name="permlab_subscribedFeedsWrite">"gravar feeds inscritos"</string> + <string name="permdesc_subscribedFeedsWrite">"Permite que um aplicativo modifique seus feeds sincronizados recentemente. Isso poderia permitir que um aplicativo malicioso alterasse seus feeds sincronizados."</string> + <!-- no translation found for permlab_readDictionary (432535716804748781) --> + <skip /> + <!-- no translation found for permdesc_readDictionary (1082972603576360690) --> + <skip /> + <!-- no translation found for permlab_writeDictionary (6703109511836343341) --> + <skip /> + <!-- no translation found for permdesc_writeDictionary (2241256206524082880) --> + <skip /> + <!-- no translation found for permlab_sdcardWrite (8079403759001777291) --> + <skip /> + <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) --> + <skip /> + <string-array name="phoneTypes"> + <item>"Página Inicial"</item> + <item>"Celular"</item> + <item>"Trabalho"</item> + <item>"Fax comercial"</item> + <item>"Fax doméstico"</item> + <item>"Pager"</item> + <item>"Outro"</item> + <item>"Personalizar"</item> + </string-array> + <string-array name="emailAddressTypes"> + <item>"Página Inicial"</item> + <item>"Trabalho"</item> + <item>"Outro"</item> + <item>"Personalizar"</item> + </string-array> + <!-- no translation found for mobileEmailTypeName (2858957283716687707) --> + <skip /> + <string-array name="postalAddressTypes"> + <item>"Página Inicial"</item> + <item>"Trabalho"</item> + <item>"Outro"</item> + <item>"Personalizar"</item> + </string-array> + <string-array name="imAddressTypes"> + <item>"Página Inicial"</item> + <item>"Trabalho"</item> + <item>"Outro"</item> + <item>"Personalizar"</item> + </string-array> + <string-array name="organizationTypes"> + <item>"Trabalho"</item> + <item>"Outro"</item> + <item>"Personalizar"</item> + </string-array> + <string-array name="imProtocols"> + <item>"AIM"</item> + <item>"Windows Live"</item> + <item>"Yahoo"</item> + <item>"Skype"</item> + <item>"QQ"</item> + <item>"Google Talk"</item> + <item>"ICQ"</item> + <item>"Jabber"</item> + </string-array> + <string name="keyguard_password_enter_pin_code">"Digite o código PIN"</string> + <string name="keyguard_password_wrong_pin_code">"Código PIN incorreto!"</string> + <string name="keyguard_label_text">"Para desbloquear, pressione Menu e 0."</string> + <string name="emergency_call_dialog_number_for_display">"Número de emergência"</string> + <string name="lockscreen_carrier_default">"(Sem serviço)"</string> + <string name="lockscreen_screen_locked">"Tela bloqueada."</string> + <string name="lockscreen_instructions_when_pattern_enabled">"Pressione Menu para desbloquear ou fazer chamada de emergência."</string> + <string name="lockscreen_instructions_when_pattern_disabled">"Pressione Menu para desbloquear."</string> + <string name="lockscreen_pattern_instructions">"Desenhar padrão para desbloqueio"</string> + <string name="lockscreen_emergency_call">"Chamada de emergência"</string> + <string name="lockscreen_pattern_correct">"Correto!"</string> + <string name="lockscreen_pattern_wrong">"Sentimos muito, tente novamente"</string> + <!-- no translation found for lockscreen_plugged_in (613343852842944435) --> + <skip /> + <string name="lockscreen_low_battery">"Conecte o carregador."</string> + <string name="lockscreen_missing_sim_message_short">"Sem cartão SIM."</string> + <string name="lockscreen_missing_sim_message">"Não há um cartão SIM no telefone."</string> + <string name="lockscreen_missing_sim_instructions">"Insira um cartão SIM."</string> + <string name="lockscreen_network_locked_message">"Rede bloqueada"</string> + <string name="lockscreen_sim_puk_locked_message">"O cartão SIM está bloqueado pelo PUK."</string> + <!-- no translation found for lockscreen_sim_puk_locked_instructions (635967534992394321) --> + <skip /> + <string name="lockscreen_sim_locked_message">"O cartão SIM está bloqueado."</string> + <string name="lockscreen_sim_unlock_progress_dialog_message">"Desbloqueando cartão SIM…"</string> + <string name="lockscreen_too_many_failed_attempts_dialog_message">"Você desenhou incorretamente seu padrão de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> + <string name="lockscreen_failed_attempts_almost_glogin">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, você receberá uma solicitação para desbloquear o telefone usando seu login do Google."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> + <string name="lockscreen_too_many_failed_attempts_countdown">"Tentar novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string> + <string name="lockscreen_forgot_pattern_button_text">"Esqueceu o padrão?"</string> + <string name="lockscreen_glogin_too_many_attempts">"Muitas tentativas de padrão!"</string> + <!-- no translation found for lockscreen_glogin_instructions (1816635201812207709) --> + <skip /> + <string name="lockscreen_glogin_username_hint">"Nome de usuário (e-mail)"</string> + <string name="lockscreen_glogin_password_hint">"Senha"</string> + <string name="lockscreen_glogin_submit_button">"Fazer login"</string> + <string name="lockscreen_glogin_invalid_input">"Nome de usuário ou senha inválida."</string> + <!-- no translation found for hour_ampm (4329881288269772723) --> + <skip /> + <!-- no translation found for hour_cap_ampm (1829009197680861107) --> + <skip /> + <string name="status_bar_clear_all_button">"Limpar notificações"</string> + <string name="status_bar_no_notifications_title">"Sem modificações"</string> + <string name="status_bar_ongoing_events_title">"Em andamento"</string> + <string name="status_bar_latest_events_title">"Notificações"</string> + <!-- no translation found for battery_status_text_percent_format (7660311274698797147) --> + <skip /> + <string name="battery_status_charging">"Carregando..."</string> + <string name="battery_low_title">"Conecte o carregador"</string> + <string name="battery_low_subtitle">"A carga da bateria está ficando baixa:"</string> + <string name="battery_low_percent_format">"menos de <xliff:g id="NUMBER">%d%%</xliff:g> restantes."</string> + <string name="factorytest_failed">"Falha no teste de fábrica"</string> + <string name="factorytest_not_system">"A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app."</string> + <string name="factorytest_no_action">"Nenhum pacote foi encontrado que forneça a ação FACTORY_TEST."</string> + <string name="factorytest_reboot">"Reiniciar"</string> + <!-- no translation found for js_dialog_title (8143918455087008109) --> + <skip /> + <!-- no translation found for js_dialog_title_default (6961903213729667573) --> + <skip /> + <!-- no translation found for js_dialog_before_unload (1901675448179653089) --> + <skip /> + <string name="save_password_label">"Confirmar"</string> + <string name="save_password_message">"Deseja que o navegador se lembre desta senha?"</string> + <string name="save_password_notnow">"Não agora"</string> + <string name="save_password_remember">"Lembre-se"</string> + <string name="save_password_never">"Nunca"</string> + <string name="open_permission_deny">"Você não tem permissão para abrir essa página."</string> + <string name="text_copied">"Texto copiado para a área de transferência."</string> + <string name="more_item_label">"Mais"</string> + <string name="prepend_shortcut_label">"Menu+"</string> + <string name="menu_space_shortcut_label">"espaço"</string> + <string name="menu_enter_shortcut_label">"enter"</string> + <string name="menu_delete_shortcut_label">"excluir"</string> + <string name="search_go">"Procurar"</string> + <string name="oneMonthDurationPast">"1 mês atrás"</string> + <string name="beforeOneMonthDurationPast">"Antes de 1 mês atrás"</string> + <plurals name="num_seconds_ago"> + <item quantity="one">"1 segundo atrás"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> segundos atrás"</item> + </plurals> + <plurals name="num_minutes_ago"> + <item quantity="one">"1 minute atrás"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> minutos atrás"</item> + </plurals> + <plurals name="num_hours_ago"> + <item quantity="one">"1 hora trás"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> horas atrás"</item> + </plurals> + <plurals name="num_days_ago"> + <item quantity="one">"ontem"</item> + <item quantity="other">"<xliff:g id="COUNT">%d</xliff:g> dias atrás"</item> + </plurals> + <plurals name="in_num_seconds"> + <item quantity="one">"em 1 segundo"</item> + <item quantity="other">"em <xliff:g id="COUNT">%d</xliff:g> segundos"</item> + </plurals> + <plurals name="in_num_minutes"> + <item quantity="one">"em 1 minuto"</item> + <item quantity="other">"em <xliff:g id="COUNT">%d</xliff:g> minutos"</item> + </plurals> + <plurals name="in_num_hours"> + <item quantity="one">"em 1 hora"</item> + <item quantity="other">"em <xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> + <plurals name="in_num_days"> + <item quantity="one">"amanhã"</item> + <item quantity="other">"em <xliff:g id="COUNT">%d</xliff:g> dias"</item> + </plurals> + <!-- no translation found for abbrev_num_seconds_ago:one (1849036840200069118) --> + <!-- no translation found for abbrev_num_seconds_ago:other (3699169366650930415) --> + <!-- no translation found for abbrev_num_minutes_ago:one (6361490147113871545) --> + <!-- no translation found for abbrev_num_minutes_ago:other (851164968597150710) --> + <!-- no translation found for abbrev_num_hours_ago:one (4796212039724722116) --> + <!-- no translation found for abbrev_num_hours_ago:other (6889970745748538901) --> + <!-- no translation found for abbrev_num_days_ago:one (8463161711492680309) --> + <!-- no translation found for abbrev_num_days_ago:other (3453342639616481191) --> + <!-- no translation found for abbrev_in_num_seconds:one (5842225370795066299) --> + <!-- no translation found for abbrev_in_num_seconds:other (5495880108825805108) --> + <!-- no translation found for abbrev_in_num_minutes:one (562786149928284878) --> + <!-- no translation found for abbrev_in_num_minutes:other (4216113292706568726) --> + <!-- no translation found for abbrev_in_num_hours:one (3274708118124045246) --> + <!-- no translation found for abbrev_in_num_hours:other (3705373766798013406) --> + <!-- no translation found for abbrev_in_num_days:one (2178576254385739855) --> + <!-- no translation found for abbrev_in_num_days:other (2973062968038355991) --> + <string name="preposition_for_date">"em %s"</string> + <string name="preposition_for_time">"a %s"</string> + <string name="preposition_for_year">"em %s"</string> + <string name="day">"dia"</string> + <string name="days">"dias"</string> + <string name="hour">"hora"</string> + <string name="hours">"horas"</string> + <string name="minute">"minuto"</string> + <string name="minutes">"minutos"</string> + <string name="second">"segundos"</string> + <string name="seconds">"segundos"</string> + <string name="week">"semana"</string> + <string name="weeks">"semanas"</string> + <string name="year">"ano"</string> + <string name="years">"anos"</string> + <string name="every_weekday">"Todo dia de semana (Seg–Sex)"</string> + <string name="daily">"Diariamente"</string> + <string name="weekly">"Semanalmente em <xliff:g id="DAY">%s</xliff:g>"</string> + <string name="monthly">"Mensalmente"</string> + <string name="yearly">"Anualmente"</string> + <string name="VideoView_error_title">"Não é possível reproduzir o vídeo"</string> + <!-- no translation found for VideoView_error_text_invalid_progressive_playback (897920883624437033) --> + <skip /> + <string name="VideoView_error_text_unknown">"Sentimos muito, este vídeo não pode ser reproduzido."</string> + <string name="VideoView_error_button">"OK"</string> + <!-- no translation found for relative_time (1818557177829411417) --> + <skip /> + <string name="noon">"meio-dia"</string> + <string name="Noon">"Meio-dia"</string> + <string name="midnight">"meia-noite"</string> + <string name="Midnight">"Meia-noite"</string> + <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> + <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> + <string name="selectAll">"Selecionar tudo"</string> + <string name="selectText">"Selecionar texto"</string> + <string name="stopSelectingText">"Interromper seleção de texto"</string> + <string name="cut">"Recortar"</string> + <string name="cutAll">"Recortar tudo"</string> + <string name="copy">"Copiar"</string> + <string name="copyAll">"Copiar tudo"</string> + <string name="paste">"Colar"</string> + <string name="copyUrl">"Copiar URL"</string> + <string name="inputMethod">"Método de entrada"</string> + <!-- no translation found for addToDictionary (726256909274177272) --> + <skip /> + <string name="editTextMenuTitle">"Editar texto"</string> + <string name="low_internal_storage_view_title">"Pouco espaço"</string> + <string name="low_internal_storage_view_text">"O espaço de armazenamento do telefone está diminuindo."</string> + <string name="ok">"OK"</string> + <string name="cancel">"Cancelar"</string> + <string name="yes">"OK"</string> + <string name="no">"Cancelar"</string> + <!-- no translation found for dialog_alert_title (2049658708609043103) --> + <skip /> + <string name="capital_on">"LIGAR"</string> + <string name="capital_off">"DESLIGADO"</string> + <string name="whichApplication">"Completar ação usando"</string> + <string name="alwaysUse">"Use por padrão para esta ação."</string> + <string name="clearDefaultHintMsg">"Limpar o padrão nas Configurações da página inicial> Aplicativos > Gerenciar aplicativos."</string> + <string name="chooseActivity">"Selecione uma ação"</string> + <string name="noApplications">"Nenhum aplicativo pode executar essa ação."</string> + <string name="aerr_title">"Sentimos muito."</string> + <string name="aerr_application">"O aplicativo <xliff:g id="APPLICATION">%1$s</xliff:g>(processo <xliff:g id="PROCESS">%2$s</xliff:g>) parou inesperadamente. Tente novamente."</string> + <string name="aerr_process">"O processo <xliff:g id="PROCESS">%1$s</xliff:g> parou inesperadamente. Tente novamente."</string> + <string name="anr_title">"Sentimos muito."</string> + <string name="anr_activity_application">"A atividade <xliff:g id="ACTIVITY">%1$s</xliff:g> (no aplicativo <xliff:g id="APPLICATION">%2$s</xliff:g>) não está respondendo."</string> + <string name="anr_activity_process">"A atividade <xliff:g id="ACTIVITY">%1$s</xliff:g> (<xliff:g id="PROCESS">%2$s</xliff:g> em processamento) não está respondendo."</string> + <string name="anr_application_process">"O aplicativo <xliff:g id="APPLICATION">%1$s</xliff:g> (<xliff:g id="PROCESS">%2$s</xliff:g> em processamento) não está respondendo."</string> + <string name="anr_process">"O processo <xliff:g id="PROCESS">%1$s</xliff:g> não está respondendo."</string> + <string name="force_close">"Forçar fechamento"</string> + <!-- no translation found for report (4060218260984795706) --> + <skip /> + <string name="wait">"Aguarde"</string> + <string name="debug">"Depuração"</string> + <string name="sendText">"Selecione uma ação para texto"</string> + <string name="volume_ringtone">"Volume da campainha"</string> + <string name="volume_music">"Volume da mídia"</string> + <string name="volume_music_hint_playing_through_bluetooth">"Reprodução usando Bluetooth"</string> + <string name="volume_call">"Volume da chamada recebida"</string> + <!-- no translation found for volume_bluetooth_call (2002891926351151534) --> + <skip /> + <string name="volume_alarm">"Volume do alarme"</string> + <string name="volume_notification">"Volume da notificação"</string> + <string name="volume_unknown">"Volume"</string> + <string name="ringtone_default">"Ringtone padrão"</string> + <string name="ringtone_default_with_actual">"Ringtone padrão (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> + <string name="ringtone_silent">"Silencioso"</string> + <string name="ringtone_picker_title">"Ringtones"</string> + <string name="ringtone_unknown">"Ringtone desconhecido"</string> + <plurals name="wifi_available"> + <item quantity="one">"Rede Wi-Fi disponível"</item> + <item quantity="other">"Redes Wi-Fi disponíveis"</item> + </plurals> + <plurals name="wifi_available_detailed"> + <item quantity="one">"Redes Wi-Fi abertas disponíveis"</item> + <item quantity="other">"Redes Wi-Fi abertas disponíveis"</item> + </plurals> + <string name="select_character">"Inserir caractere"</string> + <string name="sms_control_default_app_name">"Aplicativo desconhecido"</string> + <string name="sms_control_title">"Envio de mensagens SMS"</string> + <string name="sms_control_message">"Uma grande quantidade de mensagens SMS está sendo enviada. Selecione \"OK\" para continuar ou \"Cancelar\" para parar de enviar."</string> + <string name="sms_control_yes">"OK"</string> + <string name="sms_control_no">"Cancelar"</string> + <string name="date_time_set">"Definir"</string> + <string name="default_permission_group">"Padrão"</string> + <string name="no_permissions">"Nenhuma permissão é necessária"</string> + <string name="perms_hide"><b>"Ocultar"</b></string> + <string name="perms_show_all"><b>"Mostrar tudo"</b></string> + <string name="googlewebcontenthelper_loading">"Carregando…"</string> + <string name="usb_storage_title">"Conectado via USB"</string> + <string name="usb_storage_message">"Você conectou o telefone ao seu computador via USB. Selecione \"Montar\" se quiser copiar os arquivos entre seu computador e o cartão SD do telefone."</string> + <string name="usb_storage_button_mount">"Montar"</string> + <string name="usb_storage_button_unmount">"Não montar"</string> + <string name="usb_storage_error_message">"Há um problema com o uso do seu cartão SD para armazenamento USB."</string> + <string name="usb_storage_notification_title">"Conectado via USB"</string> + <string name="usb_storage_notification_message">"Selecione para copiar os arquivos para/do computador."</string> + <!-- no translation found for usb_storage_stop_notification_title (2336058396663516017) --> + <skip /> + <!-- no translation found for usb_storage_stop_notification_message (2591813490269841539) --> + <skip /> + <!-- no translation found for usb_storage_stop_title (6014127947456185321) --> + <skip /> + <!-- no translation found for usb_storage_stop_message (2390958966725232848) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_mount (1181858854166273345) --> + <skip /> + <!-- no translation found for usb_storage_stop_button_unmount (3774611918660582898) --> + <skip /> + <!-- no translation found for usb_storage_stop_error_message (3917317248440072084) --> + <skip /> + <!-- no translation found for extmedia_format_title (8663247929551095854) --> + <skip /> + <!-- no translation found for extmedia_format_message (3621369962433523619) --> + <skip /> + <!-- no translation found for extmedia_format_button_format (4131064560127478695) --> + <skip /> + <string name="select_input_method">"Selecionar método de entrada"</string> + <string name="fast_scroll_alphabet">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> + <string name="fast_scroll_numeric_alphabet">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> + <!-- no translation found for candidates_style (4333913089637062257) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_title (5457603418970994050) --> + <skip /> + <!-- no translation found for ext_media_checking_notification_message (4747432538578886744) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_title (780477838241212997) --> + <skip /> + <!-- no translation found for ext_media_nofs_notification_message (1312266820092958014) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_title (6410723906019100189) --> + <skip /> + <!-- no translation found for ext_media_unmountable_notification_message (2679412884290061775) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_title (6872152882604407837) --> + <skip /> + <!-- no translation found for ext_media_badremoval_notification_message (7260183293747448241) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_title (6729801130790616200) --> + <skip /> + <!-- no translation found for ext_media_safe_unmount_notification_message (7613960686747592770) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_title (8902518030404381318) --> + <skip /> + <!-- no translation found for ext_media_nomedia_notification_message (4205117227342822275) --> + <skip /> + <!-- no translation found for activity_list_empty (4168820609403385789) --> + <skip /> + <!-- no translation found for permlab_pkgUsageStats (8787352074326748892) --> + <skip /> + <!-- no translation found for permdesc_pkgUsageStats (891553695716752835) --> + <skip /> + <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) --> + <skip /> + <!-- no translation found for gadget_host_error_inflating (2613287218853846830) --> + <skip /> + <!-- no translation found for ime_action_go (8320845651737369027) --> + <skip /> + <!-- no translation found for ime_action_search (658110271822807811) --> + <skip /> + <!-- no translation found for ime_action_send (2316166556349314424) --> + <skip /> + <!-- no translation found for ime_action_next (3138843904009813834) --> + <skip /> + <!-- no translation found for ime_action_done (8971516117910934605) --> + <skip /> + <!-- no translation found for ime_action_default (2840921885558045721) --> + <skip /> + <!-- no translation found for dial_number_using (5789176425167573586) --> + <skip /> + <!-- no translation found for create_contact_using (4947405226788104538) --> + <skip /> + <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) --> + <skip /> + <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) --> + <skip /> +</resources> diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml new file mode 100644 index 000000000000..4622445ca6c2 --- /dev/null +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">ianuarie</string> + <string name="month_long_standalone_february">februarie</string> + <string name="month_long_standalone_march">martie</string> + <string name="month_long_standalone_april">aprilie</string> + <string name="month_long_standalone_may">mai</string> + <string name="month_long_standalone_june">iunie</string> + <string name="month_long_standalone_july">iulie</string> + <string name="month_long_standalone_august">august</string> + <string name="month_long_standalone_september">septembrie</string> + <string name="month_long_standalone_october">octombrie</string> + <string name="month_long_standalone_november">noiembrie</string> + <string name="month_long_standalone_december">decembrie</string> + + <string name="month_long_january">ianuarie</string> + <string name="month_long_february">februarie</string> + <string name="month_long_march">martie</string> + <string name="month_long_april">aprilie</string> + <string name="month_long_may">mai</string> + <string name="month_long_june">iunie</string> + <string name="month_long_july">iulie</string> + <string name="month_long_august">august</string> + <string name="month_long_september">septembrie</string> + <string name="month_long_october">octombrie</string> + <string name="month_long_november">noiembrie</string> + <string name="month_long_december">decembrie</string> + + <string name="month_medium_january">ian.</string> + <string name="month_medium_february">feb.</string> + <string name="month_medium_march">mar.</string> + <string name="month_medium_april">apr.</string> + <string name="month_medium_may">mai</string> + <string name="month_medium_june">iun.</string> + <string name="month_medium_july">iul.</string> + <string name="month_medium_august">aug.</string> + <string name="month_medium_september">sept.</string> + <string name="month_medium_october">oct.</string> + <string name="month_medium_november">nov.</string> + <string name="month_medium_december">dec.</string> + + <string name="month_shortest_january">I</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">I</string> + <string name="month_shortest_july">I</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">duminică</string> + <string name="day_of_week_long_monday">luni</string> + <string name="day_of_week_long_tuesday">marți</string> + <string name="day_of_week_long_wednesday">miercuri</string> + <string name="day_of_week_long_thursday">joi</string> + <string name="day_of_week_long_friday">vineri</string> + <string name="day_of_week_long_saturday">sâmbătă</string> + + <string name="day_of_week_medium_sunday">Du</string> + <string name="day_of_week_medium_monday">Lu</string> + <string name="day_of_week_medium_tuesday">Ma</string> + <string name="day_of_week_medium_wednesday">Mi</string> + <string name="day_of_week_medium_thursday">Jo</string> + <string name="day_of_week_medium_friday">Vi</string> + <string name="day_of_week_medium_saturday">Sâ</string> + + <string name="day_of_week_short_sunday">Du</string> + <string name="day_of_week_short_monday">Lu</string> + <string name="day_of_week_short_tuesday">Ma</string> + <string name="day_of_week_short_wednesday">Mi</string> + <string name="day_of_week_short_thursday">Jo</string> + <string name="day_of_week_short_friday">Vi</string> + <string name="day_of_week_short_saturday">Sâ</string> + + <string name="day_of_week_shortest_sunday">D</string> + <string name="day_of_week_shortest_monday">L</string> + <string name="day_of_week_shortest_tuesday">M</string> + <string name="day_of_week_shortest_wednesday">M</string> + <string name="day_of_week_shortest_thursday">J</string> + <string name="day_of_week_shortest_friday">V</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">ieri</string> + <string name="today">azi</string> + <string name="tomorrow">mâine</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S, %d.%m.%Y</string> + <string name="date_time">%2$s, %1$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s, %3$s/%2$s/%4$s - %10$s, %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s, %3$s.%2$s - %10$s, %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s, %3$s.%2$s.%4$s - %10$s, %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s, %1$s, %2$s - %6$s, %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s, %2$s - %6$s, %5$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s, %3$s %2$s - %10$s, %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s, %3$s %2$s - %10$s, %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s, %3$s %2$s %4$s - %10$s, %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s, %3$s %2$s %4$s - %10$s, %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ru-rRU/donottranslate-cldr.xml b/core/res/res/values-ru-rRU/donottranslate-cldr.xml new file mode 100644 index 000000000000..21c06ff3a0ab --- /dev/null +++ b/core/res/res/values-ru-rRU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Январь</string> + <string name="month_long_standalone_february">Февраль</string> + <string name="month_long_standalone_march">Март</string> + <string name="month_long_standalone_april">Апрель</string> + <string name="month_long_standalone_may">Май</string> + <string name="month_long_standalone_june">Июнь</string> + <string name="month_long_standalone_july">Июль</string> + <string name="month_long_standalone_august">Август</string> + <string name="month_long_standalone_september">Сентябрь</string> + <string name="month_long_standalone_october">Октябрь</string> + <string name="month_long_standalone_november">Ноябрь</string> + <string name="month_long_standalone_december">Декабрь</string> + + <string name="month_long_january">января</string> + <string name="month_long_february">февраля</string> + <string name="month_long_march">марта</string> + <string name="month_long_april">апреля</string> + <string name="month_long_may">мая</string> + <string name="month_long_june">июня</string> + <string name="month_long_july">июля</string> + <string name="month_long_august">августа</string> + <string name="month_long_september">сентября</string> + <string name="month_long_october">октября</string> + <string name="month_long_november">ноября</string> + <string name="month_long_december">декабря</string> + + <string name="month_medium_january">янв.</string> + <string name="month_medium_february">февр.</string> + <string name="month_medium_march">марта</string> + <string name="month_medium_april">апр.</string> + <string name="month_medium_may">мая</string> + <string name="month_medium_june">июня</string> + <string name="month_medium_july">июля</string> + <string name="month_medium_august">авг.</string> + <string name="month_medium_september">сент.</string> + <string name="month_medium_october">окт.</string> + <string name="month_medium_november">нояб.</string> + <string name="month_medium_december">дек.</string> + + <string name="month_shortest_january">Я</string> + <string name="month_shortest_february">Ф</string> + <string name="month_shortest_march">М</string> + <string name="month_shortest_april">А</string> + <string name="month_shortest_may">М</string> + <string name="month_shortest_june">И</string> + <string name="month_shortest_july">И</string> + <string name="month_shortest_august">А</string> + <string name="month_shortest_september">С</string> + <string name="month_shortest_october">О</string> + <string name="month_shortest_november">Н</string> + <string name="month_shortest_december">Д</string> + + <string name="day_of_week_long_sunday">воскресенье</string> + <string name="day_of_week_long_monday">понедельник</string> + <string name="day_of_week_long_tuesday">вторник</string> + <string name="day_of_week_long_wednesday">среда</string> + <string name="day_of_week_long_thursday">четверг</string> + <string name="day_of_week_long_friday">пятница</string> + <string name="day_of_week_long_saturday">суббота</string> + + <string name="day_of_week_medium_sunday">Вс</string> + <string name="day_of_week_medium_monday">Пн</string> + <string name="day_of_week_medium_tuesday">Вт</string> + <string name="day_of_week_medium_wednesday">Ср</string> + <string name="day_of_week_medium_thursday">Чт</string> + <string name="day_of_week_medium_friday">Пт</string> + <string name="day_of_week_medium_saturday">Сб</string> + + <string name="day_of_week_short_sunday">Вс</string> + <string name="day_of_week_short_monday">Пн</string> + <string name="day_of_week_short_tuesday">Вт</string> + <string name="day_of_week_short_wednesday">Ср</string> + <string name="day_of_week_short_thursday">Чт</string> + <string name="day_of_week_short_friday">Пт</string> + <string name="day_of_week_short_saturday">Сб</string> + + <string name="day_of_week_shortest_sunday">В</string> + <string name="day_of_week_shortest_monday">П</string> + <string name="day_of_week_shortest_tuesday">В</string> + <string name="day_of_week_shortest_wednesday">С</string> + <string name="day_of_week_shortest_thursday">Ч</string> + <string name="day_of_week_shortest_friday">П</string> + <string name="day_of_week_shortest_saturday">С</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Вчера</string> + <string name="today">Сегодня</string> + <string name="tomorrow">Завтра</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y г.</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s-%3$s – %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="wday1_date1_wday2_date2">%2$s - %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s</string> + <string name="wday_date">%3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s </string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s </string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml new file mode 100644 index 000000000000..21c06ff3a0ab --- /dev/null +++ b/core/res/res/values-ru/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Январь</string> + <string name="month_long_standalone_february">Февраль</string> + <string name="month_long_standalone_march">Март</string> + <string name="month_long_standalone_april">Апрель</string> + <string name="month_long_standalone_may">Май</string> + <string name="month_long_standalone_june">Июнь</string> + <string name="month_long_standalone_july">Июль</string> + <string name="month_long_standalone_august">Август</string> + <string name="month_long_standalone_september">Сентябрь</string> + <string name="month_long_standalone_october">Октябрь</string> + <string name="month_long_standalone_november">Ноябрь</string> + <string name="month_long_standalone_december">Декабрь</string> + + <string name="month_long_january">января</string> + <string name="month_long_february">февраля</string> + <string name="month_long_march">марта</string> + <string name="month_long_april">апреля</string> + <string name="month_long_may">мая</string> + <string name="month_long_june">июня</string> + <string name="month_long_july">июля</string> + <string name="month_long_august">августа</string> + <string name="month_long_september">сентября</string> + <string name="month_long_october">октября</string> + <string name="month_long_november">ноября</string> + <string name="month_long_december">декабря</string> + + <string name="month_medium_january">янв.</string> + <string name="month_medium_february">февр.</string> + <string name="month_medium_march">марта</string> + <string name="month_medium_april">апр.</string> + <string name="month_medium_may">мая</string> + <string name="month_medium_june">июня</string> + <string name="month_medium_july">июля</string> + <string name="month_medium_august">авг.</string> + <string name="month_medium_september">сент.</string> + <string name="month_medium_october">окт.</string> + <string name="month_medium_november">нояб.</string> + <string name="month_medium_december">дек.</string> + + <string name="month_shortest_january">Я</string> + <string name="month_shortest_february">Ф</string> + <string name="month_shortest_march">М</string> + <string name="month_shortest_april">А</string> + <string name="month_shortest_may">М</string> + <string name="month_shortest_june">И</string> + <string name="month_shortest_july">И</string> + <string name="month_shortest_august">А</string> + <string name="month_shortest_september">С</string> + <string name="month_shortest_october">О</string> + <string name="month_shortest_november">Н</string> + <string name="month_shortest_december">Д</string> + + <string name="day_of_week_long_sunday">воскресенье</string> + <string name="day_of_week_long_monday">понедельник</string> + <string name="day_of_week_long_tuesday">вторник</string> + <string name="day_of_week_long_wednesday">среда</string> + <string name="day_of_week_long_thursday">четверг</string> + <string name="day_of_week_long_friday">пятница</string> + <string name="day_of_week_long_saturday">суббота</string> + + <string name="day_of_week_medium_sunday">Вс</string> + <string name="day_of_week_medium_monday">Пн</string> + <string name="day_of_week_medium_tuesday">Вт</string> + <string name="day_of_week_medium_wednesday">Ср</string> + <string name="day_of_week_medium_thursday">Чт</string> + <string name="day_of_week_medium_friday">Пт</string> + <string name="day_of_week_medium_saturday">Сб</string> + + <string name="day_of_week_short_sunday">Вс</string> + <string name="day_of_week_short_monday">Пн</string> + <string name="day_of_week_short_tuesday">Вт</string> + <string name="day_of_week_short_wednesday">Ср</string> + <string name="day_of_week_short_thursday">Чт</string> + <string name="day_of_week_short_friday">Пт</string> + <string name="day_of_week_short_saturday">Сб</string> + + <string name="day_of_week_shortest_sunday">В</string> + <string name="day_of_week_shortest_monday">П</string> + <string name="day_of_week_shortest_tuesday">В</string> + <string name="day_of_week_shortest_wednesday">С</string> + <string name="day_of_week_shortest_thursday">Ч</string> + <string name="day_of_week_shortest_friday">П</string> + <string name="day_of_week_shortest_saturday">С</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Вчера</string> + <string name="today">Сегодня</string> + <string name="tomorrow">Завтра</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y г.</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %d.%m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s-%3$s – %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="wday1_date1_wday2_date2">%2$s - %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s</string> + <string name="wday_date">%3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s </string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s </string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index fac6cb7ea753..048f6b1c20d0 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"Позволяет приложению изменять текущую конфигурацию, например локаль и общий размер шрифта."</string> <string name="permlab_restartPackages">"перезапускать другие приложения"</string> <string name="permdesc_restartPackages">"Разрешает приложению принудительно перезапускать другие приложения."</string> - <string name="permlab_setProcessForeground">"предотвращать остановку"</string> - <string name="permdesc_setProcessForeground">"Разрешает приложению запускать любые процессы на переднем плане так, что их нельзя прекратить. Не требуется обычным приложениям."</string> <string name="permlab_forceBack">"принудительно закрывать приложения"</string> <string name="permdesc_forceBack">"Позволяет приложению принудительно закрывать и переводить в фоновый режим действия, работающие на переднем плане. Не требуется обычным приложениям."</string> <string name="permlab_dump">"получать внутреннее состояние системы"</string> <string name="permdesc_dump">"Разрешает приложениям получать внутреннее состояние системы. Вредоносное ПО может получать множество личной и защищенной информации, которая обычно не была бы им доступна."</string> - <string name="permlab_addSystemService">"публиковать службы низкого уровня"</string> - <string name="permdesc_addSystemService">"Разрешает приложению публиковать собственные системные службы низкого уровня. Вредоносное ПО может взломать систему и украсть или повредить данные в ней."</string> <string name="permlab_runSetActivityWatcher">"наблюдать и управлять запуском всех приложений"</string> <string name="permdesc_runSetActivityWatcher">"Разрешает приложению следить и управлять тем, как система запускает действия. Вредоносное ПО может полностью нарушить работу системы. Это разрешение нужно только для разработки, но не при обычном использовании телефона."</string> <string name="permlab_broadcastPackageRemoved">"отправлять оповещения об удалении пакетов"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"Позволяет приложению контролировать максимальное количество выполняемых процессов. Не требуется обычным приложениям."</string> <string name="permlab_setAlwaysFinish">"закрывать все фоновые приложения"</string> <string name="permdesc_setAlwaysFinish">"Разрешает приложению следить, чтобы действия всегда завершались после перехода в фоновый режим. Не требуется обычным приложениям."</string> - <string name="permlab_fotaUpdate">"автоматически устанавливать системные обновления"</string> - <string name="permdesc_fotaUpdate">"Разрешает приложению получать уведомления о предстоящих обновлениях системы и запускать их установку. Это дает вредоносному ПО возможность повредить систему неавторизованными обновлениями или помешать выполнению обновления."</string> <string name="permlab_batteryStats">"изменять данные о батарее"</string> <string name="permdesc_batteryStats">"Разрешает изменять данные о батарее. Не используется обычными приложениями."</string> <string name="permlab_internalSystemWindow">"отображать неавторизованные окна"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"Пароль"</string> <string name="lockscreen_glogin_submit_button">"Войти"</string> <string name="lockscreen_glogin_invalid_input">"Недействительное имя пользователя или пароль."</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"Очистить уведомления"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"ввод"</string> <string name="menu_delete_shortcut_label">"удалить"</string> <string name="search_go">"Поиск"</string> - <string name="today">"Сегодня"</string> - <string name="yesterday">"Вчера"</string> - <string name="tomorrow">"Завтра"</string> <string name="oneMonthDurationPast">"1 месяц назад"</string> <string name="beforeOneMonthDurationPast">"Больше 1 месяца назад"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"недели"</string> <string name="year">"год"</string> <string name="years">"годы"</string> - <string name="sunday">"воскресенье"</string> - <string name="monday">"понедельник"</string> - <string name="tuesday">"вторник"</string> - <string name="wednesday">"среда"</string> - <string name="thursday">"четверг"</string> - <string name="friday">"пятница"</string> - <string name="saturday">"суббота"</string> <string name="every_weekday">"По рабочим дням (пн-пт)"</string> <string name="daily">"Ежедневно"</string> <string name="weekly">"Еженедельно в: <xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"К сожалению, это видео не подходит для потокового воспроизведения на данном устройстве."</string> <string name="VideoView_error_text_unknown">"К сожалению, это видео нельзя воспроизвести."</string> <string name="VideoView_error_button">"ОК"</string> - <string name="am">"AM"</string> - <string name="pm">"PM"</string> - <string name="numeric_date">"<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%4$s</xliff:g>, <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>, <xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>, <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="WEEKDAY">%2$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="DATE">%3$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="TIME_RANGE">%1$s</xliff:g>, <xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="DAY">d</xliff:g>' '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"полдень"</string> <string name="Noon">"Полдень"</string> <string name="midnight">"полночь"</string> <string name="Midnight">"Полночь"</string> - <string name="month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g>"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g> <xliff:g id="MONTH">%B</xliff:g> г."</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g> <xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR">%9$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="DAY1">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="DAY2">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="WEEKDAY1">%1$s</xliff:g>, <xliff:g id="DAY1_0">%3$s</xliff:g> <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="YEAR1">%4$s</xliff:g>, <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="WEEKDAY2">%6$s</xliff:g>, <xliff:g id="DAY2_1">%8$s</xliff:g> <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="YEAR2">%9$s</xliff:g>, <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="abbrev_month_year">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="YEAR">%Y</xliff:g> г."</string> - <string name="abbrev_month_day">"<xliff:g id="DAY">%-d</xliff:g> <xliff:g id="MONTH">%b</xliff:g>"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"воскресенье"</string> - <string name="day_of_week_long_monday">"понедельник"</string> - <string name="day_of_week_long_tuesday">"вторник"</string> - <string name="day_of_week_long_wednesday">"среда"</string> - <string name="day_of_week_long_thursday">"четверг"</string> - <string name="day_of_week_long_friday">"пятница"</string> - <string name="day_of_week_long_saturday">"суббота"</string> - <string name="day_of_week_medium_sunday">"вс"</string> - <string name="day_of_week_medium_monday">"пн"</string> - <string name="day_of_week_medium_tuesday">"вт"</string> - <string name="day_of_week_medium_wednesday">"ср"</string> - <string name="day_of_week_medium_thursday">"чт"</string> - <string name="day_of_week_medium_friday">"пт"</string> - <string name="day_of_week_medium_saturday">"сб"</string> - <string name="day_of_week_short_sunday">"вс"</string> - <string name="day_of_week_short_monday">"пн"</string> - <string name="day_of_week_short_tuesday">"вт"</string> - <string name="day_of_week_short_wednesday">"ср"</string> - <string name="day_of_week_short_thursday">"чт"</string> - <string name="day_of_week_short_friday">"пт"</string> - <string name="day_of_week_short_saturday">"сб"</string> - <string name="day_of_week_shorter_sunday">"вс"</string> - <string name="day_of_week_shorter_monday">"пн"</string> - <string name="day_of_week_shorter_tuesday">"вт"</string> - <string name="day_of_week_shorter_wednesday">"с"</string> - <string name="day_of_week_shorter_thursday">"чт"</string> - <string name="day_of_week_shorter_friday">"пт"</string> - <string name="day_of_week_shorter_saturday">"сб"</string> - <string name="day_of_week_shortest_sunday">"в"</string> - <string name="day_of_week_shortest_monday">"п"</string> - <string name="day_of_week_shortest_tuesday">"в"</string> - <string name="day_of_week_shortest_wednesday">"с"</string> - <string name="day_of_week_shortest_thursday">"ч"</string> - <string name="day_of_week_shortest_friday">"п"</string> - <string name="day_of_week_shortest_saturday">"с"</string> - <string name="month_long_january">"январь"</string> - <string name="month_long_february">"февраль"</string> - <string name="month_long_march">"март"</string> - <string name="month_long_april">"апрель"</string> - <string name="month_long_may">"май"</string> - <string name="month_long_june">"июнь"</string> - <string name="month_long_july">"июль"</string> - <string name="month_long_august">"август"</string> - <string name="month_long_september">"сентябрь"</string> - <string name="month_long_october">"октябрь"</string> - <string name="month_long_november">"ноябрь"</string> - <string name="month_long_december">"декабрь"</string> - <string name="month_medium_january">"янв"</string> - <string name="month_medium_february">"фев"</string> - <string name="month_medium_march">"мар"</string> - <string name="month_medium_april">"апр"</string> - <string name="month_medium_may">"май"</string> - <string name="month_medium_june">"июн"</string> - <string name="month_medium_july">"июл"</string> - <string name="month_medium_august">"авг"</string> - <string name="month_medium_september">"сен"</string> - <string name="month_medium_october">"окт"</string> - <string name="month_medium_november">"ноя"</string> - <string name="month_medium_december">"дек"</string> - <string name="month_shortest_january">"Я"</string> - <string name="month_shortest_february">"ф"</string> - <string name="month_shortest_march">"м"</string> - <string name="month_shortest_april">"а"</string> - <string name="month_shortest_may">"м"</string> - <string name="month_shortest_june">"и"</string> - <string name="month_shortest_july">"и"</string> - <string name="month_shortest_august">"а"</string> - <string name="month_shortest_september">"с"</string> - <string name="month_shortest_october">"о"</string> - <string name="month_shortest_november">"н"</string> - <string name="month_shortest_december">"д"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"Выбрать все"</string> diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml new file mode 100644 index 000000000000..16239df8c04e --- /dev/null +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">január</string> + <string name="month_long_standalone_february">február</string> + <string name="month_long_standalone_march">marec</string> + <string name="month_long_standalone_april">apríl</string> + <string name="month_long_standalone_may">máj</string> + <string name="month_long_standalone_june">jún</string> + <string name="month_long_standalone_july">júl</string> + <string name="month_long_standalone_august">august</string> + <string name="month_long_standalone_september">september</string> + <string name="month_long_standalone_october">október</string> + <string name="month_long_standalone_november">november</string> + <string name="month_long_standalone_december">december</string> + + <string name="month_long_january">januára</string> + <string name="month_long_february">februára</string> + <string name="month_long_march">marca</string> + <string name="month_long_april">apríla</string> + <string name="month_long_may">mája</string> + <string name="month_long_june">júna</string> + <string name="month_long_july">júla</string> + <string name="month_long_august">augusta</string> + <string name="month_long_september">septembra</string> + <string name="month_long_october">októbra</string> + <string name="month_long_november">novembra</string> + <string name="month_long_december">decembra</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">máj</string> + <string name="month_medium_june">jún</string> + <string name="month_medium_july">júl</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">dec</string> + + <string name="month_shortest_january">j</string> + <string name="month_shortest_february">f</string> + <string name="month_shortest_march">m</string> + <string name="month_shortest_april">a</string> + <string name="month_shortest_may">m</string> + <string name="month_shortest_june">j</string> + <string name="month_shortest_july">j</string> + <string name="month_shortest_august">a</string> + <string name="month_shortest_september">s</string> + <string name="month_shortest_october">o</string> + <string name="month_shortest_november">n</string> + <string name="month_shortest_december">d</string> + + <string name="day_of_week_long_sunday">nedeľa</string> + <string name="day_of_week_long_monday">pondelok</string> + <string name="day_of_week_long_tuesday">utorok</string> + <string name="day_of_week_long_wednesday">streda</string> + <string name="day_of_week_long_thursday">štvrtok</string> + <string name="day_of_week_long_friday">piatok</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">ne</string> + <string name="day_of_week_medium_monday">po</string> + <string name="day_of_week_medium_tuesday">ut</string> + <string name="day_of_week_medium_wednesday">st</string> + <string name="day_of_week_medium_thursday">št</string> + <string name="day_of_week_medium_friday">pi</string> + <string name="day_of_week_medium_saturday">so</string> + + <string name="day_of_week_short_sunday">ne</string> + <string name="day_of_week_short_monday">po</string> + <string name="day_of_week_short_tuesday">ut</string> + <string name="day_of_week_short_wednesday">st</string> + <string name="day_of_week_short_thursday">št</string> + <string name="day_of_week_short_friday">pi</string> + <string name="day_of_week_short_saturday">so</string> + + <string name="day_of_week_shortest_sunday">N</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">U</string> + <string name="day_of_week_shortest_wednesday">S</string> + <string name="day_of_week_shortest_thursday">Š</string> + <string name="day_of_week_shortest_friday">P</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">dopoludnia</string> + <string name="pm">popoludní</string> + <string name="yesterday">Včera</string> + <string name="today">Dnes</string> + <string name="tomorrow">Zajtra</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-e.%-m.%Y</string> + <string name="numeric_date_format">d.M.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e. %B %Y</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S %-e.%-m.%Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e.%-m.%Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s. - %8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s. - %8$s. %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml new file mode 100644 index 000000000000..b4ea32f37abd --- /dev/null +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januar</string> + <string name="month_long_standalone_february">februar</string> + <string name="month_long_standalone_march">marec</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">maj</string> + <string name="month_long_standalone_june">junij</string> + <string name="month_long_standalone_july">julij</string> + <string name="month_long_standalone_august">avgust</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">december</string> + + <string name="month_long_january">januar</string> + <string name="month_long_february">februar</string> + <string name="month_long_march">marec</string> + <string name="month_long_april">april</string> + <string name="month_long_may">maj</string> + <string name="month_long_june">junij</string> + <string name="month_long_july">julij</string> + <string name="month_long_august">avgust</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">december</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">maj</string> + <string name="month_medium_june">jun</string> + <string name="month_medium_july">jul</string> + <string name="month_medium_august">avg</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">dec</string> + + <string name="month_shortest_january">j</string> + <string name="month_shortest_february">f</string> + <string name="month_shortest_march">m</string> + <string name="month_shortest_april">a</string> + <string name="month_shortest_may">m</string> + <string name="month_shortest_june">j</string> + <string name="month_shortest_july">j</string> + <string name="month_shortest_august">a</string> + <string name="month_shortest_september">s</string> + <string name="month_shortest_october">o</string> + <string name="month_shortest_november">n</string> + <string name="month_shortest_december">d</string> + + <string name="day_of_week_long_sunday">nedelja</string> + <string name="day_of_week_long_monday">ponedeljek</string> + <string name="day_of_week_long_tuesday">torek</string> + <string name="day_of_week_long_wednesday">sreda</string> + <string name="day_of_week_long_thursday">četrtek</string> + <string name="day_of_week_long_friday">petek</string> + <string name="day_of_week_long_saturday">sobota</string> + + <string name="day_of_week_medium_sunday">ned</string> + <string name="day_of_week_medium_monday">pon</string> + <string name="day_of_week_medium_tuesday">tor</string> + <string name="day_of_week_medium_wednesday">sre</string> + <string name="day_of_week_medium_thursday">čet</string> + <string name="day_of_week_medium_friday">pet</string> + <string name="day_of_week_medium_saturday">sob</string> + + <string name="day_of_week_short_sunday">ned</string> + <string name="day_of_week_short_monday">pon</string> + <string name="day_of_week_short_tuesday">tor</string> + <string name="day_of_week_short_wednesday">sre</string> + <string name="day_of_week_short_thursday">čet</string> + <string name="day_of_week_short_friday">pet</string> + <string name="day_of_week_short_saturday">sob</string> + + <string name="day_of_week_shortest_sunday">n</string> + <string name="day_of_week_shortest_monday">p</string> + <string name="day_of_week_shortest_tuesday">t</string> + <string name="day_of_week_shortest_wednesday">s</string> + <string name="day_of_week_shortest_thursday">č</string> + <string name="day_of_week_shortest_friday">p</string> + <string name="day_of_week_shortest_saturday">s</string> + + <string name="am">dop.</string> + <string name="pm">pop.</string> + <string name="yesterday">Včeraj</string> + <string name="today">Danes</string> + <string name="tomorrow">Jutri</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e. %m. %Y</string> + <string name="numeric_date_format">d. MM. yyyy</string> + <string name="numeric_date_template">"%s. %s. %s"</string> + <string name="month_day_year">%d. %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e. %b. %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e. %b. %Y</string> + <string name="month_day">%-e. %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e. %b.</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b. %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s. %2$s. – %8$s. %7$s.</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s.</string> + <string name="numeric_mdy1_mdy2">%3$s. %2$s. %4$s – %8$s. %7$s. %9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s., %3$s. %2$s. %4$s – %6$s., %8$s. %7$s. %9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s. %2$s. – %10$s %8$s. %7$s.</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s.</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s., %2$s – %6$s %4$s., %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s., %2$s – %4$s., %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s., %3$s</string> + <string name="wday_date">%2$s., %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s. %2$s – %8$s. %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s.</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s – %10$s %8$s. %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s – %10$s %8$s. %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s.</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s.</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s., %3$s. %2$s. %4$s – %6$s., %8$s. %7$s. %9$s</string> + <string name="same_month_md1_md2">%3$s.–%8$s. %2$s.</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s.</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s. – %8$s. %7$s. %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s. %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml new file mode 100644 index 000000000000..55ca96829f4e --- /dev/null +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">јануар</string> + <string name="month_long_standalone_february">фебруар</string> + <string name="month_long_standalone_march">март</string> + <string name="month_long_standalone_april">април</string> + <string name="month_long_standalone_may">мај</string> + <string name="month_long_standalone_june">јун</string> + <string name="month_long_standalone_july">јул</string> + <string name="month_long_standalone_august">август</string> + <string name="month_long_standalone_september">септембар</string> + <string name="month_long_standalone_october">октобар</string> + <string name="month_long_standalone_november">новембар</string> + <string name="month_long_standalone_december">децембар</string> + + <string name="month_long_january">јануар</string> + <string name="month_long_february">фебруар</string> + <string name="month_long_march">март</string> + <string name="month_long_april">април</string> + <string name="month_long_may">мај</string> + <string name="month_long_june">јун</string> + <string name="month_long_july">јул</string> + <string name="month_long_august">август</string> + <string name="month_long_september">септембар</string> + <string name="month_long_october">октобар</string> + <string name="month_long_november">новембар</string> + <string name="month_long_december">децембар</string> + + <string name="month_medium_january">јан</string> + <string name="month_medium_february">феб</string> + <string name="month_medium_march">мар</string> + <string name="month_medium_april">апр</string> + <string name="month_medium_may">мај</string> + <string name="month_medium_june">јун</string> + <string name="month_medium_july">јул</string> + <string name="month_medium_august">авг</string> + <string name="month_medium_september">сеп</string> + <string name="month_medium_october">окт</string> + <string name="month_medium_november">нов</string> + <string name="month_medium_december">дец</string> + + <string name="month_shortest_january">ј</string> + <string name="month_shortest_february">ф</string> + <string name="month_shortest_march">м</string> + <string name="month_shortest_april">а</string> + <string name="month_shortest_may">м</string> + <string name="month_shortest_june">ј</string> + <string name="month_shortest_july">ј</string> + <string name="month_shortest_august">а</string> + <string name="month_shortest_september">с</string> + <string name="month_shortest_october">о</string> + <string name="month_shortest_november">н</string> + <string name="month_shortest_december">д</string> + + <string name="day_of_week_long_sunday">недеља</string> + <string name="day_of_week_long_monday">понедељак</string> + <string name="day_of_week_long_tuesday">уторак</string> + <string name="day_of_week_long_wednesday">среда</string> + <string name="day_of_week_long_thursday">четвртак</string> + <string name="day_of_week_long_friday">петак</string> + <string name="day_of_week_long_saturday">субота</string> + + <string name="day_of_week_medium_sunday">нед</string> + <string name="day_of_week_medium_monday">пон</string> + <string name="day_of_week_medium_tuesday">уто</string> + <string name="day_of_week_medium_wednesday">сре</string> + <string name="day_of_week_medium_thursday">чет</string> + <string name="day_of_week_medium_friday">пет</string> + <string name="day_of_week_medium_saturday">суб</string> + + <string name="day_of_week_short_sunday">нед</string> + <string name="day_of_week_short_monday">пон</string> + <string name="day_of_week_short_tuesday">уто</string> + <string name="day_of_week_short_wednesday">сре</string> + <string name="day_of_week_short_thursday">чет</string> + <string name="day_of_week_short_friday">пет</string> + <string name="day_of_week_short_saturday">суб</string> + + <string name="day_of_week_shortest_sunday">н</string> + <string name="day_of_week_shortest_monday">п</string> + <string name="day_of_week_shortest_tuesday">у</string> + <string name="day_of_week_shortest_wednesday">с</string> + <string name="day_of_week_shortest_thursday">ч</string> + <string name="day_of_week_shortest_friday">п</string> + <string name="day_of_week_shortest_saturday">с</string> + + <string name="am">пре подне</string> + <string name="pm">поподне</string> + <string name="yesterday">јуче</string> + <string name="today">данас</string> + <string name="tomorrow">сутра</string> + + <string name="hour_minute_24">%H.%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH.mm</string> + <string name="numeric_date">%-e.%-m.%Y.</string> + <string name="numeric_date_format">d.M.yyyy.</string> + <string name="numeric_date_template">"%s.%s.%s."</string> + <string name="month_day_year">%d. %B %Y.</string> + <string name="time_of_day">%H.%M.%S</string> + <string name="date_and_time">%H.%M.%S %d.%m.%Y.</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d.%m.%Y.</string> + <string name="month_day">%B %-e.</string> + <string name="month">%-B</string> + <string name="month_year">%Y %B</string> + <string name="abbrev_month_day">%b %-e.</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b. %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s - %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s. - %8$s.%7$s.%9$s.</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s. - %6$s, %8$s.%7$s.%9$s.</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s. %4$s. - %10$s %6$s, %8$s. %7$s. %9$s.</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s. - %10$s %8$s.%7$s.%9$s.</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s. - %7$s %8$s.</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s %3$s. - %10$s %7$s %8$s.</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s %3$s. - %10$s %7$s %8$s.</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s. - %10$s %8$s. %7$s %9$s.</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s. - %10$s %8$s. %7$s %9$s.</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s. - %10$s %6$s, %8$s. %7$s %9$s.</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s. - %10$s %6$s, %8$s. %7$s %9$s.</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s. - %6$s, %8$s. %7$s %9$s.</string> + <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s.</string> + <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s.</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s.</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-sv-rSE/donottranslate-cldr.xml b/core/res/res/values-sv-rSE/donottranslate-cldr.xml new file mode 100644 index 000000000000..a6ffc9aa77af --- /dev/null +++ b/core/res/res/values-sv-rSE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januari</string> + <string name="month_long_standalone_february">februari</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">maj</string> + <string name="month_long_standalone_june">juni</string> + <string name="month_long_standalone_july">juli</string> + <string name="month_long_standalone_august">augusti</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">december</string> + + <string name="month_long_january">januari</string> + <string name="month_long_february">februari</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">april</string> + <string name="month_long_may">maj</string> + <string name="month_long_june">juni</string> + <string name="month_long_july">juli</string> + <string name="month_long_august">augusti</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">december</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">maj</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">dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">söndag</string> + <string name="day_of_week_long_monday">måndag</string> + <string name="day_of_week_long_tuesday">tisdag</string> + <string name="day_of_week_long_wednesday">onsdag</string> + <string name="day_of_week_long_thursday">torsdag</string> + <string name="day_of_week_long_friday">fredag</string> + <string name="day_of_week_long_saturday">lördag</string> + + <string name="day_of_week_medium_sunday">sön</string> + <string name="day_of_week_medium_monday">mån</string> + <string name="day_of_week_medium_tuesday">tis</string> + <string name="day_of_week_medium_wednesday">ons</string> + <string name="day_of_week_medium_thursday">tors</string> + <string name="day_of_week_medium_friday">fre</string> + <string name="day_of_week_medium_saturday">lör</string> + + <string name="day_of_week_short_sunday">sön</string> + <string name="day_of_week_short_monday">mån</string> + <string name="day_of_week_short_tuesday">tis</string> + <string name="day_of_week_short_wednesday">ons</string> + <string name="day_of_week_short_thursday">tors</string> + <string name="day_of_week_short_friday">fre</string> + <string name="day_of_week_short_saturday">lör</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">O</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">f.m.</string> + <string name="pm">e.m.</string> + <string name="yesterday">igår</string> + <string name="today">idag</string> + <string name="tomorrow">imorgon</string> + + <string name="hour_minute_24">%-k.%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H.mm</string> + <string name="numeric_date">%Y-%m-%d</string> + <string name="numeric_date_format">yyyy-MM-dd</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%Y %B</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s–%6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml new file mode 100644 index 000000000000..a6ffc9aa77af --- /dev/null +++ b/core/res/res/values-sv/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">januari</string> + <string name="month_long_standalone_february">februari</string> + <string name="month_long_standalone_march">mars</string> + <string name="month_long_standalone_april">april</string> + <string name="month_long_standalone_may">maj</string> + <string name="month_long_standalone_june">juni</string> + <string name="month_long_standalone_july">juli</string> + <string name="month_long_standalone_august">augusti</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">december</string> + + <string name="month_long_january">januari</string> + <string name="month_long_february">februari</string> + <string name="month_long_march">mars</string> + <string name="month_long_april">april</string> + <string name="month_long_may">maj</string> + <string name="month_long_june">juni</string> + <string name="month_long_july">juli</string> + <string name="month_long_august">augusti</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">december</string> + + <string name="month_medium_january">jan</string> + <string name="month_medium_february">feb</string> + <string name="month_medium_march">mar</string> + <string name="month_medium_april">apr</string> + <string name="month_medium_may">maj</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">dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">söndag</string> + <string name="day_of_week_long_monday">måndag</string> + <string name="day_of_week_long_tuesday">tisdag</string> + <string name="day_of_week_long_wednesday">onsdag</string> + <string name="day_of_week_long_thursday">torsdag</string> + <string name="day_of_week_long_friday">fredag</string> + <string name="day_of_week_long_saturday">lördag</string> + + <string name="day_of_week_medium_sunday">sön</string> + <string name="day_of_week_medium_monday">mån</string> + <string name="day_of_week_medium_tuesday">tis</string> + <string name="day_of_week_medium_wednesday">ons</string> + <string name="day_of_week_medium_thursday">tors</string> + <string name="day_of_week_medium_friday">fre</string> + <string name="day_of_week_medium_saturday">lör</string> + + <string name="day_of_week_short_sunday">sön</string> + <string name="day_of_week_short_monday">mån</string> + <string name="day_of_week_short_tuesday">tis</string> + <string name="day_of_week_short_wednesday">ons</string> + <string name="day_of_week_short_thursday">tors</string> + <string name="day_of_week_short_friday">fre</string> + <string name="day_of_week_short_saturday">lör</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">O</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">L</string> + + <string name="am">f.m.</string> + <string name="pm">e.m.</string> + <string name="yesterday">igår</string> + <string name="today">idag</string> + <string name="tomorrow">imorgon</string> + + <string name="hour_minute_24">%-k.%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H.mm</string> + <string name="numeric_date">%Y-%m-%d</string> + <string name="numeric_date_format">yyyy-MM-dd</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%Y %B</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y %b</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s–%6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml new file mode 100644 index 000000000000..b3c76a33abf5 --- /dev/null +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">มกราคม</string> + <string name="month_long_standalone_february">กุมภาพันธ์</string> + <string name="month_long_standalone_march">มีนาคม</string> + <string name="month_long_standalone_april">เมษายน</string> + <string name="month_long_standalone_may">พฤษภาคม</string> + <string name="month_long_standalone_june">มิถุนายน</string> + <string name="month_long_standalone_july">กรกฎาคม</string> + <string name="month_long_standalone_august">สิงหาคม</string> + <string name="month_long_standalone_september">กันยายน</string> + <string name="month_long_standalone_october">ตุลาคม</string> + <string name="month_long_standalone_november">พฤศจิกายน</string> + <string name="month_long_standalone_december">ธันวาคม</string> + + <string name="month_long_january">มกราคม</string> + <string name="month_long_february">กุมภาพันธ์</string> + <string name="month_long_march">มีนาคม</string> + <string name="month_long_april">เมษายน</string> + <string name="month_long_may">พฤษภาคม</string> + <string name="month_long_june">มิถุนายน</string> + <string name="month_long_july">กรกฎาคม</string> + <string name="month_long_august">สิงหาคม</string> + <string name="month_long_september">กันยายน</string> + <string name="month_long_october">ตุลาคม</string> + <string name="month_long_november">พฤศจิกายน</string> + <string name="month_long_december">ธันวาคม</string> + + <string name="month_medium_january">ม.ค.</string> + <string name="month_medium_february">ก.พ.</string> + <string name="month_medium_march">มี.ค.</string> + <string name="month_medium_april">เม.ย.</string> + <string name="month_medium_may">พ.ค.</string> + <string name="month_medium_june">มิ.ย.</string> + <string name="month_medium_july">ก.ค.</string> + <string name="month_medium_august">ส.ค.</string> + <string name="month_medium_september">ก.ย.</string> + <string name="month_medium_october">ต.ค.</string> + <string name="month_medium_november">พ.ย.</string> + <string name="month_medium_december">ธ.ค.</string> + + <string name="month_shortest_january">ม.ค.</string> + <string name="month_shortest_february">ก.พ.</string> + <string name="month_shortest_march">มี.ค.</string> + <string name="month_shortest_april">เม.ย.</string> + <string name="month_shortest_may">พ.ค.</string> + <string name="month_shortest_june">มิ.ย.</string> + <string name="month_shortest_july">ก.ค.</string> + <string name="month_shortest_august">ส.ค.</string> + <string name="month_shortest_september">ก.ย.</string> + <string name="month_shortest_october">ต.ค.</string> + <string name="month_shortest_november">พ.ย.</string> + <string name="month_shortest_december">ธ.ค.</string> + + <string name="day_of_week_long_sunday">วันอาทิตย์</string> + <string name="day_of_week_long_monday">วันจันทร์</string> + <string name="day_of_week_long_tuesday">วันอังคาร</string> + <string name="day_of_week_long_wednesday">วันพุธ</string> + <string name="day_of_week_long_thursday">วันพฤหัสบดี</string> + <string name="day_of_week_long_friday">วันศุกร์</string> + <string name="day_of_week_long_saturday">วันเสาร์</string> + + <string name="day_of_week_medium_sunday">อา.</string> + <string name="day_of_week_medium_monday">จ.</string> + <string name="day_of_week_medium_tuesday">อ.</string> + <string name="day_of_week_medium_wednesday">พ.</string> + <string name="day_of_week_medium_thursday">พฤ.</string> + <string name="day_of_week_medium_friday">ศ.</string> + <string name="day_of_week_medium_saturday">ส.</string> + + <string name="day_of_week_short_sunday">อา.</string> + <string name="day_of_week_short_monday">จ.</string> + <string name="day_of_week_short_tuesday">อ.</string> + <string name="day_of_week_short_wednesday">พ.</string> + <string name="day_of_week_short_thursday">พฤ.</string> + <string name="day_of_week_short_friday">ศ.</string> + <string name="day_of_week_short_saturday">ส.</string> + + <string name="day_of_week_shortest_sunday">อ</string> + <string name="day_of_week_shortest_monday">จ</string> + <string name="day_of_week_shortest_tuesday">อ</string> + <string name="day_of_week_shortest_wednesday">พ</string> + <string name="day_of_week_shortest_thursday">พ</string> + <string name="day_of_week_shortest_friday">ศ</string> + <string name="day_of_week_shortest_saturday">ส</string> + + <string name="am">ก่อนเที่ยง</string> + <string name="pm">หลังเที่ยง</string> + <string name="yesterday">เมื่อวาน</string> + <string name="today">วันนี้</string> + <string name="tomorrow">พรุ่งนี้</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%-e/%-m/%Y</string> + <string name="numeric_date_format">d/M/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%-k:%M:%S</string> + <string name="date_and_time">%-k:%M:%S, %-e %b %Y</string> + <string name="date_time">%2$s, %1$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s – %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s %3$s/%2$s/%4$s - %10$s, %6$s %8$s/%7$s/%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s, %3$s/%2$s - %10$s, %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s, %3$s/%2$s - %10$s, %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s, %3$s/%2$s/%4$s - %10$s, %8$s/%7$s/%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s, %1$s %2$s - %6$s, %4$s %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%3$s, %2$s - %6$s, %5$s</string> + <string name="time_wday_date">%1$s, %2$s %3$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s, %3$s %2$s - %10$s, %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s, %3$s %2$s - %10$s, %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s %3$s %2$s - %10$s, %6$s %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s, %1$s %3$s %2$s - %10$s, %6$s %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s %3$s %2$s %4$s - %10$s, %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s, %1$s %3$s %2$s %4$s - %10$s, %6$s %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s – %8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-tr-rTR/donottranslate-cldr.xml b/core/res/res/values-tr-rTR/donottranslate-cldr.xml new file mode 100644 index 000000000000..d61230593437 --- /dev/null +++ b/core/res/res/values-tr-rTR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Ocak</string> + <string name="month_long_standalone_february">Şubat</string> + <string name="month_long_standalone_march">Mart</string> + <string name="month_long_standalone_april">Nisan</string> + <string name="month_long_standalone_may">Mayıs</string> + <string name="month_long_standalone_june">Haziran</string> + <string name="month_long_standalone_july">Temmuz</string> + <string name="month_long_standalone_august">Ağustos</string> + <string name="month_long_standalone_september">Eylül</string> + <string name="month_long_standalone_october">Ekim</string> + <string name="month_long_standalone_november">Kasım</string> + <string name="month_long_standalone_december">Aralık</string> + + <string name="month_long_january">Ocak</string> + <string name="month_long_february">Şubat</string> + <string name="month_long_march">Mart</string> + <string name="month_long_april">Nisan</string> + <string name="month_long_may">Mayıs</string> + <string name="month_long_june">Haziran</string> + <string name="month_long_july">Temmuz</string> + <string name="month_long_august">Ağustos</string> + <string name="month_long_september">Eylül</string> + <string name="month_long_october">Ekim</string> + <string name="month_long_november">Kasım</string> + <string name="month_long_december">Aralık</string> + + <string name="month_medium_january">Oca</string> + <string name="month_medium_february">Şub</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Nis</string> + <string name="month_medium_may">May</string> + <string name="month_medium_june">Haz</string> + <string name="month_medium_july">Tem</string> + <string name="month_medium_august">Ağu</string> + <string name="month_medium_september">Eyl</string> + <string name="month_medium_october">Eki</string> + <string name="month_medium_november">Kas</string> + <string name="month_medium_december">Ara</string> + + <string name="month_shortest_january">O</string> + <string name="month_shortest_february">Ş</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">N</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">H</string> + <string name="month_shortest_july">T</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">E</string> + <string name="month_shortest_october">E</string> + <string name="month_shortest_november">K</string> + <string name="month_shortest_december">A</string> + + <string name="day_of_week_long_sunday">Pazar</string> + <string name="day_of_week_long_monday">Pazartesi</string> + <string name="day_of_week_long_tuesday">Salı</string> + <string name="day_of_week_long_wednesday">Çarşamba</string> + <string name="day_of_week_long_thursday">Perşembe</string> + <string name="day_of_week_long_friday">Cuma</string> + <string name="day_of_week_long_saturday">Cumartesi</string> + + <string name="day_of_week_medium_sunday">Paz</string> + <string name="day_of_week_medium_monday">Pzt</string> + <string name="day_of_week_medium_tuesday">Sal</string> + <string name="day_of_week_medium_wednesday">Çar</string> + <string name="day_of_week_medium_thursday">Per</string> + <string name="day_of_week_medium_friday">Cum</string> + <string name="day_of_week_medium_saturday">Cmt</string> + + <string name="day_of_week_short_sunday">Paz</string> + <string name="day_of_week_short_monday">Pzt</string> + <string name="day_of_week_short_tuesday">Sal</string> + <string name="day_of_week_short_wednesday">Çar</string> + <string name="day_of_week_short_thursday">Per</string> + <string name="day_of_week_short_friday">Cum</string> + <string name="day_of_week_short_saturday">Cmt</string> + + <string name="day_of_week_shortest_sunday">P</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">S</string> + <string name="day_of_week_shortest_wednesday">Ç</string> + <string name="day_of_week_shortest_thursday">P</string> + <string name="day_of_week_shortest_friday">C</string> + <string name="day_of_week_shortest_saturday">C</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Dün</string> + <string name="today">Bugün</string> + <string name="tomorrow">Yarın</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d %m %Y</string> + <string name="numeric_date_format">dd MM yyyy</string> + <string name="numeric_date_template">"%s %s %s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d %b %Y</string> + <string name="month_day">%d %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%d %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s %1$s - %8$s.%7$s %6$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s %1$s - %6$s %5$s %4$s</string> + <string name="wday1_date1_wday2_date2">%2$s %1$s - %5$s %4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s %2$s</string> + <string name="wday_date">%3$s %2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml new file mode 100644 index 000000000000..d61230593437 --- /dev/null +++ b/core/res/res/values-tr/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Ocak</string> + <string name="month_long_standalone_february">Şubat</string> + <string name="month_long_standalone_march">Mart</string> + <string name="month_long_standalone_april">Nisan</string> + <string name="month_long_standalone_may">Mayıs</string> + <string name="month_long_standalone_june">Haziran</string> + <string name="month_long_standalone_july">Temmuz</string> + <string name="month_long_standalone_august">Ağustos</string> + <string name="month_long_standalone_september">Eylül</string> + <string name="month_long_standalone_october">Ekim</string> + <string name="month_long_standalone_november">Kasım</string> + <string name="month_long_standalone_december">Aralık</string> + + <string name="month_long_january">Ocak</string> + <string name="month_long_february">Şubat</string> + <string name="month_long_march">Mart</string> + <string name="month_long_april">Nisan</string> + <string name="month_long_may">Mayıs</string> + <string name="month_long_june">Haziran</string> + <string name="month_long_july">Temmuz</string> + <string name="month_long_august">Ağustos</string> + <string name="month_long_september">Eylül</string> + <string name="month_long_october">Ekim</string> + <string name="month_long_november">Kasım</string> + <string name="month_long_december">Aralık</string> + + <string name="month_medium_january">Oca</string> + <string name="month_medium_february">Şub</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Nis</string> + <string name="month_medium_may">May</string> + <string name="month_medium_june">Haz</string> + <string name="month_medium_july">Tem</string> + <string name="month_medium_august">Ağu</string> + <string name="month_medium_september">Eyl</string> + <string name="month_medium_october">Eki</string> + <string name="month_medium_november">Kas</string> + <string name="month_medium_december">Ara</string> + + <string name="month_shortest_january">O</string> + <string name="month_shortest_february">Ş</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">N</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">H</string> + <string name="month_shortest_july">T</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">E</string> + <string name="month_shortest_october">E</string> + <string name="month_shortest_november">K</string> + <string name="month_shortest_december">A</string> + + <string name="day_of_week_long_sunday">Pazar</string> + <string name="day_of_week_long_monday">Pazartesi</string> + <string name="day_of_week_long_tuesday">Salı</string> + <string name="day_of_week_long_wednesday">Çarşamba</string> + <string name="day_of_week_long_thursday">Perşembe</string> + <string name="day_of_week_long_friday">Cuma</string> + <string name="day_of_week_long_saturday">Cumartesi</string> + + <string name="day_of_week_medium_sunday">Paz</string> + <string name="day_of_week_medium_monday">Pzt</string> + <string name="day_of_week_medium_tuesday">Sal</string> + <string name="day_of_week_medium_wednesday">Çar</string> + <string name="day_of_week_medium_thursday">Per</string> + <string name="day_of_week_medium_friday">Cum</string> + <string name="day_of_week_medium_saturday">Cmt</string> + + <string name="day_of_week_short_sunday">Paz</string> + <string name="day_of_week_short_monday">Pzt</string> + <string name="day_of_week_short_tuesday">Sal</string> + <string name="day_of_week_short_wednesday">Çar</string> + <string name="day_of_week_short_thursday">Per</string> + <string name="day_of_week_short_friday">Cum</string> + <string name="day_of_week_short_saturday">Cmt</string> + + <string name="day_of_week_shortest_sunday">P</string> + <string name="day_of_week_shortest_monday">P</string> + <string name="day_of_week_shortest_tuesday">S</string> + <string name="day_of_week_shortest_wednesday">Ç</string> + <string name="day_of_week_shortest_thursday">P</string> + <string name="day_of_week_shortest_friday">C</string> + <string name="day_of_week_shortest_saturday">C</string> + + <string name="am">AM</string> + <string name="pm">PM</string> + <string name="yesterday">Dün</string> + <string name="today">Bugün</string> + <string name="tomorrow">Yarın</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%d %m %Y</string> + <string name="numeric_date_format">dd MM yyyy</string> + <string name="numeric_date_template">"%s %s %s"</string> + <string name="month_day_year">%d %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %d %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%d %b %Y</string> + <string name="month_day">%d %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%d %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s %1$s - %8$s.%7$s %6$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s %1$s - %6$s %5$s %4$s</string> + <string name="wday1_date1_wday2_date2">%2$s %1$s - %5$s %4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s %2$s</string> + <string name="wday_date">%3$s %2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s</string> + <string name="same_month_md1_md2">%3$s-%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml new file mode 100644 index 000000000000..c51f8d1eae06 --- /dev/null +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">Січень</string> + <string name="month_long_standalone_february">Лютий</string> + <string name="month_long_standalone_march">Березень</string> + <string name="month_long_standalone_april">Квітень</string> + <string name="month_long_standalone_may">Травень</string> + <string name="month_long_standalone_june">Червень</string> + <string name="month_long_standalone_july">Липень</string> + <string name="month_long_standalone_august">Серпень</string> + <string name="month_long_standalone_september">Вересень</string> + <string name="month_long_standalone_october">Жовтень</string> + <string name="month_long_standalone_november">Листопад</string> + <string name="month_long_standalone_december">Грудень</string> + + <string name="month_long_january">січня</string> + <string name="month_long_february">лютого</string> + <string name="month_long_march">березня</string> + <string name="month_long_april">квітня</string> + <string name="month_long_may">травня</string> + <string name="month_long_june">червня</string> + <string name="month_long_july">липня</string> + <string name="month_long_august">серпня</string> + <string name="month_long_september">вересня</string> + <string name="month_long_october">жовтня</string> + <string name="month_long_november">листопада</string> + <string name="month_long_december">грудня</string> + + <string name="month_medium_january">січ.</string> + <string name="month_medium_february">лют.</string> + <string name="month_medium_march">бер.</string> + <string name="month_medium_april">квіт.</string> + <string name="month_medium_may">трав.</string> + <string name="month_medium_june">черв.</string> + <string name="month_medium_july">лип.</string> + <string name="month_medium_august">серп.</string> + <string name="month_medium_september">вер.</string> + <string name="month_medium_october">жовт.</string> + <string name="month_medium_november">лист.</string> + <string name="month_medium_december">груд.</string> + + <string name="month_shortest_january">С</string> + <string name="month_shortest_february">Л</string> + <string name="month_shortest_march">Б</string> + <string name="month_shortest_april">К</string> + <string name="month_shortest_may">Т</string> + <string name="month_shortest_june">Ч</string> + <string name="month_shortest_july">Л</string> + <string name="month_shortest_august">С</string> + <string name="month_shortest_september">В</string> + <string name="month_shortest_october">Ж</string> + <string name="month_shortest_november">Л</string> + <string name="month_shortest_december">Г</string> + + <string name="day_of_week_long_sunday">Неділя</string> + <string name="day_of_week_long_monday">Понеділок</string> + <string name="day_of_week_long_tuesday">Вівторок</string> + <string name="day_of_week_long_wednesday">Середа</string> + <string name="day_of_week_long_thursday">Четвер</string> + <string name="day_of_week_long_friday">Пʼятниця</string> + <string name="day_of_week_long_saturday">Субота</string> + + <string name="day_of_week_medium_sunday">Нд</string> + <string name="day_of_week_medium_monday">Пн</string> + <string name="day_of_week_medium_tuesday">Вт</string> + <string name="day_of_week_medium_wednesday">Ср</string> + <string name="day_of_week_medium_thursday">Чт</string> + <string name="day_of_week_medium_friday">Пт</string> + <string name="day_of_week_medium_saturday">Сб</string> + + <string name="day_of_week_short_sunday">Нд</string> + <string name="day_of_week_short_monday">Пн</string> + <string name="day_of_week_short_tuesday">Вт</string> + <string name="day_of_week_short_wednesday">Ср</string> + <string name="day_of_week_short_thursday">Чт</string> + <string name="day_of_week_short_friday">Пт</string> + <string name="day_of_week_short_saturday">Сб</string> + + <string name="day_of_week_shortest_sunday">Н</string> + <string name="day_of_week_shortest_monday">П</string> + <string name="day_of_week_shortest_tuesday">В</string> + <string name="day_of_week_shortest_wednesday">С</string> + <string name="day_of_week_shortest_thursday">Ч</string> + <string name="day_of_week_shortest_friday">П</string> + <string name="day_of_week_shortest_saturday">С</string> + + <string name="am">дп</string> + <string name="pm">пп</string> + <string name="yesterday">Вчора</string> + <string name="today">Сьогодні</string> + <string name="tomorrow">Завтра</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d.%m.%Y</string> + <string name="numeric_date_format">dd.MM.yyyy</string> + <string name="numeric_date_template">"%s.%s.%s"</string> + <string name="month_day_year">%-e %B %Y р.</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%H:%M:%S %-e %b %Y</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%-e %b %Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%-b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%3$s.%2$s – %8$s.%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s – %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s – %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s – %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s.%2$s.%4$s – %10$s %6$s, %8$s.%7$s.%9$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s – %10$s %8$s.%7$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s – %10$s %6$s, %8$s.%7$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s – %10$s %8$s.%7$s.%9$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s – %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s – %6$s, %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">%3$s–%8$s %2$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s – %6$s, %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> + <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s – %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml new file mode 100644 index 000000000000..72ff8b68999c --- /dev/null +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">tháng một</string> + <string name="month_long_standalone_february">tháng hai</string> + <string name="month_long_standalone_march">tháng ba</string> + <string name="month_long_standalone_april">tháng tư</string> + <string name="month_long_standalone_may">tháng năm</string> + <string name="month_long_standalone_june">tháng sáu</string> + <string name="month_long_standalone_july">tháng bảy</string> + <string name="month_long_standalone_august">tháng tám</string> + <string name="month_long_standalone_september">tháng chín</string> + <string name="month_long_standalone_october">tháng mười</string> + <string name="month_long_standalone_november">tháng mười một</string> + <string name="month_long_standalone_december">tháng mười hai</string> + + <string name="month_long_january">tháng một</string> + <string name="month_long_february">tháng hai</string> + <string name="month_long_march">tháng ba</string> + <string name="month_long_april">tháng tư</string> + <string name="month_long_may">tháng năm</string> + <string name="month_long_june">tháng sáu</string> + <string name="month_long_july">tháng bảy</string> + <string name="month_long_august">tháng tám</string> + <string name="month_long_september">tháng chín</string> + <string name="month_long_october">tháng mười</string> + <string name="month_long_november">tháng mười một</string> + <string name="month_long_december">tháng mười hai</string> + + <string name="month_medium_january">thg 1</string> + <string name="month_medium_february">thg 2</string> + <string name="month_medium_march">thg 3</string> + <string name="month_medium_april">thg 4</string> + <string name="month_medium_may">thg 5</string> + <string name="month_medium_june">thg 6</string> + <string name="month_medium_july">thg 7</string> + <string name="month_medium_august">thg 8</string> + <string name="month_medium_september">thg 9</string> + <string name="month_medium_october">thg 10</string> + <string name="month_medium_november">thg 11</string> + <string name="month_medium_december">thg 12</string> + + <string name="month_shortest_january">1</string> + <string name="month_shortest_february">2</string> + <string name="month_shortest_march">3</string> + <string name="month_shortest_april">4</string> + <string name="month_shortest_may">5</string> + <string name="month_shortest_june">6</string> + <string name="month_shortest_july">7</string> + <string name="month_shortest_august">8</string> + <string name="month_shortest_september">9</string> + <string name="month_shortest_october">10</string> + <string name="month_shortest_november">11</string> + <string name="month_shortest_december">12</string> + + <string name="day_of_week_long_sunday">Chủ nhật</string> + <string name="day_of_week_long_monday">Thứ hai</string> + <string name="day_of_week_long_tuesday">Thứ ba</string> + <string name="day_of_week_long_wednesday">Thứ tư</string> + <string name="day_of_week_long_thursday">Thứ năm</string> + <string name="day_of_week_long_friday">Thứ sáu</string> + <string name="day_of_week_long_saturday">Thứ bảy</string> + + <string name="day_of_week_medium_sunday">CN</string> + <string name="day_of_week_medium_monday">Th 2</string> + <string name="day_of_week_medium_tuesday">Th 3</string> + <string name="day_of_week_medium_wednesday">Th 4</string> + <string name="day_of_week_medium_thursday">Th 5</string> + <string name="day_of_week_medium_friday">Th 6</string> + <string name="day_of_week_medium_saturday">Th 7</string> + + <string name="day_of_week_short_sunday">CN</string> + <string name="day_of_week_short_monday">Th 2</string> + <string name="day_of_week_short_tuesday">Th 3</string> + <string name="day_of_week_short_wednesday">Th 4</string> + <string name="day_of_week_short_thursday">Th 5</string> + <string name="day_of_week_short_friday">Th 6</string> + <string name="day_of_week_short_saturday">Th 7</string> + + <string name="day_of_week_shortest_sunday">1</string> + <string name="day_of_week_shortest_monday">2</string> + <string name="day_of_week_shortest_tuesday">3</string> + <string name="day_of_week_shortest_wednesday">4</string> + <string name="day_of_week_shortest_thursday">5</string> + <string name="day_of_week_shortest_friday">6</string> + <string name="day_of_week_shortest_saturday">7</string> + + <string name="am">SA</string> + <string name="pm">CH</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%-l:%M %p</string> + <string name="hour_minute_cap_ampm">%-l:%M %^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%d/%m/%Y</string> + <string name="numeric_date_format">dd/MM/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">Ngày %d tháng %-m năm %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%d-%m-%Y %H:%M:%S</string> + <string name="date_time">%1$s %2$s</string> + <string name="time_date">%3$s %1$s</string> + <string name="abbrev_month_day_year">%d-%m-%Y</string> + <string name="month_day">%-e %B</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%-e %b</string> + <string name="abbrev_month">%b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s - %2$s</string> + <string name="date1_date2">%2$s - %5$s</string> + <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string> + <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s-%2$s-%4$s %5$s - %6$s, %8$s-%7$s-%9$s %10$s</string> + <string name="numeric_md1_time1_md2_time2">%3$s-%2$s %5$s - %8$s-%7$s %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %3$s-%2$s %5$s - %6$s, %8$s-%7$s %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%3$s/%2$s/%4$s %5$s - %8$s/%7$s/%9$s %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s %2$s %3$s - %4$s %5$s %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="date1_time1_date2_time2">%2$s %3$s - %5$s %6$s</string> + <string name="time_wday_date">%2$s %3$s %1$s</string> + <string name="wday_date">%2$s %3$s</string> + <string name="time_wday">%2$s %1$s</string> + <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_md1_time1_md2_time2">%3$s %2$s %5$s - %8$s %7$s %10$s</string> + <string name="same_month_md1_time1_md2_time2">%3$s %2$s %5$s - %8$s %7$s %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string> + <string name="same_month_md1_md2">Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string> + <string name="same_year_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s năm %9$s</string> + <string name="same_month_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng M năm %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml new file mode 100644 index 000000000000..6d52d7046f99 --- /dev/null +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">一月</string> + <string name="month_long_standalone_february">二月</string> + <string name="month_long_standalone_march">三月</string> + <string name="month_long_standalone_april">四月</string> + <string name="month_long_standalone_may">五月</string> + <string name="month_long_standalone_june">六月</string> + <string name="month_long_standalone_july">七月</string> + <string name="month_long_standalone_august">八月</string> + <string name="month_long_standalone_september">九月</string> + <string name="month_long_standalone_october">十月</string> + <string name="month_long_standalone_november">十一月</string> + <string name="month_long_standalone_december">十二月</string> + + <string name="month_long_january">1月</string> + <string name="month_long_february">2月</string> + <string name="month_long_march">3月</string> + <string name="month_long_april">4月</string> + <string name="month_long_may">5月</string> + <string name="month_long_june">6月</string> + <string name="month_long_july">7月</string> + <string name="month_long_august">8月</string> + <string name="month_long_september">9月</string> + <string name="month_long_october">10月</string> + <string name="month_long_november">11月</string> + <string name="month_long_december">12月</string> + + <string name="month_medium_january">1月</string> + <string name="month_medium_february">2月</string> + <string name="month_medium_march">3月</string> + <string name="month_medium_april">4月</string> + <string name="month_medium_may">5月</string> + <string name="month_medium_june">6月</string> + <string name="month_medium_july">7月</string> + <string name="month_medium_august">8月</string> + <string name="month_medium_september">9月</string> + <string name="month_medium_october">10月</string> + <string name="month_medium_november">11月</string> + <string name="month_medium_december">12月</string> + + <string name="month_shortest_january">1月</string> + <string name="month_shortest_february">2月</string> + <string name="month_shortest_march">3月</string> + <string name="month_shortest_april">4月</string> + <string name="month_shortest_may">5月</string> + <string name="month_shortest_june">6月</string> + <string name="month_shortest_july">7月</string> + <string name="month_shortest_august">8月</string> + <string name="month_shortest_september">9月</string> + <string name="month_shortest_october">10月</string> + <string name="month_shortest_november">11月</string> + <string name="month_shortest_december">12月</string> + + <string name="day_of_week_long_sunday">星期日</string> + <string name="day_of_week_long_monday">星期一</string> + <string name="day_of_week_long_tuesday">星期二</string> + <string name="day_of_week_long_wednesday">星期三</string> + <string name="day_of_week_long_thursday">星期四</string> + <string name="day_of_week_long_friday">星期五</string> + <string name="day_of_week_long_saturday">星期六</string> + + <string name="day_of_week_medium_sunday">周日</string> + <string name="day_of_week_medium_monday">周一</string> + <string name="day_of_week_medium_tuesday">周二</string> + <string name="day_of_week_medium_wednesday">周三</string> + <string name="day_of_week_medium_thursday">周四</string> + <string name="day_of_week_medium_friday">周五</string> + <string name="day_of_week_medium_saturday">周六</string> + + <string name="day_of_week_short_sunday">周日</string> + <string name="day_of_week_short_monday">周一</string> + <string name="day_of_week_short_tuesday">周二</string> + <string name="day_of_week_short_wednesday">周三</string> + <string name="day_of_week_short_thursday">周四</string> + <string name="day_of_week_short_friday">周五</string> + <string name="day_of_week_short_saturday">周六</string> + + <string name="day_of_week_shortest_sunday">日</string> + <string name="day_of_week_shortest_monday">一</string> + <string name="day_of_week_shortest_tuesday">二</string> + <string name="day_of_week_shortest_wednesday">三</string> + <string name="day_of_week_shortest_thursday">四</string> + <string name="day_of_week_shortest_friday">五</string> + <string name="day_of_week_shortest_saturday">六</string> + + <string name="am">上午</string> + <string name="pm">下午</string> + <string name="yesterday">昨天</string> + <string name="today">今天</string> + <string name="tomorrow">明天</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%p%-l:%M</string> + <string name="hour_minute_cap_ampm">%p%-l:%M</string> + <string name="twelve_hour_time_format">ah:mm</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y-%-m-%-e</string> + <string name="numeric_date_format">yyyy-M-d</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%Y年%-m月%-e日</string> + <string name="time_of_day">%p%I:%M:%S</string> + <string name="date_and_time">%p%I:%M:%S %Y-%-m-%-e</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y-%-m-%-e</string> + <string name="month_day">%B%-e日</string> + <string name="month">%-B</string> + <string name="month_year">%Y年%B</string> + <string name="abbrev_month_day">%b%-e日</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y年%b</string> + <string name="time1_time2">%1$s–%2$s</string> + <string name="date1_date2">%2$s–%5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s至%7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s-%3$s%1$s至%7$s-%8$s%6$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s至%9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s-%2$s-%3$s%1$s至%9$s-%7$s-%8$s%6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s月%3$s日,%1$s–%10$s %9$s年%7$s月%8$s日,%6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s-%3$s–%10$s %7$s-%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s-%3$s%1$s–%10$s %7$s-%8$s%6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s–%10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s%1$s–%6$s %5$s%4$s</string> + <string name="wday1_date1_wday2_date2">%2$s%1$s–%5$s%4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s–%6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s%2$s</string> + <string name="wday_date">%3$s%2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s%3$s日–%7$s%8$s日</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s%3$s日%1$s–%7$s%8$s日%6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s%3$s日–%10$s %7$s%8$s日</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s%3$s日–%10$s %7$s%8$s日</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s年%2$s%3$s日%1$s–%9$s年%7$s%8$s日%6$s</string> + <string name="same_month_md1_md2">%2$s%3$s日至%8$s日</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s%3$s日%1$s–%7$s%8$s日%6$s</string> + <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s%8$s日</string> + <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s%8$s日%6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 54b0b97d9691..3be8aa09a5fe 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -166,14 +166,10 @@ <string name="permdesc_changeConfiguration">"允许应用程序更改当前配置,例如语言设置或整体的字体大小。"</string> <string name="permlab_restartPackages">"重新启动其他应用程序"</string> <string name="permdesc_restartPackages">"允许应用程序强制重新启动其他应用程序。"</string> - <string name="permlab_setProcessForeground">"防止停止"</string> - <string name="permdesc_setProcessForeground">"允许应用程序在前台运行任何进程,因此该进程不能被终止。普通应用程序从不需要使用此权限。"</string> <string name="permlab_forceBack">"强制应用程序关闭"</string> <string name="permdesc_forceBack">"允许应用程序强制前台的任何活动关闭和重新开始。普通应用程序从不需要使用此权限。"</string> <string name="permlab_dump">"检索系统内部状态"</string> <string name="permdesc_dump">"允许应用程序检索系统的内部状态。恶意应用程序可能会借此检索通常它们本不需要的各种私有和安全信息。"</string> - <string name="permlab_addSystemService">"发布低级服务"</string> - <string name="permdesc_addSystemService">"允许应用程序发布自己的低级系统服务。恶意应用程序可能会借此攻击系统,以及盗取或破坏系统中的任何数据。"</string> <string name="permlab_runSetActivityWatcher">"监视和控制所有应用程序启动"</string> <string name="permdesc_runSetActivityWatcher">"允许应用程序监视和控制系统启动活动的方式。恶意应用程序可能会借此彻底损坏系统。此权限仅在开发时才需要,普通的手机应用不需要。"</string> <string name="permlab_broadcastPackageRemoved">"发送包删除的广播"</string> @@ -186,8 +182,6 @@ <string name="permdesc_setProcessLimit">"允许应用程序控制运行的进程数上限。普通应用程序从不需要使用此权限。"</string> <string name="permlab_setAlwaysFinish">"关闭所有后台应用程序"</string> <string name="permdesc_setAlwaysFinish">"允许应用程序控制活动是否始终是一转至后台就完成。普通应用程序从不需要使用此权限。"</string> - <string name="permlab_fotaUpdate">"自动安装系统更新"</string> - <string name="permdesc_fotaUpdate">"允许应用程序接收有关未决系统更新的通知并安装这些更新。恶意应用程序可能会借此通过未经授权的更新破坏系统,通常情况下,它们会干扰更新过程。"</string> <string name="permlab_batteryStats">"修改电池统计信息"</string> <string name="permdesc_batteryStats">"允许修改收集的电池统计信息。普通应用程序不能使用此权限。"</string> <string name="permlab_internalSystemWindow">"显示未授权的窗口"</string> @@ -424,9 +418,6 @@ <string name="lockscreen_glogin_password_hint">"密码"</string> <string name="lockscreen_glogin_submit_button">"登录"</string> <string name="lockscreen_glogin_invalid_input">"用户名或密码无效。"</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="AMPM">%P</xliff:g><xliff:g id="HOUR">%-l</xliff:g>点"</string> <string name="hour_cap_ampm">"<xliff:g id="AMPM">%p</xliff:g><xliff:g id="HOUR">%-l</xliff:g>点"</string> <string name="status_bar_clear_all_button">"清除通知"</string> @@ -458,9 +449,6 @@ <string name="menu_enter_shortcut_label">"Enter 键"</string> <string name="menu_delete_shortcut_label">"删除"</string> <string name="search_go">"搜索"</string> - <string name="today">"今天"</string> - <string name="yesterday">"昨天"</string> - <string name="tomorrow">"明天"</string> <string name="oneMonthDurationPast">"1 个月前"</string> <string name="beforeOneMonthDurationPast">"1 个月前"</string> <plurals name="num_seconds_ago"> @@ -542,13 +530,6 @@ <string name="weeks">"周"</string> <string name="year">"年"</string> <string name="years">"年"</string> - <string name="sunday">"周日"</string> - <string name="monday">"周一"</string> - <string name="tuesday">"周二"</string> - <string name="wednesday">"周三"</string> - <string name="thursday">"周四"</string> - <string name="friday">"周五"</string> - <string name="saturday">"周六"</string> <string name="every_weekday">"每个工作日(周一到周五)"</string> <string name="daily">"每天"</string> <string name="weekly">"每周的<xliff:g id="DAY">%s</xliff:g>"</string> @@ -558,137 +539,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"抱歉,该视频不适合在此设备上播放。"</string> <string name="VideoView_error_text_unknown">"很抱歉,此视频不能播放。"</string> <string name="VideoView_error_button">"确定"</string> - <string name="am">"上午"</string> - <string name="pm">"下午"</string> - <string name="numeric_date">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%m</xliff:g> 月 <xliff:g id="DAY">%d</xliff:g> 日"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> 至 <xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g><xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="DATE2">%5$s</xliff:g><xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g> <xliff:g id="TIME1">%3$s</xliff:g> 至 <xliff:g id="DATE2">%5$s</xliff:g> <xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g>至 <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> 至 <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="DATE">%3$s</xliff:g><xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g> <xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMM</xliff:g>' 月 '<xliff:g id="DAY">d</xliff:g>' 日'"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="DAY">d</xliff:g>' 月 '<xliff:g id="MONTH">MMM</xliff:g>' 日'"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"中午"</string> <string name="Noon">"中午"</string> <string name="midnight">"午夜"</string> <string name="Midnight">"午夜"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string> - <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> 月"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日 <xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 – <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至<xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日至 <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g>至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> 月 <xliff:g id="DAY1_0">%3$s</xliff:g> 日<xliff:g id="WEEKDAY1">%1$s</xliff:g> <xliff:g id="TIME1">%5$s</xliff:g> 至 <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> 月 <xliff:g id="DAY2_1">%8$s</xliff:g> 日<xliff:g id="WEEKDAY2">%6$s</xliff:g> <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%b</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string> - <string name="abbrev_month_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%b</xliff:g> 月"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> 月 <xliff:g id="DAY">%-d</xliff:g> 日"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"周日"</string> - <string name="day_of_week_long_monday">"周一"</string> - <string name="day_of_week_long_tuesday">"周二"</string> - <string name="day_of_week_long_wednesday">"周三"</string> - <string name="day_of_week_long_thursday">"周四"</string> - <string name="day_of_week_long_friday">"周五"</string> - <string name="day_of_week_long_saturday">"周六"</string> - <string name="day_of_week_medium_sunday">"周日"</string> - <string name="day_of_week_medium_monday">"周一"</string> - <string name="day_of_week_medium_tuesday">"周二"</string> - <string name="day_of_week_medium_wednesday">"周三"</string> - <string name="day_of_week_medium_thursday">"周四"</string> - <string name="day_of_week_medium_friday">"周五"</string> - <string name="day_of_week_medium_saturday">"周六"</string> - <string name="day_of_week_short_sunday">"周日"</string> - <string name="day_of_week_short_monday">"周一"</string> - <string name="day_of_week_short_tuesday">"周二"</string> - <string name="day_of_week_short_wednesday">"周三"</string> - <string name="day_of_week_short_thursday">"周四"</string> - <string name="day_of_week_short_friday">"周五"</string> - <string name="day_of_week_short_saturday">"周六"</string> - <string name="day_of_week_shorter_sunday">"周日"</string> - <string name="day_of_week_shorter_monday">"周一"</string> - <string name="day_of_week_shorter_tuesday">"周二"</string> - <string name="day_of_week_shorter_wednesday">"周三"</string> - <string name="day_of_week_shorter_thursday">"周四"</string> - <string name="day_of_week_shorter_friday">"周五"</string> - <string name="day_of_week_shorter_saturday">"周六"</string> - <string name="day_of_week_shortest_sunday">"周日"</string> - <string name="day_of_week_shortest_monday">"周一"</string> - <string name="day_of_week_shortest_tuesday">"周二"</string> - <string name="day_of_week_shortest_wednesday">"周三"</string> - <string name="day_of_week_shortest_thursday">"周四"</string> - <string name="day_of_week_shortest_friday">"周五"</string> - <string name="day_of_week_shortest_saturday">"周六"</string> - <string name="month_long_january">"1 月"</string> - <string name="month_long_february">"2 月"</string> - <string name="month_long_march">"3 月"</string> - <string name="month_long_april">"4 月"</string> - <string name="month_long_may">"5 月"</string> - <string name="month_long_june">"6 月"</string> - <string name="month_long_july">"7 月"</string> - <string name="month_long_august">"8 月"</string> - <string name="month_long_september">"9 月"</string> - <string name="month_long_october">"10 月"</string> - <string name="month_long_november">"11 月"</string> - <string name="month_long_december">"12 月"</string> - <string name="month_medium_january">"1 月"</string> - <string name="month_medium_february">"2 月"</string> - <string name="month_medium_march">"3 月"</string> - <string name="month_medium_april">"4 月"</string> - <string name="month_medium_may">"5 月"</string> - <string name="month_medium_june">"6 月"</string> - <string name="month_medium_july">"7 月"</string> - <string name="month_medium_august">"8 月"</string> - <string name="month_medium_september">"9 月"</string> - <string name="month_medium_october">"10 月"</string> - <string name="month_medium_november">"11 月"</string> - <string name="month_medium_december">"12 月"</string> - <string name="month_shortest_january">"1 月"</string> - <string name="month_shortest_february">"2 月"</string> - <string name="month_shortest_march">"3 月"</string> - <string name="month_shortest_april">"4 月"</string> - <string name="month_shortest_may">"5 月"</string> - <string name="month_shortest_june">"6 月"</string> - <string name="month_shortest_july">"7 月"</string> - <string name="month_shortest_august">"8 月"</string> - <string name="month_shortest_september">"9 月"</string> - <string name="month_shortest_october">"10 月"</string> - <string name="month_shortest_november">"11 月"</string> - <string name="month_shortest_december">"12 月"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"全选"</string> diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml new file mode 100644 index 000000000000..6d52d7046f99 --- /dev/null +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">一月</string> + <string name="month_long_standalone_february">二月</string> + <string name="month_long_standalone_march">三月</string> + <string name="month_long_standalone_april">四月</string> + <string name="month_long_standalone_may">五月</string> + <string name="month_long_standalone_june">六月</string> + <string name="month_long_standalone_july">七月</string> + <string name="month_long_standalone_august">八月</string> + <string name="month_long_standalone_september">九月</string> + <string name="month_long_standalone_october">十月</string> + <string name="month_long_standalone_november">十一月</string> + <string name="month_long_standalone_december">十二月</string> + + <string name="month_long_january">1月</string> + <string name="month_long_february">2月</string> + <string name="month_long_march">3月</string> + <string name="month_long_april">4月</string> + <string name="month_long_may">5月</string> + <string name="month_long_june">6月</string> + <string name="month_long_july">7月</string> + <string name="month_long_august">8月</string> + <string name="month_long_september">9月</string> + <string name="month_long_october">10月</string> + <string name="month_long_november">11月</string> + <string name="month_long_december">12月</string> + + <string name="month_medium_january">1月</string> + <string name="month_medium_february">2月</string> + <string name="month_medium_march">3月</string> + <string name="month_medium_april">4月</string> + <string name="month_medium_may">5月</string> + <string name="month_medium_june">6月</string> + <string name="month_medium_july">7月</string> + <string name="month_medium_august">8月</string> + <string name="month_medium_september">9月</string> + <string name="month_medium_october">10月</string> + <string name="month_medium_november">11月</string> + <string name="month_medium_december">12月</string> + + <string name="month_shortest_january">1月</string> + <string name="month_shortest_february">2月</string> + <string name="month_shortest_march">3月</string> + <string name="month_shortest_april">4月</string> + <string name="month_shortest_may">5月</string> + <string name="month_shortest_june">6月</string> + <string name="month_shortest_july">7月</string> + <string name="month_shortest_august">8月</string> + <string name="month_shortest_september">9月</string> + <string name="month_shortest_october">10月</string> + <string name="month_shortest_november">11月</string> + <string name="month_shortest_december">12月</string> + + <string name="day_of_week_long_sunday">星期日</string> + <string name="day_of_week_long_monday">星期一</string> + <string name="day_of_week_long_tuesday">星期二</string> + <string name="day_of_week_long_wednesday">星期三</string> + <string name="day_of_week_long_thursday">星期四</string> + <string name="day_of_week_long_friday">星期五</string> + <string name="day_of_week_long_saturday">星期六</string> + + <string name="day_of_week_medium_sunday">周日</string> + <string name="day_of_week_medium_monday">周一</string> + <string name="day_of_week_medium_tuesday">周二</string> + <string name="day_of_week_medium_wednesday">周三</string> + <string name="day_of_week_medium_thursday">周四</string> + <string name="day_of_week_medium_friday">周五</string> + <string name="day_of_week_medium_saturday">周六</string> + + <string name="day_of_week_short_sunday">周日</string> + <string name="day_of_week_short_monday">周一</string> + <string name="day_of_week_short_tuesday">周二</string> + <string name="day_of_week_short_wednesday">周三</string> + <string name="day_of_week_short_thursday">周四</string> + <string name="day_of_week_short_friday">周五</string> + <string name="day_of_week_short_saturday">周六</string> + + <string name="day_of_week_shortest_sunday">日</string> + <string name="day_of_week_shortest_monday">一</string> + <string name="day_of_week_shortest_tuesday">二</string> + <string name="day_of_week_shortest_wednesday">三</string> + <string name="day_of_week_shortest_thursday">四</string> + <string name="day_of_week_shortest_friday">五</string> + <string name="day_of_week_shortest_saturday">六</string> + + <string name="am">上午</string> + <string name="pm">下午</string> + <string name="yesterday">昨天</string> + <string name="today">今天</string> + <string name="tomorrow">明天</string> + + <string name="hour_minute_24">%-k:%M</string> + <string name="hour_minute_ampm">%p%-l:%M</string> + <string name="hour_minute_cap_ampm">%p%-l:%M</string> + <string name="twelve_hour_time_format">ah:mm</string> + <string name="twenty_four_hour_time_format">H:mm</string> + <string name="numeric_date">%Y-%-m-%-e</string> + <string name="numeric_date_format">yyyy-M-d</string> + <string name="numeric_date_template">"%s-%s-%s"</string> + <string name="month_day_year">%Y年%-m月%-e日</string> + <string name="time_of_day">%p%I:%M:%S</string> + <string name="date_and_time">%p%I:%M:%S %Y-%-m-%-e</string> + <string name="date_time">%2$s %1$s</string> + <string name="time_date">%1$s %3$s</string> + <string name="abbrev_month_day_year">%Y-%-m-%-e</string> + <string name="month_day">%B%-e日</string> + <string name="month">%-B</string> + <string name="month_year">%Y年%B</string> + <string name="abbrev_month_day">%b%-e日</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%Y年%b</string> + <string name="time1_time2">%1$s–%2$s</string> + <string name="date1_date2">%2$s–%5$s</string> + <string name="numeric_md1_md2">%2$s-%3$s至%7$s-%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%2$s-%3$s%1$s至%7$s-%8$s%6$s</string> + <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s至%9$s-%7$s-%8$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%4$s-%2$s-%3$s%1$s至%9$s-%7$s-%8$s%6$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s月%3$s日,%1$s–%10$s %9$s年%7$s月%8$s日,%6$s</string> + <string name="numeric_md1_time1_md2_time2">%5$s %2$s-%3$s–%10$s %7$s-%8$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %2$s-%3$s%1$s–%10$s %7$s-%8$s%6$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s–%10$s %9$s-%7$s-%8$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s%1$s–%6$s %5$s%4$s</string> + <string name="wday1_date1_wday2_date2">%2$s%1$s–%5$s%4$s</string> + <string name="date1_time1_date2_time2">%3$s %2$s–%6$s %5$s</string> + <string name="time_wday_date">%1$s %3$s%2$s</string> + <string name="wday_date">%3$s%2$s</string> + <string name="time_wday">%1$s %2$s</string> + <string name="same_year_md1_md2">%2$s%3$s日–%7$s%8$s日</string> + <string name="same_year_wday1_md1_wday2_md2">%2$s%3$s日%1$s–%7$s%8$s日%6$s</string> + <string name="same_year_md1_time1_md2_time2">%5$s %2$s%3$s日–%10$s %7$s%8$s日</string> + <string name="same_month_md1_time1_md2_time2">%5$s %2$s%3$s日–%10$s %7$s%8$s日</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日</string> + <string name="same_month_mdy1_time1_mdy2_time2">%5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%4$s年%2$s%3$s日%1$s–%9$s年%7$s%8$s日%6$s</string> + <string name="same_month_md1_md2">%2$s%3$s日至%8$s日</string> + <string name="same_month_wday1_md1_wday2_md2">%2$s%3$s日%1$s–%7$s%8$s日%6$s</string> + <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s%8$s日</string> + <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s%8$s日%6$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 514e304f1b25..8cace6638b43 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -161,14 +161,10 @@ <string name="permdesc_changeConfiguration">"允許應用程式變更目前設定,例如:地區設定或字型大小。"</string> <string name="permlab_restartPackages">"重新啟動其他應用程式"</string> <string name="permdesc_restartPackages">"允許應用程式強制重新啟動其他應用程式。"</string> - <string name="permlab_setProcessForeground">"保持已停止狀態"</string> - <string name="permdesc_setProcessForeground">"允許應用程式在前端執行任何程序 (無法中止)。一般應用程式不需要此功能。"</string> <string name="permlab_forceBack">"強制關閉應用程式"</string> <string name="permdesc_forceBack">"允許應用程式強制關閉在前端運作的活動並返回。一般應用程式不需要此功能。"</string> <string name="permlab_dump">"接收系統內部狀態"</string> <string name="permdesc_dump">"允許應用程式取得系統內部狀態。請注意:惡意程式可能利用此功能,不當取得私人或安全性資料。"</string> - <string name="permlab_addSystemService">"發行低階服務"</string> - <string name="permdesc_addSystemService">"允許應用程式發行自有低階系統服務。請注意:惡意程式可能利用此功能綁架系統或偷取、竄改資料內容。"</string> <string name="permlab_runSetActivityWatcher">"監視控制所有應用程式啟動狀態。"</string> <string name="permdesc_runSetActivityWatcher">"允許應用程式監控管理系統啟動活動。請注意:惡意程式可能因此癱瘓整個系統。此權限只在開發時需要,一般手機使用不需要此權限。"</string> <string name="permlab_broadcastPackageRemoved">"傳送程式已移除廣播"</string> @@ -181,8 +177,6 @@ <string name="permdesc_setProcessLimit">"允許應用程式控制可使用的最大執行緒。一般應用程式不需要此功能。"</string> <string name="permlab_setAlwaysFinish">"關閉所有背景程式"</string> <string name="permdesc_setAlwaysFinish">"允許應用程式控制哪些活動在被移到背景執行時,儘速結束。一般應用程式不需要此功能。"</string> - <string name="permlab_fotaUpdate">"自動安裝系統更新"</string> - <string name="permdesc_fotaUpdate">"允許應用程式接收可安裝系統更新的通知,並啟動安裝。請注意:惡意程式可能透過未授權的更新竄改系統,或干擾更新程序。"</string> <string name="permlab_batteryStats">"編輯電池狀態"</string> <string name="permdesc_batteryStats">"允許修改電池狀態。一般應用程式不會使用此功能。"</string> <string name="permlab_internalSystemWindow">"顯示未授權視窗"</string> @@ -418,9 +412,6 @@ <string name="lockscreen_glogin_password_hint">"密碼"</string> <string name="lockscreen_glogin_submit_button">"登入"</string> <string name="lockscreen_glogin_invalid_input">"使用者名稱或密碼錯誤。"</string> - <string name="status_bar_time_format">"<xliff:g id="HOUR">h</xliff:g>:<xliff:g id="MINUTE">mm</xliff:g> <xliff:g id="AMPM">AA</xliff:g>"</string> - <string name="hour_minute_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> - <string name="hour_minute_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="hour_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> <string name="hour_cap_ampm">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> <string name="status_bar_clear_all_button">"清除通知"</string> @@ -452,9 +443,6 @@ <string name="menu_enter_shortcut_label">"輸入"</string> <string name="menu_delete_shortcut_label">"刪除"</string> <string name="search_go">"搜尋"</string> - <string name="today">"今天"</string> - <string name="yesterday">"昨天"</string> - <string name="tomorrow">"明天"</string> <string name="oneMonthDurationPast">"1 個月以前"</string> <string name="beforeOneMonthDurationPast">"1 個月前"</string> <plurals name="num_seconds_ago"> @@ -536,13 +524,6 @@ <string name="weeks">"週"</string> <string name="year">"年"</string> <string name="years">"年"</string> - <string name="sunday">"星期日"</string> - <string name="monday">"星期一"</string> - <string name="tuesday">"星期二"</string> - <string name="wednesday">"星期三"</string> - <string name="thursday">"星期四"</string> - <string name="friday">"星期五"</string> - <string name="saturday">"星期六"</string> <string name="every_weekday">"每天 (週一至週五)"</string> <string name="daily">"每天"</string> <string name="weekly">"每週<xliff:g id="DAY">%s</xliff:g>"</string> @@ -552,137 +533,15 @@ <string name="VideoView_error_text_invalid_progressive_playback">"很抱歉,影片格式無效,裝置無法進行串流處理。"</string> <string name="VideoView_error_text_unknown">"很抱歉,此影片無法播放。"</string> <string name="VideoView_error_button">"確定"</string> - <string name="am">"上午"</string> - <string name="pm">"下午"</string> - <string name="numeric_date">"<xliff:g id="MONTH">%m</xliff:g>/<xliff:g id="DAY">%d</xliff:g>/<xliff:g id="YEAR">%Y</xliff:g>"</string> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>,<xliff:g id="WEEKDAY2">%4$s</xliff:g>,<xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="wday1_date1_wday2_date2">"<xliff:g id="DATE1">%2$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>,<xliff:g id="WEEKDAY2">%4$s</xliff:g>"</string> - <string name="date1_time1_date2_time2">"<xliff:g id="DATE1">%2$s</xliff:g>,<xliff:g id="TIME1">%3$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>,<xliff:g id="TIME2">%6$s</xliff:g>"</string> - <string name="date1_date2">"<xliff:g id="DATE1">%2$s</xliff:g> – <xliff:g id="DATE2">%5$s</xliff:g>"</string> - <string name="time1_time2">"<xliff:g id="TIME1">%1$s</xliff:g> – <xliff:g id="TIME2">%2$s</xliff:g>"</string> - <string name="time_wday_date">"<xliff:g id="DATE">%3$s</xliff:g>,<xliff:g id="WEEKDAY">%2$s</xliff:g>,<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="wday_date">"<xliff:g id="DATE">%3$s</xliff:g>,<xliff:g id="WEEKDAY">%2$s</xliff:g>"</string> - <string name="time_date">"<xliff:g id="DATE">%3$s</xliff:g>,<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="date_time">"<xliff:g id="DATE">%1$s</xliff:g>,<xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="relative_time">"<xliff:g id="DATE">%1$s</xliff:g>,<xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="time_wday">"<xliff:g id="WEEKDAY">%2$s</xliff:g>,<xliff:g id="TIME_RANGE">%1$s</xliff:g>"</string> - <string name="full_date_month_first" format="date">"<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="full_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>' 日'"</string> - <string name="medium_date_month_first" format="date">"<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>','<xliff:g id="YEAR">yyyy</xliff:g>"</string> - <string name="medium_date_day_first" format="date">"<xliff:g id="YEAR">yyyy</xliff:g>' 年 '<xliff:g id="MONTH">MMM</xliff:g>' '<xliff:g id="DAY">d</xliff:g>' 日'"</string> - <string name="twelve_hour_time_format" format="date">"<xliff:g id="HOUR">h</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>' '<xliff:g id="AMPM">a</xliff:g>"</string> - <string name="twenty_four_hour_time_format" format="date">"<xliff:g id="HOUR">HH</xliff:g>':'<xliff:g id="MINUTE">mm</xliff:g>"</string> <string name="noon">"中午"</string> <string name="Noon">"中午"</string> <string name="midnight">"午夜"</string> <string name="Midnight">"午夜"</string> - <string name="month_day">"<xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g> 日"</string> <!-- no translation found for month (7026169712234774086) --> <skip /> - <string name="month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g> 日"</string> - <string name="month_year">"<xliff:g id="YEAR">%Y</xliff:g> 年<xliff:g id="MONTH">%B</xliff:g>"</string> - <string name="time_of_day">"<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="date_and_time">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%B</xliff:g> <xliff:g id="DAY">%-d</xliff:g> 日,<xliff:g id="HOUR">%H</xliff:g>:<xliff:g id="MINUTE">%M</xliff:g>:<xliff:g id="SECOND">%S</xliff:g>"</string> - <string name="same_year_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> 日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_mdy1_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日 – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g> 日,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g> 日,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>,<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>,<xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>, <xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_mdy1_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1">%3$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2">%8$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g>/<xliff:g id="MONTH1">%2$s</xliff:g>/<xliff:g id="DAY1_0">%3$s</xliff:g>,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g>/<xliff:g id="MONTH2">%7$s</xliff:g>/<xliff:g id="DAY2_1">%8$s</xliff:g>,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_md1_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日 – <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> 日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_mdy1_mdy2">"<xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日 – <xliff:g id="DAY2">%8$s</xliff:g> 日"</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> 日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>"</string> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g> 日,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> 日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1">%3$s</xliff:g> 日 <xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2">%8$s</xliff:g> 日 <xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="YEAR1">%4$s</xliff:g> 年 <xliff:g id="MONTH1">%2$s</xliff:g> <xliff:g id="DAY1_0">%3$s</xliff:g> 日,<xliff:g id="WEEKDAY1">%1$s</xliff:g>,<xliff:g id="TIME1">%5$s</xliff:g> – <xliff:g id="YEAR2">%9$s</xliff:g> 年 <xliff:g id="MONTH2">%7$s</xliff:g> <xliff:g id="DAY2_1">%8$s</xliff:g> 日,<xliff:g id="WEEKDAY2">%6$s</xliff:g>,<xliff:g id="TIME2">%10$s</xliff:g>"</string> - <string name="abbrev_month_day_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g> 日"</string> - <string name="abbrev_month_year">"<xliff:g id="YEAR">%Y</xliff:g> 年 <xliff:g id="MONTH">%b</xliff:g>"</string> - <string name="abbrev_month_day">"<xliff:g id="MONTH">%b</xliff:g> <xliff:g id="DAY">%-d</xliff:g> 日"</string> <!-- no translation found for abbrev_month (3131032032850777433) --> <skip /> - <string name="day_of_week_long_sunday">"星期日"</string> - <string name="day_of_week_long_monday">"星期一"</string> - <string name="day_of_week_long_tuesday">"星期二"</string> - <string name="day_of_week_long_wednesday">"星期三"</string> - <string name="day_of_week_long_thursday">"星期四"</string> - <string name="day_of_week_long_friday">"星期五"</string> - <string name="day_of_week_long_saturday">"星期六"</string> - <string name="day_of_week_medium_sunday">"週日"</string> - <string name="day_of_week_medium_monday">"週一"</string> - <string name="day_of_week_medium_tuesday">"週二"</string> - <string name="day_of_week_medium_wednesday">"週三"</string> - <string name="day_of_week_medium_thursday">"週四"</string> - <string name="day_of_week_medium_friday">"五"</string> - <string name="day_of_week_medium_saturday">"週六"</string> - <string name="day_of_week_short_sunday">"日"</string> - <string name="day_of_week_short_monday">"一"</string> - <string name="day_of_week_short_tuesday">"二"</string> - <string name="day_of_week_short_wednesday">"三"</string> - <string name="day_of_week_short_thursday">"週四"</string> - <string name="day_of_week_short_friday">"五"</string> - <string name="day_of_week_short_saturday">"六"</string> - <string name="day_of_week_shorter_sunday">"日"</string> - <string name="day_of_week_shorter_monday">"一"</string> - <string name="day_of_week_shorter_tuesday">"二"</string> - <string name="day_of_week_shorter_wednesday">"三"</string> - <string name="day_of_week_shorter_thursday">"四"</string> - <string name="day_of_week_shorter_friday">"五"</string> - <string name="day_of_week_shorter_saturday">"六"</string> - <string name="day_of_week_shortest_sunday">"日"</string> - <string name="day_of_week_shortest_monday">"一"</string> - <string name="day_of_week_shortest_tuesday">"二"</string> - <string name="day_of_week_shortest_wednesday">"三"</string> - <string name="day_of_week_shortest_thursday">"四"</string> - <string name="day_of_week_shortest_friday">"五"</string> - <string name="day_of_week_shortest_saturday">"六"</string> - <string name="month_long_january">"1 月"</string> - <string name="month_long_february">"2 月"</string> - <string name="month_long_march">"3 月"</string> - <string name="month_long_april">"4 月"</string> - <string name="month_long_may">"5 月"</string> - <string name="month_long_june">"6 月"</string> - <string name="month_long_july">"7 月"</string> - <string name="month_long_august">"8 月"</string> - <string name="month_long_september">"9 月"</string> - <string name="month_long_october">"10 月"</string> - <string name="month_long_november">"11 月"</string> - <string name="month_long_december">"12 月"</string> - <string name="month_medium_january">"1 月"</string> - <string name="month_medium_february">"2 月"</string> - <string name="month_medium_march">"3 月"</string> - <string name="month_medium_april">"4 月"</string> - <string name="month_medium_may">"5 月"</string> - <string name="month_medium_june">"6 月"</string> - <string name="month_medium_july">"7 月"</string> - <string name="month_medium_august">"8 月"</string> - <string name="month_medium_september">"9 月"</string> - <string name="month_medium_october">"10 月"</string> - <string name="month_medium_november">"11 月"</string> - <string name="month_medium_december">"12 月"</string> - <string name="month_shortest_january">"1"</string> - <string name="month_shortest_february">"2"</string> - <string name="month_shortest_march">"3"</string> - <string name="month_shortest_april">"4"</string> - <string name="month_shortest_may">"5"</string> - <string name="month_shortest_june">"6"</string> - <string name="month_shortest_july">"7"</string> - <string name="month_shortest_august">"8"</string> - <string name="month_shortest_september">"9"</string> - <string name="month_shortest_october">"10"</string> - <string name="month_shortest_november">"11"</string> - <string name="month_shortest_december">"12"</string> <string name="elapsed_time_short_format_mm_ss">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll">"全部選取"</string> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 7db73f014140..66f0e820cb38 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -110,7 +110,10 @@ <item><xliff:g id="id">alarm_clock</xliff:g></item> <item><xliff:g id="id">battery</xliff:g></item> <item><xliff:g id="id">phone_signal</xliff:g></item> + <item><xliff:g id="id">phone_evdo_signal</xliff:g></item> <item><xliff:g id="id">data_connection</xliff:g></item> + <item><xliff:g id="id">cdma_eri</xliff:g></item> + <item><xliff:g id="id">tty</xliff:g></item> <item><xliff:g id="id">volume</xliff:g></item> <item><xliff:g id="id">mute</xliff:g></item> <item><xliff:g id="id">speakerphone</xliff:g></item> @@ -122,4 +125,14 @@ <item><xliff:g id="id">sync_failing</xliff:g></item> <item><xliff:g id="id">ime</xliff:g></item> </string-array> + + <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH + handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers, + the first component from this list which is found to be installed is set as the + preferred activity. --> + <string-array name="default_web_search_providers"> + <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item> + <item>com.android.googlesearch/.GoogleSearch</item> + <item>com.android.websearch/.Search.1</item> + </string-array> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 633a831f0b18..6f2a5d3c6f8a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4,9 +4,9 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -64,10 +64,13 @@ <!-- Inverse hint text color --> <attr name="textColorHintInverse" format="reference|color" /> - + <!-- Bright text color. Only differentiates based on the disabled state. --> <attr name="textColorPrimaryDisableOnly" format="reference|color" /> + <!-- Bright inverse text color. Only differentiates based on the disabled state. --> + <attr name="textColorPrimaryInverseDisableOnly" format="reference|color" /> + <!-- Bright text color. This does not differentiate the disabled state. As an example, buttons use this since they display the disabled state via the background and not the foreground text color. --> @@ -80,6 +83,11 @@ <!-- Dim inverse text color. This does not differentiate the disabled state. --> <attr name="textColorSecondaryInverseNoDisable" format="reference|color" /> + <!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide --> + <attr name="textColorSearchUrl" format="reference|color" /> + <!-- Search widget more corpus result item background. --> + <attr name="searchWidgetCorpusItemBackground" format="reference|color" /> + <!-- Text color, typeface, size, and style for "large" text. Defaults to primary text color. --> <attr name="textAppearanceLarge" format="reference" /> <!-- Text color, typeface, size, and style for "medium" text. Defaults to primary text color. --> @@ -94,27 +102,28 @@ <!-- Text color, typeface, size, and style for "small" inverse text. Defaults to secondary inverse text color. --> <attr name="textAppearanceSmallInverse" format="reference" /> - <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. @hide --> - <attr name="textAppearanceSearchResultTitle" format="reference" /> - <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. @hide --> + <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. @hide --> + <attr name="textAppearanceSearchResultTitle" format="reference" /> + <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. @hide --> <attr name="textAppearanceSearchResultSubtitle" format="reference" /> + <!-- Text color, typeface, size, and style for the text inside of a button. --> <attr name="textAppearanceButton" format="reference" /> - + <!-- A styled string, specifying the style to be used for showing inline candidate text when composing with an input method. The text itself will be ignored, but the style spans will be applied to the candidate text as it is edited. --> <attr name="candidatesTextStyleSpans" format="reference|string" /> - + <!-- Drawable to use for check marks --> <attr name="textCheckMark" format="reference" /> <attr name="textCheckMarkInverse" format="reference" /> <!-- Drawable to use for multiple choice indicators--> <attr name="listChoiceIndicatorMultiple" format="reference" /> - + <!-- Drawable to use for single choice indicators--> <attr name="listChoiceIndicatorSingle" format="reference" /> @@ -139,7 +148,7 @@ <!-- Gallery styles --> <!-- ============== --> <eat-comment /> - + <!-- The preferred background for gallery items. This should be set as the background of any Views you provide from the Adapter. --> <attr name="galleryItemBackground" format="reference" /> @@ -152,27 +161,27 @@ <!-- The preferred list item height --> <attr name="listPreferredItemHeight" format="dimension" /> <!-- The drawable for the list divider --> - <!-- The list item height for search results. @hide --> - <attr name="searchResultListItemHeight" format="dimension" /> + <!-- The list item height for search results. @hide --> + <attr name="searchResultListItemHeight" format="dimension" /> <attr name="listDivider" format="reference" /> <!-- TextView style for list separators. --> <attr name="listSeparatorTextViewStyle" format="reference" /> <!-- The preferred left padding for an expandable list item (for child-specific layouts, use expandableListPreferredChildPaddingLeft). This takes into account - the indicator that will be shown to next to the item. --> + the indicator that will be shown to next to the item. --> <attr name="expandableListPreferredItemPaddingLeft" format="dimension" /> <!-- The preferred left padding for an expandable list item that is a child. - If this is not provided, it defaults to the expandableListPreferredItemPaddingLeft. --> + If this is not provided, it defaults to the expandableListPreferredItemPaddingLeft. --> <attr name="expandableListPreferredChildPaddingLeft" format="dimension" /> <!-- The preferred left bound for an expandable list item's indicator. For a child-specific - indicator, use expandableListPreferredChildIndicatorLeft. --> + indicator, use expandableListPreferredChildIndicatorLeft. --> <attr name="expandableListPreferredItemIndicatorLeft" format="dimension" /> <!-- The preferred right bound for an expandable list item's indicator. For a child-specific - indicator, use expandableListPreferredChildIndicatorRight. --> + indicator, use expandableListPreferredChildIndicatorRight. --> <attr name="expandableListPreferredItemIndicatorRight" format="dimension" /> - <!-- The preferred left bound for an expandable list child's indicator. --> + <!-- The preferred left bound for an expandable list child's indicator. --> <attr name="expandableListPreferredChildIndicatorLeft" format="dimension" /> - <!-- The preferred right bound for an expandable list child's indicator. --> + <!-- The preferred right bound for an expandable list child's indicator. --> <attr name="expandableListPreferredChildIndicatorRight" format="dimension" /> <!-- ============= --> @@ -221,7 +230,7 @@ any of the attributes defined by {@link android.R.styleable#WindowAnimation}. --> <attr name="windowAnimationStyle" format="reference" /> - + <!-- Defines the default soft input state that this window would like when it is displayed. --> <attr name="windowSoftInputMode"> @@ -243,7 +252,7 @@ <!-- Always make the soft input area visible when this window has input focus. --> <flag name="stateAlwaysVisible" value="5" /> - + <!-- The window resize/pan adjustment has not been specified, the system will automatically select between resize and pan modes, depending @@ -270,7 +279,7 @@ use the window's theme to show a preview of it before your actual instance is shown to the user. --> <attr name="windowDisablePreview" format="boolean" /> - + <!-- Flag indicating that this window should not be displayed at all. The default value is false; if set to true, and this window is the main window of an Activity, then it will never actually @@ -278,13 +287,13 @@ must immediately quit without waiting for user interaction, because there will be no such interaction coming. --> <attr name="windowNoDisplay" format="boolean" /> - + <!-- ============ --> <!-- Alert Dialog styles --> <!-- ============ --> <eat-comment /> <attr name="alertDialogStyle" format="reference" /> - + <!-- ============ --> <!-- Panel styles --> <!-- ============ --> @@ -320,6 +329,8 @@ <attr name="expandableListViewStyle" format="reference" /> <!-- Default Gallery style. --> <attr name="galleryStyle" format="reference" /> + <!-- Default GestureOverlayView style. --> + <attr name="gestureOverlayViewStyle" format="reference" /> <!-- Default GridView style. --> <attr name="gridViewStyle" format="reference" /> <!-- The style resource to use for an ImageButton --> @@ -342,6 +353,12 @@ <attr name="progressBarStyleSmallTitle" format="reference" /> <!-- Large ProgressBar style. This is a large circular progress bar. --> <attr name="progressBarStyleLarge" format="reference" /> + <!-- Inverse ProgressBar style. This is a medium circular progress bar. --> + <attr name="progressBarStyleInverse" format="reference" /> + <!-- Small inverse ProgressBar style. This is a small circular progress bar. --> + <attr name="progressBarStyleSmallInverse" format="reference" /> + <!-- Large inverse ProgressBar style. This is a large circular progress bar. --> + <attr name="progressBarStyleLargeInverse" format="reference" /> <!-- Default SeekBar style. --> <attr name="seekBarStyle" format="reference" /> <!-- Default RatingBar style. --> @@ -381,7 +398,7 @@ <!-- Preference styles --> <!-- =================== --> <eat-comment /> - + <!-- Default style for PreferenceScreen. --> <attr name="preferenceScreenStyle" format="reference" /> <!-- Default style for PreferenceCategory. --> @@ -402,7 +419,7 @@ <attr name="ringtonePreferenceStyle" format="reference" /> <!-- The preference layout that has the child/tabbed effect. --> <attr name="preferenceLayoutChild" format="reference" /> - + </declare-styleable> <!-- **************************************************************** --> @@ -420,7 +437,7 @@ </ul> --> <attr name="textSize" format="dimension" /> - + <!-- Default text typeface. --> <attr name="typeface"> <enum name="normal" value="0" /> @@ -428,26 +445,26 @@ <enum name="serif" value="2" /> <enum name="monospace" value="3" /> </attr> - + <!-- Default text typeface style. --> <attr name="textStyle"> <flag name="normal" value="0" /> <flag name="bold" value="1" /> <flag name="italic" value="2" /> </attr> - + <!-- Color of text (usually same as colorForeground). --> <attr name="textColor" format="reference|color" /> - + <!-- Color of highlighted text. --> <attr name="textColorHighlight" format="reference|color" /> - + <!-- Color of hint text (displayed when the field is empty). --> <attr name="textColorHint" format="reference|color" /> - + <!-- Color of link text (URLs). --> <attr name="textColorLink" format="reference|color" /> - + <!-- Where to ellipsize text. --> <attr name="ellipsize"> <enum name="none" value="0" /> @@ -456,7 +473,7 @@ <enum name="end" value="3" /> <enum name="marquee" value="4" /> </attr> - + <!-- The type of data being placed in a text field, used to help an input method decide how to let the user enter text. The constants here correspond to those defined by @@ -663,9 +680,9 @@ <attr name="x" format="dimension" /> <!-- A coordinate in the Y dimension. --> <attr name="y" format="dimension" /> - - <!-- Specifies how to place an object, both - its x and y axis, within a larger containing object. --> + + <!-- Specifies how to place the content of an object, both + on the x and y axis, within the object itself. --> <attr name="gravity"> <!-- Push object to the top of its container, not changing its size. --> <flag name="top" value="0x30" /> @@ -721,8 +738,7 @@ <attr name="entries" format="reference" /> <!-- Standard gravity constant that a child can supply to its parent. - Defines how to place an object, both - its x and y axis, within a larger containing object. --> + Defines how to place the view, both its x and y axis, within its parent view group. --> <attr name="layout_gravity"> <!-- Push object to the top of its container, not changing its size. --> <flag name="top" value="0x30" /> @@ -769,11 +785,11 @@ <!-- ========================== --> <eat-comment /> - <!-- This enum provides the same keycode values as can be found in + <!-- This enum provides the same keycode values as can be found in {@link android.view.KeyEvent} --> <attr name="keycode"> <enum name="KEYCODE_UNKNOWN" value="0" /> - <enum name="KEYCODE_SOFT_LEFT" value="1" /> + <enum name="KEYCODE_SOFT_LEFT" value="1" /> <enum name="KEYCODE_SOFT_RIGHT" value="2" /> <enum name="KEYCODE_HOME" value="3" /> <enum name="KEYCODE_BACK" value="4" /> @@ -906,7 +922,7 @@ <attr name="bottomMedium" format="reference|color" /> <attr name="centerMedium" format="reference|color" /> </declare-styleable> - + <!-- Window animation class attributes. --> <declare-styleable name="WindowAnimation"> <!-- The animation used when a window is being added. --> @@ -950,7 +966,7 @@ allows you to later retrieve the view with <code>findViewById(R.id.my_id)</code>. --> <attr name="id" format="reference" /> - + <!-- Supply a tag for this view containing a String, to be retrieved later with {@link android.view.View#getTag View.getTag()} or searched for with {@link android.view.View#findViewWithTag @@ -958,7 +974,7 @@ IDs (through the android:id attribute) instead of tags because they are faster and allow for compile-time type checking. --> <attr name="tag" format="string" /> - + <!-- The initial horizontal scroll offset, in pixels.--> <attr name="scrollX" format="dimension" /> @@ -972,7 +988,7 @@ <attr name="background" format="reference|color" /> <!-- Sets the padding, in pixels, of all four edges. Padding is defined as - space between the edges of the view and the view's content. A views size + space between the edges of the view and the view's content. A views size will include it's padding. If a {@link android.R.attr#background} is provided, the padding will initially be set to that (0 if the drawable does not have padding). Explicitly setting a padding value @@ -1027,12 +1043,12 @@ </attr> <!-- Controls the scrollbar style and position. The scrollbars can be overlaid or - inset. When inset, they add to the padding of the view. And the - scrollbars can be drawn inside the padding area or on the edge of - the view. For example, if a view has a background drawable and you - want to draw the scrollbars inside the padding specified by the - drawable, you can use insideOverlay or insideInset. If you want them - to appear at the edge of the view, ignoring the padding, then you can + inset. When inset, they add to the padding of the view. And the + scrollbars can be drawn inside the padding area or on the edge of + the view. For example, if a view has a background drawable and you + want to draw the scrollbars inside the padding specified by the + drawable, you can use insideOverlay or insideInset. If you want them + to appear at the edge of the view, ignoring the padding, then you can use outsideOverlay or outsideInset.--> <attr name="scrollbarStyle"> <!-- Inside the padding and overlaid --> @@ -1044,14 +1060,14 @@ <!-- Edge of the view and inset --> <enum name="outsideInset" value="0x03000000" /> </attr> - + <!-- Set this if the view will serve as a scrolling container, meaing that it can be resized to shrink its overall window so that there will be space for an input method. If not set, the default value will be true if "scrollbars" has the vertical scrollbar set, else it will be false. --> <attr name="isScrollContainer" format="boolean" /> - + <!-- Sets the width of vertical scrollbars and height of horizontal scrollbars. --> <attr name="scrollbarSize" format="dimension" /> <!-- Defines the horizontal scrollbar thumb drawable. --> @@ -1113,10 +1129,10 @@ <!-- Defines whether this view reacts to click events. --> <attr name="clickable" format="boolean" /> - + <!-- Defines whether this view reacts to long click events. --> <attr name="longClickable" format="boolean" /> - + <!-- If unset, no state will be saved for this view when it is being frozen. The default is true, allowing the view to be saved (however it also must have an ID assigned to it for its @@ -1124,7 +1140,7 @@ state for this view, not for its children which may still be saved. --> <attr name="saveEnabled" format="boolean" /> - + <!-- Defines the quality of translucent drawing caches. This property is used only when the drawing cache is enabled and translucent. The default value is auto. --> <attr name="drawingCacheQuality"> @@ -1142,16 +1158,16 @@ <!-- Controls whether the view's window should keep the screen on while visible. --> <attr name="keepScreenOn" format="boolean" /> - + <!-- When this attribute is set to true, the view gets its drawable state (focused, pressed, etc.) from its direct parent rather than from itself. --> <attr name="duplicateParentState" format="boolean" /> - + <!-- Defines the minimum height of the view. It is not guaranteed the view will be able to achieve this minimum height (for example, if its parent layout constrains it with less available height). --> <attr name="minHeight" /> - + <!-- Defines the minimum width of the view. It is not guaranteed the view will be able to achieve this minimum width (for example, if its parent layout constrains it with less available width). --> @@ -1165,6 +1181,11 @@ enabled for events such as long presses. --> <attr name="hapticFeedbackEnabled" format="boolean" /> + <!-- Defines text that briefly describes content of the view. This property is used + primarily for accessibility. Since some views do not have textual + representation this attribute can be used for providing such. --> + <attr name="contentDescription" format="string" localization="suggested" /> + <!-- Name of the method in this View's context to invoke when the view is clicked. This name must correspond to a public method that takes exactly one parameter of type View. For instance, if you specify @@ -1208,7 +1229,7 @@ <flag name="animation" value="0x1" /> <!-- The drawing cache is persisted after a scroll. --> <flag name="scrolling" value="0x2" /> - <!-- The drawing cache is always persisted. --> + <!-- The drawing cache is always persisted. --> <flag name="all" value="0x3" /> </attr> <!-- Defines whether the ViewGroup should always draw its children using their @@ -1321,7 +1342,7 @@ method should be considered an option as the default. --> <attr name="isDefault" format="boolean" /> </declare-styleable> - + <!-- =============================== --> <!-- Widget package class attributes --> <!-- =============================== --> @@ -1330,7 +1351,7 @@ <declare-styleable name="AbsListView"> <!-- Drawable used to indicate the currently selected item in the list. --> <attr name="listSelector" format="color|reference" /> - <!-- When set to true, the selector will be drawn over the selected item. + <!-- When set to true, the selector will be drawn over the selected item. Otherwise the selector is drawn behind the selected item. The default value is false. --> <attr name="drawSelectorOnTop" format="boolean" /> @@ -1353,13 +1374,13 @@ already visible on screen. --> <enum name="normal" value="1" /> <!-- The list will automatically scroll to the bottom, no matter what items - are currently visible. --> + are currently visible. --> <enum name="alwaysScroll" value="2" /> </attr> <!-- Indicates that this list will always be drawn on top of solid, single-color opaque background. This allows the list to optimize drawing. --> <attr name="cacheColorHint" format="color" /> - <!-- Enables the fast scroll thumb that can be dragged to quickly scroll through + <!-- Enables the fast scroll thumb that can be dragged to quickly scroll through the list. --> <attr name="fastScrollEnabled" format="boolean" /> <!-- When set to true, the list will use a more refined calculation @@ -1448,7 +1469,7 @@ <!-- Defines whether the foreground drawable should be drawn inside the padding. This property is turned on by default. --> <attr name="foregroundInsidePadding" format="boolean" /> - <!-- Determines whether to measure all children or just those in + <!-- Determines whether to measure all children or just those in the VISIBLE or INVISIBLE state when measuring. Defaults to false. --> <attr name="measureAllChildren" format="boolean" /> </declare-styleable> @@ -1458,14 +1479,14 @@ <!-- Indicator shown beside the child View. This can be a stateful Drawable. --> <attr name="childIndicator" format="reference" /> <!-- The left bound for an item's indicator. To specify a left bound specific to children, - use childIndicatorLeft. --> + use childIndicatorLeft. --> <attr name="indicatorLeft" format="dimension" /> <!-- The right bound for an item's indicator. To specify a right bound specific to children, - use childIndicatorRight. --> + use childIndicatorRight. --> <attr name="indicatorRight" format="dimension" /> - <!-- The left bound for a child's indicator. --> + <!-- The left bound for a child's indicator. --> <attr name="childIndicatorLeft" format="dimension" /> - <!-- The right bound for a child's indicator. --> + <!-- The right bound for a child's indicator. --> <attr name="childIndicatorRight" format="dimension" /> <!-- Drawable or color that is used as a divider for children. (It will drawn below and above child items.) The height of this will be the same as @@ -1516,10 +1537,10 @@ <!-- Set this to true if you want the ImageView to adjust its bounds to preserve the aspect ratio of its drawable. --> <attr name="adjustViewBounds" format="boolean" /> - <!-- An optional argument to supply a maximum width for this view. + <!-- An optional argument to supply a maximum width for this view. See {see android.widget.ImageView#setMaxWidth} for details. --> <attr name="maxWidth" format="dimension" /> - <!-- An optional argument to supply a maximum height for this view. + <!-- An optional argument to supply a maximum height for this view. See {see android.widget.ImageView#setMaxHeight} for details. --> <attr name="maxHeight" format="dimension" /> <!-- Set a tinting color for the image --> @@ -1573,9 +1594,9 @@ <!-- Height of the divider. Will use the intrinsic height of the divider if this is not specified. --> <attr name="dividerHeight" format="dimension" /> - <!-- Defines the choice behavior for the List. By default, Lists do not have + <!-- Defines the choice behavior for the ListView. By default, lists do not have any choice behavior. By setting the choiceMode to singleChoice, the List - allows up to one item to be in a chosen state. By setting the choiceMode to + allows up to one item to be in a chosen state. By setting the choiceMode to multipleChoice, the list allows any number of items to be chosen. --> <attr name="choiceMode"> <!-- Normal list that does not indicate choices --> @@ -1620,7 +1641,7 @@ <!-- 'More' icon --> <attr name="moreIcon" format="reference" /> </declare-styleable> - + <declare-styleable name="ProgressBar"> <!-- Defines the maximum value the progress can take. --> <attr name="max" format="integer" /> @@ -1653,27 +1674,27 @@ <attr name="maxWidth" /> <attr name="minHeight" format="dimension" /> <attr name="maxHeight" /> - <attr name="interpolator" format="reference" /> + <attr name="interpolator" format="reference" /> </declare-styleable> - + <declare-styleable name="SeekBar"> - <!-- Draws the thumb on a seekbar --> + <!-- Draws the thumb on a seekbar --> <attr name="thumb" format="reference" /> <!-- An offset for the thumb that allows it to extend out of the range of the track. --> - <attr name="thumbOffset" format="dimension" /> + <attr name="thumbOffset" format="dimension" /> </declare-styleable> - + <declare-styleable name="RatingBar"> - <!-- The number of stars (or rating items) to show. --> - <attr name="numStars" format="integer" /> - <!-- The rating to set by default. --> - <attr name="rating" format="float" /> - <!-- The step size of the rating. --> - <attr name="stepSize" format="float" /> - <!-- Whether this rating bar is an indicator (and non-changeable by the user). --> - <attr name="isIndicator" format="boolean" /> + <!-- The number of stars (or rating items) to show. --> + <attr name="numStars" format="integer" /> + <!-- The rating to set by default. --> + <attr name="rating" format="float" /> + <!-- The step size of the rating. --> + <attr name="stepSize" format="float" /> + <!-- Whether this rating bar is an indicator (and non-changeable by the user). --> + <attr name="isIndicator" format="boolean" /> </declare-styleable> - + <declare-styleable name="RadioGroup"> <!-- The id of the child radio button that should be checked by default within this radio group. --> @@ -1695,7 +1716,7 @@ indices are ignored. You can shrink all columns by using the value "*" instead. Note that a column can be marked stretchable and shrinkable at the same time. --> - <attr name="shrinkColumns" format="string" /> + <attr name="shrinkColumns" format="string" /> <!-- The 0 based index of the columns to collapse. The column indices must be separated by a comma: 1, 2, 5. Illegal and duplicate indices are ignored. --> @@ -1796,7 +1817,7 @@ <attr name="minEms" format="integer" min="0" /> <!-- Makes the TextView be at least this many pixels wide --> <attr name="minWidth" /> - <!-- Specifies how to align the text by the view's x and/or y axis + <!-- Specifies how to align the text by the view's x and/or y axis when the text is smaller than the view. --> <attr name="gravity" /> <!-- Whether the text is allowed to be wider than the view (and @@ -1814,10 +1835,10 @@ attribute. (If both singleLine and inputType are supplied, the inputType flags will override the value of singleLine.) {@deprecated This attribute is deprecated and is replaced by the textMultiLine flag - in the inputType attribute. Use caution when altering existing layouts, as the - default value of singeLine is false (multi-line mode), but if you specify any - value for inputType, the default is single-line mode. (If both singleLine and - inputType attributes are found, the inputType flags will override the value of + in the inputType attribute. Use caution when altering existing layouts, as the + default value of singeLine is false (multi-line mode), but if you specify any + value for inputType, the default is single-line mode. (If both singleLine and + inputType attributes are found, the inputType flags will override the value of singleLine.) } --> <attr name="singleLine" format="boolean" /> <!-- {@deprecated Use state_enabled instead.} --> @@ -1921,7 +1942,7 @@ <attr name="lineSpacingExtra" format="dimension" /> <!-- Extra spacing between lines of text, as a multiplier. --> <attr name="lineSpacingMultiplier" format="float" /> - <!-- The number of times to repeat the marquee animation. Only applied if the + <!-- The number of times to repeat the marquee animation. Only applied if the TextView has marquee enabled. --> <attr name="marqueeRepeatLimit" format="integer"> <!-- Indicates that marquee should repeat indefinitely --> @@ -1986,6 +2007,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"> @@ -2012,7 +2043,7 @@ <!-- The prompt to display when the spinner's dialog is shown. --> <attr name="prompt" format="reference" /> </declare-styleable> - <declare-styleable name="DatePicker"> + <declare-styleable name="DatePicker"> <!-- The first year (inclusive) i.e. 1940 --> <attr name="startYear" format="integer" /> <!-- The last year (inclusive) i.e. 2010 --> @@ -2053,6 +2084,44 @@ <attr name="animateOnClick" format="boolean" /> </declare-styleable> + <!-- GestureOverlayView specific attributes. These attributes are used to configure + a GestureOverlayView from XML. --> + <declare-styleable name="GestureOverlayView"> + <!-- Width of the stroke used to draw the gesture. --> + <attr name="gestureStrokeWidth" format="float" /> + <!-- Color used to draw a gesture. --> + <attr name="gestureColor" format="color" /> + <!-- Color used to draw the user's strokes until we are sure it's a gesture. --> + <attr name="uncertainGestureColor" format="color" /> + <!-- Time, in milliseconds, to wait before the gesture fades out after the user + is done drawing it. --> + <attr name="fadeOffset" format="integer" /> + <!-- Duration, in milliseconds, of the fade out effect after the user is done + drawing a gesture. --> + <attr name="fadeDuration" format="integer" /> + <!-- Defines the type of strokes that define a gesture. --> + <attr name="gestureStrokeType"> + <!-- A gesture is made of only one stroke. --> + <enum name="single" value="0" /> + <!-- A gesture is made of multiple strokes. --> + <enum name="multiple" value="1" /> + </attr> + <!-- Minimum length of a stroke before it is recognized as a gesture. --> + <attr name="gestureStrokeLengthThreshold" format="float" /> + <!-- Squareness threshold of a stroke before it is recognized as a gesture. --> + <attr name="gestureStrokeSquarenessThreshold" format="float" /> + <!-- Minimum curve angle a stroke must contain before it is recognized as a gesture. --> + <attr name="gestureStrokeAngleThreshold" format="float" /> + <!-- Defines whether the overlay should intercept the motion events when a gesture + is recognized. --> + <attr name="eventsInterceptionEnabled" format="boolean" /> + <!-- Defines whether the gesture will automatically fade out after being recognized. --> + <attr name="fadeEnabled" format="boolean" /> + <!-- Indicates whether horizontal (when the orientation is vertical) or vertical + (when orientation is horizontal) strokes automatically define a gesture. --> + <attr name="orientation" /> + </declare-styleable> + <!-- ======================================= --> <!-- Widget package parent layout attributes --> <!-- ======================================= --> @@ -2136,7 +2205,7 @@ {@link android.graphics.drawable.Drawable#setVisible} --> <attr name="visible" format="boolean" /> </declare-styleable> - + <declare-styleable name="StateListDrawable"> <attr name="visible" /> <!-- If true, allows the drawable's padding to change based on the @@ -2152,7 +2221,7 @@ current state. --> <attr name="constantSize" format="boolean" /> </declare-styleable> - + <declare-styleable name="AnimationDrawable"> <attr name="visible" /> <attr name="variablePadding" /> @@ -2161,15 +2230,15 @@ restarting at the first frame after the last has finished. --> <attr name="oneshot" format="boolean" /> </declare-styleable> - + <declare-styleable name="AnimationDrawableItem"> <!-- Amount of time (in milliseconds) to display this frame. --> - <attr name="duration" format="integer" /> + <attr name="duration" format="integer" /> <!-- Reference to a drawable resource to use for the frame. If not given, the drawable must be defined by the first child tag. --> <attr name="drawable" format="reference" /> </declare-styleable> - + <declare-styleable name="GradientDrawable"> <attr name="visible" /> <attr name="shape"> @@ -2192,18 +2261,18 @@ <attr name="thickness" format="dimension" /> <attr name="useLevel" /> </declare-styleable> - + <declare-styleable name="GradientDrawableSize"> <attr name="width" /> <attr name="height" /> </declare-styleable> - + <declare-styleable name="GradientDrawableGradient"> <attr name="startColor" format="color" /> <!-- Optional center color. For linear gradients, use centerX or centerY to place the center color. --> <attr name="centerColor" format="color" /> <attr name="endColor" format="color" /> - <attr name="useLevel" format="boolean" /> + <attr name="useLevel" format="boolean" /> <attr name="angle" format="float" /> <attr name="type"> <enum name="linear" value="0" /> @@ -2214,18 +2283,18 @@ <attr name="centerY" format="float|fraction" /> <attr name="gradientRadius" format="float|fraction" /> </declare-styleable> - + <declare-styleable name="GradientDrawableSolid"> <attr name="color" format="color" /> </declare-styleable> - + <declare-styleable name="GradientDrawableStroke"> <attr name="width" /> <attr name="color" /> <attr name="dashWidth" format="dimension" /> <attr name="dashGap" format="dimension" /> </declare-styleable> - + <declare-styleable name="DrawableCorners"> <attr name="radius" format="dimension" /> <attr name="topLeftRadius" format="dimension" /> @@ -2233,14 +2302,14 @@ <attr name="bottomLeftRadius" format="dimension" /> <attr name="bottomRightRadius" format="dimension" /> </declare-styleable> - + <declare-styleable name="GradientDrawablePadding"> <attr name="left" format="dimension" /> <attr name="top" format="dimension" /> <attr name="right" format="dimension" /> <attr name="bottom" format="dimension" /> </declare-styleable> - + <declare-styleable name="LayerDrawableItem"> <attr name="left" /> <attr name="top" /> @@ -2249,7 +2318,7 @@ <attr name="drawable" /> <attr name="id" /> </declare-styleable> - + <declare-styleable name="LevelListDrawableItem"> <!-- The minimum level allowed for this item. --> <attr name="minLevel" format="integer" /> @@ -2257,7 +2326,7 @@ <attr name="maxLevel" format="integer" /> <attr name="drawable" /> </declare-styleable> - + <declare-styleable name="RotateDrawable"> <attr name="visible" /> <attr name="fromDegrees" format="float" /> @@ -2267,6 +2336,15 @@ <attr name="drawable" /> </declare-styleable> + <declare-styleable name="AnimatedRotateDrawable"> + <attr name="visible" /> + <attr name="frameDuration" format="integer" /> + <attr name="framesCount" format="integer" /> + <attr name="pivotX" /> + <attr name="pivotY" /> + <attr name="drawable" /> + </declare-styleable> + <declare-styleable name="InsetDrawable"> <attr name="visible" /> <attr name="drawable" /> @@ -2413,7 +2491,7 @@ <declare-styleable name="AnimationSet"> <attr name="shareInterpolator" format="boolean" /> </declare-styleable> - + <declare-styleable name="Animation"> <!-- Defines the interpolator used to smooth the animation movement in time. --> <attr name="interpolator" /> @@ -2456,14 +2534,14 @@ <enum name="bottom" value="-1" /> </attr> </declare-styleable> - + <declare-styleable name="RotateAnimation"> <attr name="fromDegrees" /> <attr name="toDegrees" /> <attr name="pivotX" /> <attr name="pivotY" /> </declare-styleable> - + <declare-styleable name="ScaleAnimation"> <attr name="fromXScale" format="float" /> <attr name="toXScale" format="float" /> @@ -2472,14 +2550,14 @@ <attr name="pivotX" /> <attr name="pivotY" /> </declare-styleable> - + <declare-styleable name="TranslateAnimation"> <attr name="fromXDelta" format="float|fraction" /> <attr name="toXDelta" format="float|fraction" /> <attr name="fromYDelta" format="float|fraction" /> <attr name="toYDelta" format="float|fraction" /> </declare-styleable> - + <declare-styleable name="AlphaAnimation"> <attr name="fromAlpha" format="float" /> <attr name="toAlpha" format="float" /> @@ -2541,12 +2619,12 @@ <!-- This is the amount of deceleration to add when easing in. --> <attr name="factor" format="float" /> </declare-styleable> - + <declare-styleable name="DecelerateInterpolator"> <!-- This is the amount of acceleration to add when easing out. --> <attr name="factor" /> </declare-styleable> - + <declare-styleable name="CycleInterpolator"> <attr name="cycles" format="float" /> </declare-styleable> @@ -2656,18 +2734,18 @@ </declare-styleable> <!-- State array representing an expandable list child's indicator. --> <declare-styleable name="ExpandableListChildIndicatorState"> - <!-- State identifier indicating the child is the last child within its group. --> + <!-- State identifier indicating the child is the last child within its group. --> <attr name="state_last" /> </declare-styleable> <!-- State array representing an expandable list group's indicator. --> <declare-styleable name="ExpandableListGroupIndicatorState"> - <!-- State identifier indicating the group is expanded. --> + <!-- State identifier indicating the group is expanded. --> <attr name="state_expanded" format="boolean" /> - <!-- State identifier indicating the group is empty (has no children). --> + <!-- State identifier indicating the group is empty (has no children). --> <attr name="state_empty" format="boolean" /> </declare-styleable> <declare-styleable name="PopupWindowBackgroundState"> - <!-- State identifier indicating the popup will be above the anchor. --> + <!-- State identifier indicating the popup will be above the anchor. --> <attr name="state_above_anchor" format="boolean" /> </declare-styleable> @@ -2677,7 +2755,7 @@ <eat-comment /> <!-- Searchable activities and applications must provide search configuration information - in an XML file, typically called searchable.xml. This file is referenced in your manifest. + in an XML file, typically called searchable.xml. This file is referenced in your manifest. For a more in-depth discussion of search configuration, please refer to {@link android.app.SearchManager}. --> <declare-styleable name="Searchable"> @@ -2686,29 +2764,29 @@ plain text. This is a reference to a drawable (icon) resource. <i>Optional attribute.</i> --> <attr name="icon" /> - <!-- This is the user-displayed name of the searchable activity. <i>Required + <!-- This is the user-displayed name of the searchable activity. <i>Required attribute.</i> --> <attr name="label" /> - <!-- If supplied, this string will be displayed as a hint to the user. <i>Optional + <!-- If supplied, this string will be displayed as a hint to the user. <i>Optional attribute.</i> --> <attr name="hint" /> <!-- If supplied, this string will be displayed as the text of the "Search" button. - <i>Optional attribute.</i> + <i>Optional attribute.</i> {@deprecated This will create a non-standard UI appearance, because the search bar UI is changing to use only icons for its buttons.}--> <attr name="searchButtonText" format="string" /> <attr name="inputType" /> <attr name="imeOptions" /> - + <!-- Additional features are controlled by mode bits in this field. Omitting - this field, or setting to zero, provides default behavior. <i>Optional attribute.</i> + this field, or setting to zero, provides default behavior. <i>Optional attribute.</i> --> <attr name="searchMode"> <!-- If set, this flag enables the display of the search target (label) within the search bar. If neither bad mode is selected, no badge will be shown. --> <flag name="showSearchLabelAsBadge" value="0x04" /> <!-- If set, this flag enables the display of the search target (icon) within the - search bar. (Note, overrides showSearchLabel) If neither bad mode is selected, + search bar. (Note, overrides showSearchLabel) If neither bad mode is selected, no badge will be shown.--> <flag name="showSearchIconAsBadge" value="0x08" /> <!-- If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA to @@ -2722,11 +2800,11 @@ values are not suitable for user inspection and editing. --> <flag name="queryRewriteFromText" value="0x20" /> </attr> - + <!-- Voice search features are controlled by mode bits in this field. Omitting this field, or setting to zero, provides default behavior. If showVoiceSearchButton is set, then launchWebSearch or launchRecognizer must - also be set. <i>Optional attribute.</i> + also be set. <i>Optional attribute.</i> --> <attr name="voiceSearchMode"> <!-- If set, display a voice search button. This only takes effect if voice search is @@ -2745,9 +2823,9 @@ </attr> <!-- If provided, this specifies the language model that should be used by the - voice recognition system. See - {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL } for more information. - If not provided, the default value + voice recognition system. See + {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL } for more information. + If not provided, the default value {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM } will be used. --> <attr name="voiceLanguageModel" format="string" /> <!-- If provided, this specifies a prompt that will be displayed during voice input. --> @@ -2757,14 +2835,14 @@ <attr name="voiceLanguage" format="string" /> <!-- If provided, enforces the maximum number of results to return, including the "best" result which will always be provided as the SEARCH intent's primary query. Must be one - or greater. If not provided, the recognizer will choose how many results to return. + or greater. If not provided, the recognizer will choose how many results to return. --> <attr name="voiceMaxResults" format="integer" /> <!-- If provided, this is the trigger indicating that the searchable activity provides suggestions as well. The value must be a fully-qualified content provider - authority (e.g. "com.example.android.apis.SuggestionProvider") and should match the - "android:authorities" tag in your content provider's manifest entry. <i>Optional + authority (e.g. "com.example.android.apis.SuggestionProvider") and should match the + "android:authorities" tag in your content provider's manifest entry. <i>Optional attribute.</i> --> <attr name="searchSuggestAuthority" format="string" /> <!-- If provided, this will be inserted in the suggestions query Uri, after the authority @@ -2772,31 +2850,44 @@ --> <attr name="searchSuggestPath" format="string" /> <!-- If provided, suggestion queries will be passed into your query function - as the <i>selection</i> parameter. Typically this will be a WHERE clause for your - database, and will contain a single question mark, which represents the actual query + as the <i>selection</i> parameter. Typically this will be a WHERE clause for your + database, and will contain a single question mark, which represents the actual query string that has been typed by the user. If not provided, then the user query text - will be appended to the query Uri (after an additional "/".) <i>Optional + will be appended to the query Uri (after an additional "/".) <i>Optional attribute.</i> --> <attr name="searchSuggestSelection" format="string" /> - <!-- If provided, and not overridden by an action in the selected suggestion, this + <!-- If provided, and not overridden by an action in the selected suggestion, this string will be placed in the action field of the {@link android.content.Intent Intent} when the user clicks a suggestion. <i>Optional attribute.</i> --> <attr name="searchSuggestIntentAction" format="string" /> - <!-- If provided, and not overridden by an action in the selected suggestion, this - string will be placed in the data field of the {@link android.content.Intent Intent} + <!-- If provided, and not overridden by an action in the selected suggestion, this + string will be placed in the data field of the {@link android.content.Intent Intent} when the user clicks a suggestion. <i>Optional attribute.</i> --> <attr name="searchSuggestIntentData" format="string" /> - + <!-- If provided, this is the minimum number of characters needed to trigger search suggestions. The default value is 0. <i>Optional attribute.</i> --> <attr name="searchSuggestThreshold" format="integer" /> - + <!-- If provided and <code>true</code>, this searchable activity will be included in any global lists of search targets. The default value is <code>false</code>. <i>Optional attribute.</i>. --> <attr name="includeInGlobalSearch" format="boolean" /> + + <!-- If provided and <code>true</code>, this searchable activity will be invoked for all + queries in a particular session. If set to <code>false</code> and the activity + returned zero results for a query, it will not be invoked again in that session for + supersets of that zero-results query. For example, if the activity returned zero + results for "bo", it would not be queried again for "bob". + The default value is <code>false</code>. <i>Optional attribute.</i>. --> + <attr name="queryAfterZeroResults" format="boolean" /> + <!-- If provided, this string will be used to describe the searchable item in the + searchable items settings within system search settings. <i>Optional + attribute.</i> --> + <attr name="searchSettingsDescription" format="string" /> + </declare-styleable> <!-- In order to process special action keys during search, you must define them using @@ -2806,70 +2897,70 @@ <declare-styleable name="SearchableActionKey"> <!-- This attribute denotes the action key you wish to respond to. Note that not all action keys are actually supported using this mechanism, as many of them are - used for typing, navigation, or system functions. This will be added to the + used for typing, navigation, or system functions. This will be added to the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your - searchable activity. To examine the key code, use + searchable activity. To examine the key code, use {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}. - <p>Note, in addition to the keycode, you must also provide one or more of the action + <p>Note, in addition to the keycode, you must also provide one or more of the action specifier attributes. <i>Required attribute.</i> --> <attr name="keycode" /> - + <!-- If you wish to handle an action key during normal search query entry, you - must define an action string here. This will be added to the + must define an action string here. This will be added to the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your - searchable activity. To examine the string, use + searchable activity. To examine the string, use {@link android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. <i>Optional attribute.</i> --> <attr name="queryActionMsg" format="string" /> - + <!-- If you wish to handle an action key while a suggestion is being displayed <i>and selected</i>, there are two ways to handle this. If <i>all</i> of your suggestions - can handle the action key, you can simply define the action message using this - attribute. This will be added to the + can handle the action key, you can simply define the action message using this + attribute. This will be added to the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your - searchable activity. To examine the string, use + searchable activity. To examine the string, use {@link android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. <i>Optional attribute.</i> --> <attr name="suggestActionMsg" format="string" /> - + <!-- If you wish to handle an action key while a suggestion is being displayed <i>and - selected</i>, but you do not wish to enable this action key for every suggestion, + selected</i>, but you do not wish to enable this action key for every suggestion, then you can use this attribute to control it on a suggestion-by-suggestion basis. - First, you must define a column (and name it here) where your suggestions will include + First, you must define a column (and name it here) where your suggestions will include the action string. Then, in your content provider, you must provide this column, and when desired, provide data in this column. - The search manager will look at your suggestion cursor, using the string - provided here in order to select a column, and will use that to select a string from - the cursor. That string will be added to the - {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to - your searchable activity. To examine the string, use - {@link android.content.Intent#getStringExtra + The search manager will look at your suggestion cursor, using the string + provided here in order to select a column, and will use that to select a string from + the cursor. That string will be added to the + {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to + your searchable activity. To examine the string, use + {@link android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. <i>If the data does not exist for the selection suggestion, the action key will be ignored.</i><i>Optional attribute.</i> --> <attr name="suggestActionMsgColumn" format="string" /> </declare-styleable> - + <!-- ***************************************************************** --> <!-- Support for MapView. --> <!-- ***************************************************************** --> <eat-comment /> - + <!-- The set of attributes for a MapView. --> <declare-styleable name="MapView"> <!-- Value is a string that specifies the Maps API Key to use. --> <attr name="apiKey" format="string" /> </declare-styleable> - + <!-- **************************************************************** --> <!-- Menu XML inflation. --> <!-- **************************************************************** --> <eat-comment /> - + <!-- Base attributes that are available to all Menu objects. --> <declare-styleable name="Menu"> </declare-styleable> - + <!-- Base attributes that are available to all groups. --> <declare-styleable name="MenuGroup"> @@ -2888,11 +2979,11 @@ <!-- Items are alternative actions. --> <enum name="alternative" value="0x00040000" /> </attr> - + <!-- The order within the category applied to all items within this group. (This will be or'ed with the category attribute.) --> <attr name="orderInCategory" format="integer" /> - + <!-- Whether the items are capable of displaying a check mark. --> <attr name="checkableBehavior"> <!-- The items are not checkable. --> @@ -2903,7 +2994,7 @@ this group. --> <enum name="single" value="2" /> </attr> - + <!-- Whether the items are shown/visible. --> <attr name="visible" /> @@ -2917,7 +3008,7 @@ <!-- The ID of the item. --> <attr name="id" /> - + <!-- The category applied to the item. (This will be or'ed with the orderInCategory attribute.) --> <attr name="menuCategory" /> @@ -2928,15 +3019,15 @@ <!-- The title associated with the item. --> <attr name="title" format="string" /> - + <!-- The condensed title associated with the item. This is used in situations where the normal title may be too long to be displayed. --> <attr name="titleCondensed" format="string" /> <!-- The icon associated with this item. This icon will not always be shown, so - the title should be sufficient in describing this item. --> + the title should be sufficient in describing this item. --> <attr name="icon" /> - + <!-- The alphabetic shortcut key. This is the shortcut when using a keyboard with alphabetic keys. --> <attr name="alphabeticShortcut" format="string" /> @@ -2944,14 +3035,14 @@ <!-- The numeric shortcut key. This is the shortcut when using a numeric (e.g., 12-key) keyboard. --> <attr name="numericShortcut" format="string" /> - + <!-- Whether the item is capable of displaying a check mark. --> <attr name="checkable" format="boolean" /> - - <!-- Whether the item is checked. Note that you must first have enabled checking with + + <!-- Whether the item is checked. Note that you must first have enabled checking with the checkable attribute or else the check mark will not appear. --> <attr name="checked" /> - + <!-- Whether the item is shown/visible. --> <attr name="visible" /> @@ -2977,7 +3068,7 @@ with a View's attributes. Some subclasses (e.g., EditTextPreference) proxy all attributes to its EditText widget. --> <eat-comment /> - + <!-- Base attributes available to Preference. --> <declare-styleable name="Preference"> <!-- The key to store the Preference value. --> @@ -3106,7 +3197,7 @@ it had previously been shown. --> <attr name="imeExtractExitAnimation" format="reference" /> </declare-styleable> - + <declare-styleable name="KeyboardView"> <!-- Default KeyboardView style. --> <attr name="keyboardViewStyle" format="reference" /> @@ -3139,13 +3230,13 @@ <!-- Layout resource for popup keyboards --> <attr name="popupLayout" format="reference" /> - + <attr name="shadowColor" /> <attr name="shadowRadius" /> </declare-styleable> - + <declare-styleable name="KeyboardViewPreviewState"> - <!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView} + <!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView} key preview background --> <attr name="state_long_pressable" format="boolean" /> </declare-styleable> @@ -3228,7 +3319,7 @@ If not supplied, then no activity will be launched. --> <attr name="configure" format="string" /> </declare-styleable> - - + + </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 7b48267045fa..7571e2453627 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" /> @@ -570,6 +573,15 @@ <!-- Application's requirement for five way navigation --> <attr name="reqFiveWayNav" format="boolean" /> + <!-- The name of the class implementing <code>BackupAgent</code> to manage + backup and restore of the application's settings to external storage. --> + <attr name="backupAgent" format="string" /> + + <!-- Whether the application allows its data to be backed up at all. This + attribute defaults to 'true': unless the application opts out, the + user will be able to back up its data to desktop storage. --> + <attr name="allowBackup" format="boolean" /> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -587,7 +599,8 @@ {@link #AndroidManifestUsesPermission uses-permission}, {@link #AndroidManifestUsesConfiguration uses-configuration}, {@link #AndroidManifestApplication application}, - {@link #AndroidManifestInstrumentation instrumentation}. --> + {@link #AndroidManifestInstrumentation instrumentation}, + {@link #AndroidManifestUsesFeature uses-feature}. --> <declare-styleable name="AndroidManifest"> <attr name="versionCode" /> <attr name="versionName" /> @@ -643,6 +656,8 @@ <attr name="manageSpaceActivity" /> <attr name="allowClearUserData" /> <attr name="testOnly" /> + <attr name="backupAgent" /> + <attr name="allowBackup" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be @@ -754,6 +769,22 @@ <attr name="reqFiveWayNav" /> </declare-styleable> + <!-- The <code>uses-feature</code> tag specifies + a specific feature used by the application. + For example an application might specify that it requires + specific version of open gl. Multiple such attribute + values can be specified by the application. + + <p>This appears as a child tag of the root + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestUsesFeature" parent="AndroidManifest"> + <!-- The GLES driver version number needed by an application. + The higher 16 bits represent the major number and the lower 16 bits + represent the minor number. For example for GL 1.2 referring to + 0x00000102, the actual value should be set as 0x00010002. --> + <attr name="glEsVersion" format="integer"/> + </declare-styleable> + <!-- The <code>uses-sdk</code> tag describes the SDK features that the containing package must be running on to operate correctly. @@ -799,10 +830,68 @@ <!-- The <code>supports-density</code> specifies a screen density that this package supports. Application can specify multiple densities it supports. <p>This appears as a child tag of the - {@link #AndroidManifestApplication application} tag. --> - <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifestApplication"> - <!-- Required value of the density in dip (device independent pixel). --> - <attr name="density" format="integer" /> + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest"> + <!-- 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 + other than 320x480 dip. + <p>This appears as a child tag of the + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestExpandable" parent="AndroidManifest"> </declare-styleable> <!-- The <code>provider</code> tag declares a diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 8150067fec6a..d284d0f44498 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -18,7 +18,7 @@ */ --> <resources> - <drawable name="screen_background_light">#ffffffff</drawable> + <drawable name="screen_background_light">#fff9f9f9</drawable> <drawable name="screen_background_dark">#ff1a1a1a</drawable> <drawable name="status_bar_closed_default_background">#ff000000</drawable> <drawable name="status_bar_opened_default_background">#ff000000</drawable> @@ -37,7 +37,7 @@ <color name="dim_foreground_dark_inverse">#323232</color> <color name="dim_foreground_dark_inverse_disabled">#80323232</color> <color name="hint_foreground_dark">#808080</color> - <color name="background_light">#ffffffff</color> + <color name="background_light">#fff9f9f9</color> <color name="bright_foreground_light">#ff000000</color> <color name="bright_foreground_light_inverse">#ffffffff</color> <color name="bright_foreground_light_disabled">#80000000</color> @@ -58,7 +58,7 @@ <drawable name="editbox_dropdown_dark_frame">@drawable/editbox_dropdown_background_dark</drawable> <drawable name="editbox_dropdown_light_frame">@drawable/editbox_dropdown_background</drawable> - <drawable name="input_method_fullscreen_background">#ffffffff</drawable> + <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable> <!-- For date picker widget --> <drawable name="selected_day_background">#ff0092f4</drawable> @@ -73,5 +73,9 @@ <color name="perms_normal_grp_color">#eeeeee</color> <color name="perms_normal_perm_color">#c0c0c0</color> + <!-- For search-related UIs --> + <color name="search_url_text">#7fa87f</color> + <color name="search_widget_corpus_item_background">@android:color/lighter_gray</color> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 83ac8e27e146..7215685a3d46 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -34,4 +34,8 @@ <!-- The duration (in milliseconds) of a long animation. --> <integer name="config_longAnimTime">300</integer> + + <!-- Flag indicating whether Last Name comes before First Name. + This becomes true in Japan, for example.--> + <bool name="config_lastname_comes_before_firstname">false</bool> </resources> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml new file mode 100644 index 000000000000..286cc0e554f2 --- /dev/null +++ b/core/res/res/values/donottranslate-cldr.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">January</string> + <string name="month_long_standalone_february">February</string> + <string name="month_long_standalone_march">March</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">May</string> + <string name="month_long_standalone_june">June</string> + <string name="month_long_standalone_july">July</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">October</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">December</string> + + <string name="month_long_january">January</string> + <string name="month_long_february">February</string> + <string name="month_long_march">March</string> + <string name="month_long_april">April</string> + <string name="month_long_may">May</string> + <string name="month_long_june">June</string> + <string name="month_long_july">July</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">October</string> + <string name="month_long_november">November</string> + <string name="month_long_december">December</string> + + <string name="month_medium_january">Jan</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mar</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">May</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">Oct</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dec</string> + + <string name="month_shortest_january">J</string> + <string name="month_shortest_february">F</string> + <string name="month_shortest_march">M</string> + <string name="month_shortest_april">A</string> + <string name="month_shortest_may">M</string> + <string name="month_shortest_june">J</string> + <string name="month_shortest_july">J</string> + <string name="month_shortest_august">A</string> + <string name="month_shortest_september">S</string> + <string name="month_shortest_october">O</string> + <string name="month_shortest_november">N</string> + <string name="month_shortest_december">D</string> + + <string name="day_of_week_long_sunday">Sunday</string> + <string name="day_of_week_long_monday">Monday</string> + <string name="day_of_week_long_tuesday">Tuesday</string> + <string name="day_of_week_long_wednesday">Wednesday</string> + <string name="day_of_week_long_thursday">Thursday</string> + <string name="day_of_week_long_friday">Friday</string> + <string name="day_of_week_long_saturday">Saturday</string> + + <string name="day_of_week_medium_sunday">Sun</string> + <string name="day_of_week_medium_monday">Mon</string> + <string name="day_of_week_medium_tuesday">Tue</string> + <string name="day_of_week_medium_wednesday">Wed</string> + <string name="day_of_week_medium_thursday">Thu</string> + <string name="day_of_week_medium_friday">Fri</string> + <string name="day_of_week_medium_saturday">Sat</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">Yesterday</string> + <string name="today">Today</string> + <string name="tomorrow">Tomorrow</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-m/%-e/%Y</string> + <string name="numeric_date_format">M/d/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%B %-e, %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%b %-e, %Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%b %-e, %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s – %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s/%3$s – %6$s, %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%2$s/%3$s/%4$s – %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s/%3$s, %5$s – %7$s/%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s – %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s – %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s – %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s</string> + <string name="same_month_md1_md2">%2$s %3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> + <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> +</resources> diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml new file mode 100644 index 000000000000..6def3bfdd1b0 --- /dev/null +++ b/core/res/res/values/donottranslate.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Default text encoding for WebSettings. --> + <string name="default_text_encoding">Latin-1</string> +</resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f90c6b82edf9..32c693757dcf 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1091,22 +1091,73 @@ =============================================================== --> <eat-comment /> - <public type="attr" name="tension" id="0x0101026a" /> - <public type="attr" name="extraTension" id="0x0101026b" /> - <public type="attr" name="density" id="0x0101026c" /> - <public type="attr" name="searchSuggestThreshold" id="0x0101026d" /> - <public type="attr" name="includeInGlobalSearch" id="0x0101026e" /> - <public type="attr" name="onClick" id="0x0101026f" /> - <public type="attr" name="targetSdkVersion" id="0x01010270" /> - <public type="attr" name="maxSdkVersion" id="0x01010271" /> - <public type="attr" name="testOnly" id="0x01010272" /> + <public type="attr" name="tension" id="0x0101026a" /> + <public type="attr" name="extraTension" /> + <public type="attr" name="density" /> + <public type="attr" name="searchSuggestThreshold" /> + <public type="attr" name="includeInGlobalSearch" /> + <public type="attr" name="onClick" /> + <public type="attr" name="targetSdkVersion" /> + <public type="attr" name="maxSdkVersion" /> + <public type="attr" name="testOnly" /> + <public type="attr" name="contentDescription" /> + <public type="attr" name="gestureStrokeWidth" /> + <public type="attr" name="gestureColor" /> + <public type="attr" name="uncertainGestureColor" /> + <public type="attr" name="fadeOffset" /> + <public type="attr" name="fadeDuration" /> + <public type="attr" name="gestureStrokeType" /> + <public type="attr" name="gestureStrokeLengthThreshold" /> + <public type="attr" name="gestureStrokeSquarenessThreshold" /> + <public type="attr" name="gestureStrokeAngleThreshold" /> + <public type="attr" name="eventsInterceptionEnabled" /> + <public type="attr" name="fadeEnabled" /> + <public type="attr" name="backupAgent" /> + <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 type="attr" name="progressBarStyleInverse" /> + <public type="attr" name="progressBarStyleSmallInverse" /> + <public type="attr" name="progressBarStyleLargeInverse" /> + <public type="attr" name="searchSettingsDescription" /> + <public type="attr" name="textColorPrimaryInverseDisableOnly" /> - <public type="anim" name="anticipate_interpolator" id="0x010a0007" /> - <public type="anim" name="overshoot_interpolator" id="0x010a0008" /> - <public type="anim" name="anticipate_overshoot_interpolator" id="0x010a0009" /> - <public type="anim" name="bounce_interpolator" id="0x010a000a" /> + <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" /> - <public type="drawable" name="stat_sys_vp_phone_call" id="0x0108022d" /> - <public type="drawable" name="stat_sys_vp_phone_call_on_hold" id="0x0108022e" /> + <public-padding type="id" name="donut_resource_pad" end="0x01020040" /> + + <public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" /> + <public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" /> + <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" /> + <public-padding type="style" name="donut_resource_pad" end="0x01030070" /> + + <public-padding type="string" name="donut_resource_pad" end="0x01040030" /> + + <public-padding type="dimen" name="donut_resource_pad" end="0x01050010" /> + <public-padding type="color" name="donut_resource_pad" end="0x01060030" /> + + <public-padding type="array" name="donut_resource_pad" end="0x01070010" /> + + <public type="drawable" name="stat_sys_vp_phone_call" /> + <public type="drawable" name="stat_sys_vp_phone_call_on_hold" /> + + <public-padding type="drawable" name="donut_resource_pad" end="0x010800d0" /> + + <public-padding type="layout" name="donut_resource_pad" end="0x01090020" /> + + <public type="anim" name="anticipate_interpolator" /> + <public type="anim" name="overshoot_interpolator" /> + <public type="anim" name="anticipate_overshoot_interpolator" /> + <public type="anim" name="bounce_interpolator" /> + <public type="anim" name="linear_interpolator" /> + + <public-padding type="anim" name="donut_resource_pad" end="0x010a0020" /> + + <public-padding type="integer" name="donut_resource_pad" end="0x010e0010" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 331ef1a0feaa..9b9ba681713a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -30,6 +30,11 @@ <string name="terabyteShort">TB</string> <!-- Suffix added to a number to signify size in petabytes. --> <string name="petabyteShort">PB</string> + <!-- Format string used to add a suffix like "KB" or "MB" to a number + to display a size in kilobytes, megabytes, or other size units. + Some languages (like French) will want to add a space between + the placeholders. --> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g><xliff:g id="unit" example="KB">%2$s</xliff:g></string> <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> @@ -148,6 +153,24 @@ <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD --> <string name="serviceClassPAD">PAD</string> + <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip /> + <!-- Default roaming indicator text --> + <string name="roamingText0">Roaming Indicator On</string> + <string name="roamingText1">Roaming Indicator Off</string> + <string name="roamingText2">Roaming Indicator Flashing</string> + <string name="roamingText3">Out of Neighborhood</string> + <string name="roamingText4">Out of Building</string> + <string name="roamingText5">Roaming - Preferred System</string> + <string name="roamingText6">Roaming - Available System</string> + <string name="roamingText7">Roaming - Alliance Partner</string> + <string name="roamingText8">Roaming - Premium Partner</string> + <string name="roamingText9">Roaming - Full Service Functionality</string> + <string name="roamingText10">Roaming - Partial Service Functionality</string> + <string name="roamingText11">Roaming Banner On</string> + <string name="roamingText12">Roaming Banner Off</string> + <string name="roamingTextSearching">Searching for Service</string> + + <!-- {0} is one of "bearerServiceCode*" {1} is dialing number @@ -205,6 +228,8 @@ <string name="httpErrorFileNotFound">The requested file was not found.</string> <!-- Displayed when a request failed because there are too many requests right now. --> <string name="httpErrorTooManyRequests">Too many requests are being processed. Try again later.</string> + <!-- Displayed a toast that a certificate is saved in the keystore --> + <string name="certificateSaved">The certificate is saved in the system\'s key store.</string> <!-- Sync notifications --> <skip /> <!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). --> @@ -438,13 +463,6 @@ forcibly restart other applications.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_setProcessForeground">keep from being stopped</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_setProcessForeground">Allows an application to make - any process run in the foreground, so it can\'t be killed. - Should never be needed for normal applications.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_forceBack">force application to close</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_forceBack">Allows an application to force any @@ -460,19 +478,18 @@ never normally need.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_addSystemService">publish low-level services</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_addSystemService">Allows application to publish - its own low-level system services. Malicious applications may hijack - the system, and steal or corrupt any data on it.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_shutdown">partial shutdown</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_shutdown">Puts the activity manager into a shutdown state. Does not perform a complete shutdown.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_stopAppSwitches">prevent app switches</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_stopAppSwitches">Prevents the user from switching to + another application.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_runSetActivityWatcher">monitor and control all application launching</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_runSetActivityWatcher">Allows an application to @@ -519,21 +536,17 @@ go to the background. Never needed for normal applications.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_fotaUpdate">automatically install system updates</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_fotaUpdate">Allows an application to receive - notifications about pending system updates and trigger their - installation. Malicious applications may use this to corrupt the system - with unauthorized updates, or generally interfere with the update - process.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_batteryStats">modify battery statistics</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_batteryStats">Allows the modification of collected battery statistics. Not for use by normal applications.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_backup">control system backup and restore</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_backup">Allows the application to control the system's backup and restore mechanism. Not for use by normal applications.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_internalSystemWindow">display unauthorized windows</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_internalSystemWindow">Allows the creation of @@ -763,13 +776,7 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_installLocationProvider">Create mock location sources for testing. Malicious applications can use this to override the location and/or status returned by real - location sources such as GPS or Network providers.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_installLocationCollector">permission to install a location collector</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_installLocationCollector">Create mock location sources for testing. - Malicious applications can use this to monitor and report your location to an external source.</string> + location sources such as GPS or Network providers or monitor and report your location to an external source.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessFineLocation">fine (GPS) location</string> @@ -887,7 +894,6 @@ properties uploaded by the checkin service. Not for use by normal applications.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindGadget">choose widgets</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -1006,6 +1012,15 @@ configured Wi-Fi networks.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_changeWifiMulticastState">allow Wi-Fi Multicast + reception</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_changeWifiMulticastState">Allows an application to + receive packets not directly addressed to your device. This can be + useful when discovering services offered near by. It uses more power + than the non-multicast mode.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bluetoothAdmin">bluetooth administration</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bluetoothAdmin">Allows an application to configure @@ -1070,7 +1085,7 @@ user dictionary.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_sdcardWrite">write to SD card</string> + <string name="permlab_sdcardWrite">modify/delete SD card contents</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_sdcardWrite">Allows an application to write to the SD card.</string> @@ -1096,6 +1111,9 @@ <item>Custom</item> </string-array> + <!-- String which means the type "mobile phone". --> + <string name="mobileEmailTypeName">Mobile</string> + <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip /> <!-- Postal address types from android.provider.Contacts. This could be used when adding a new address for a contact, for example. --> <string-array name="postalAddressTypes"> @@ -1174,9 +1192,12 @@ <!-- On the unlock pattern screen, shown when the user enters the wrong lock pattern and must try again. --> <string name="lockscreen_pattern_wrong">Sorry, try again</string> - <!-- When the lock screen is showing and the phone plugged in, show the current - charge %. --> + <!-- When the lock screen is showing and the phone plugged in, and the battery + is not fully charged, show the current charge %. --> <string name="lockscreen_plugged_in">Charging (<xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>)</string> + <!-- When the lock screen is showing, the phone is plugged in and the battery is fully + charged, say that it is charged. --> + <string name="lockscreen_charged">Charged.</string> <!-- When the lock screen is showing and the battery is low, warn user to plug in the phone soon. --> @@ -1245,15 +1266,6 @@ <!-- Displayed to the user when unlocking the phone with a username and password fails. --> <string name="lockscreen_glogin_invalid_input">Invalid username or password.</string> - <!-- A format string for 12-hour time of day (example: "12:30 PM"). --> - <string name="status_bar_time_format">"<xliff:g id="hour" example="12">h</xliff:g>:<xliff:g id="minute" example="30">mm</xliff:g> <xliff:g id="ampm" example="AM">AA</xliff:g>"</string> - - <!-- A format string for 12-hour time of day, with lower-case "am" or "pm" (example: "12:30pm"). --> - <string name="hour_minute_ampm">"<xliff:g id="hour" example="12">%-l</xliff:g>:<xliff:g id="minute" example="30">%M</xliff:g><xliff:g id="ampm" example="am">%P</xliff:g>"</string> - - <!-- A format string for 12-hour time of day, with capital "AM" or "PM" (example: "12:30PM"). --> - <string name="hour_minute_cap_ampm">"<xliff:g id="hour" example="12">%-l</xliff:g>:<xliff:g id="minute" example="30">%M</xliff:g><xliff:g id="ampm" example="AM">%p</xliff:g>"</string> - <!-- A format string for 12-hour time of day, just the hour, not the minute, with lower-case "am" or "pm" (example: "3pm"). --> <string name="hour_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="pm">%P</xliff:g>"</string> @@ -1262,7 +1274,7 @@ <!-- The text for the button in the notification window-shade that clears all of the currently visible notifications. --> - <string name="status_bar_clear_all_button">Clear notifications</string> + <string name="status_bar_clear_all_button">Clear</string> <!-- The label in the bar at the top of the status bar when there are no notifications showing. --> @@ -1295,6 +1307,9 @@ <string name="battery_low_percent_format">less than <xliff:g id="number">%d%%</xliff:g> remaining.</string> + <!-- When the battery is low, this is the label of the button to go to the + power usage activity to find out what drained the battery. --> + <string name="battery_low_why">Why?</string> <!-- Title of the alert when something went wrong in the factory test. --> <string name="factorytest_failed">Factory test failed</string> @@ -1321,7 +1336,23 @@ <!-- Title of the WebView save password dialog. If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. --> <string name="save_password_label">Confirm</string> - + + <!-- Title of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> + <!-- Description of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permdesc_readHistoryBookmarks">Allows the application to read all + the URLs that the Browser has visited, and all of the Browser\'s bookmarks.</string> + <!-- Title of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permlab_writeHistoryBookmarks">write Browser\'s history and bookmarks</string> + <!-- Description of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permdesc_writeHistoryBookmarks">Allows an application to modify the + Browser\'s history or bookmarks stored on your phone. Malicious applications + can use this to erase or modify your Browser\'s data.</string> + <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. --> <string name="save_password_message">Do you want the browser to remember this password?</string> <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. --> @@ -1354,12 +1385,6 @@ It is also used by the home screen's search "widget". It should be short --> <string name="search_go">Search</string> - <!-- String used to display the date. This is shown instead of a date if the date is today's date. --> - <string name="today">Today</string> - <!-- String used to display the date. This is shown instead of a date if the date is yesterday's date. --> - <string name="yesterday">Yesterday</string> - <!-- String used to display the date. This is shown instead of a date if the date is tomorrow's date. --> - <string name="tomorrow">Tomorrow</string> <!-- String used to display the date. This is the string to say something happened 1 month ago. --> <string name="oneMonthDurationPast">1 month ago</string> <!-- String used to display the date. This is the string to say something happened more than 1 month ago. --> @@ -1493,21 +1518,6 @@ <!-- Appened to express the value is this unit of time. --> <string name="years">years</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="sunday">Sunday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="monday">Monday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="tuesday">Tuesday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="wednesday">Wednesday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="thursday">Thursday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="friday">Friday</string> - <!-- Used in the list of which days of the week a calendar event recurrs on --> - <string name="saturday">Saturday</string> - <!-- Calendar spinner item, to select that an event recurs every weekday. --> <string name="every_weekday">"Every weekday (Mon\u2013Fri)"</string> <!-- Calendar spinner item, to select that an event recurs every day. --> @@ -1530,95 +1540,12 @@ <string name="VideoView_error_button">OK</string> - <!-- AM - as in morning - as in 10:30 AM --> - <string name="am">"AM"</string> - - <!-- PM - as in afternoon - as in 10:30 PM --> - <string name="pm">"PM"</string> - - - <!-- Numeric form of the day. Example: "12/31/2007" --> - <string name="numeric_date">"<xliff:g id="month" example="12">%m</xliff:g>/<xliff:g id="day" example="31">%d</xliff:g>/<xliff:g id="year" example="2008">%Y</xliff:g>"</string> - - <!-- Format indicating a range of time, from a time on one day to a time on another day. - Example: "Mon, Dec 31, 2007, 8am - Tue, Jan 1, 2008, 5pm" --> - <string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="weekday1" example="Monday">%1$s</xliff:g>, <xliff:g id="date1" example="December 31, 2007">%2$s</xliff:g>, <xliff:g id="time1" example="8am">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Tuesday">%4$s</xliff:g>, <xliff:g id="date2" example="January 1, 2008">%5$s</xliff:g>, <xliff:g id="time2" example="5pm">%6$s</xliff:g>"</string> - - <!-- Format indicating a range of dates, from one date to another. - Example: "Mon, Dec 31, 2007 - Tue, Jan 1, 2008" --> - <string name="wday1_date1_wday2_date2">"<xliff:g id="weekday1" example="Monday">%1$s</xliff:g>, <xliff:g id="date1" example="Dec 31, 2007">%2$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Thursday">%4$s</xliff:g>, <xliff:g id="date2" example="Jan 1, 2008">%5$s</xliff:g>"</string> - <!-- Format indicating a range of time, from a time on one day to a time on another day. - Example: "Dec 31, 2007, 8am - Jan 1, 2008, 5pm" --> - <string name="date1_time1_date2_time2">"<xliff:g id="date1" example="Dec 31, 2007">%2$s</xliff:g>, <xliff:g id="time1" example="8am">%3$s</xliff:g> \u2013 <xliff:g id="date2" example="Jan 1, 2008">%5$s</xliff:g>, <xliff:g id="time2" example="5pm">%6$s</xliff:g>"</string> - - <!-- Format indicating a range of dates, from one date to another. - Example: "Dec 31, 2007 - Jan 1, 2008" --> - <string name="date1_date2">"<xliff:g id="date1" example="Dec 31, 2007">%2$s</xliff:g> \u2013 <xliff:g id="date2" example="Jan 1, 2008">%5$s</xliff:g>"</string> - - <!-- Format indicating a range of times, from one time to another. - Example: "10:00 - 11:00 am" --> - <string name="time1_time2">"<xliff:g id="time1" example="10:00">%1$s</xliff:g> \u2013 <xliff:g id="time2" example="11:00">%2$s</xliff:g>"</string> - - <!-- Format indicating a range of times on a particular date. - Example: "8:00 - 11:00 am, Mon, Dec 31, 2007" --> - <string name="time_wday_date">"<xliff:g id="time_range" example="8:00 - 11:00 am">%1$s</xliff:g>, <xliff:g id="weekday" example="Mon">%2$s</xliff:g>, <xliff:g id="date" example="Dec 31, 2007">%3$s</xliff:g>"</string> - - <!-- Format indicating a weekday and date. - Example: "Mon, Dec 31, 2007" --> - <string name="wday_date">"<xliff:g id="weekday" example="Monday">%2$s</xliff:g>, <xliff:g id="date" example="Dec 31, 2007">%3$s</xliff:g>"</string> - - <!-- Format indicating a range of times on a particular date. - Example: "8:00 - 11:00 am, Dec 31, 2007" --> - <string name="time_date">"<xliff:g id="time_range" example="8:00 - 11:00 am">%1$s</xliff:g>, <xliff:g id="date" example="Dec 31, 2007">%3$s</xliff:g>"</string> - - <!-- Format indicating a specific date and time. - Example: "Dec 31, 2007, 11:00 am" --> - <string name="date_time">"<xliff:g id="date" example="Dec 31, 2007">%1$s</xliff:g>, <xliff:g id="time" example="11:00 am">%2$s</xliff:g>"</string> <!-- Format indicating a relative expression and time. Example: "4 hours ago, 11:00 am" --> <string name="relative_time">"<xliff:g id="date" example="4 hours ago">%1$s</xliff:g>, <xliff:g id="time" example="11:00 am">%2$s</xliff:g>"</string> - <!-- Format indicating a range of times on a particular day of the week. - Example: "8:00 - 11:00 am, Mon" --> - <string name="time_wday">"<xliff:g id="time_range" example="8:00 - 11:00 am">%1$s</xliff:g>, <xliff:g id="weekday" example="Mon">%2$s</xliff:g>"</string> - - <!-- Date format string used in contexts where the user has said they - want the month first, as used in the USA, with the month fully - spelled out. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. --> - <string name="full_date_month_first" format="date"><xliff:g id="month" example="December">MMMM</xliff:g> <xliff:g id="day" example="31">d</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> - - <!-- Date format string used in contexts where the user has said they - want the day of the month first, as used in Europe, with the month - fully spelled out. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. --> - <string name="full_date_day_first" format="date"><xliff:g id="day" example="31">d</xliff:g> <xliff:g id="month" example="December">MMMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> - - <!-- Date format string used in contexts where the user has said they - want the month first, as used in the USA, with the month - abbreviated. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. --> - <string name="medium_date_month_first" format="date"><xliff:g id="month" example="Dec.">MMM</xliff:g> <xliff:g id="day" example="31">d</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> - - <!-- Date format string used in contexts where the user has said they - want the day of the month first, as used in Europe, with the month - abbreviated. You can remove the comma or add a period, - or make other punctuation changes appropriate for your locale. --> - <string name="medium_date_day_first" format="date"><xliff:g id="day" example="31">d</xliff:g> <xliff:g id="month" example="December">MMM</xliff:g>, <xliff:g id="year" example="1972">yyyy</xliff:g></string> - - <!-- Time format string used in the status bar when the user has said they - want a 12-hour clock with AM and PM. - You can remove the colon - or make other punctuation changes appropriate for your locale. --> - <string name="twelve_hour_time_format" format="date"><xliff:g id="hour" example="11">h</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g> <xliff:g id="ampm" example="AM">a</xliff:g></string> - - <!-- Time format string used in the status bar when the user has said they - want a 24-hour clock. - You can remove the colon - or make other punctuation changes appropriate for your locale. --> - <string name="twenty_four_hour_time_format" format="date"><xliff:g id="hour" example="23">HH</xliff:g>:<xliff:g id="minute" example="59">mm</xliff:g></string> <!-- Quoted name for 12pm, lowercase --> <string name="noon">"noon"</string> @@ -1629,433 +1556,15 @@ <!-- Quoted name for 12am, uppercase first letter --> <string name="Midnight">"Midnight"</string> - <!-- Date format for month and day of month. - Example: "October 9". --> - <string name="month_day">"<xliff:g id="month" example="October">%B</xliff:g> <xliff:g id="day" example="9">%-d</xliff:g>"</string> - - <!-- Date format for month alone. - Example: "October" --> - <string name="month">"<xliff:g id="month" example="October">%B</xliff:g>"</string> - - <!-- Date format for month, day, and year. - Example: "October 9, 2007" --> - <string name="month_day_year">"<xliff:g id="month" example="October">%B</xliff:g> <xliff:g id="day" example="9">%-d</xliff:g>, <xliff:g id="year" example="2007">%Y</xliff:g>"</string> - - <!-- Date format for month and year. - Example: "October 2007" --> - <string name="month_year">"<xliff:g id="month" example="October">%B</xliff:g> <xliff:g id="year" example="2007">%Y</xliff:g>"</string> - - <!-- A format string for 24-hour time of day (example "23:59"). --> - <string name="time_of_day">"<xliff:g id="hour" example="23">%H</xliff:g>:<xliff:g id="minute" example="59">%M</xliff:g>:<xliff:g id="second" example="59">%S</xliff:g>"</string> - - <!-- Format string for date and 24-hour time of day. - Example: 23:59:15 Jan 31 2008 --> - <string name="date_and_time">"<xliff:g id="hour" example="23">%H</xliff:g>:<xliff:g id="minute" example="59">%M</xliff:g>:<xliff:g id="second" example="59">%S</xliff:g> <xliff:g id="month" example="Jan">%B</xliff:g> <xliff:g id="day" example="31">%-d</xliff:g>, <xliff:g id="year" example="2008">%Y</xliff:g>"</string> - - <!-- Format indicating a range of dates in the same year. - Example: "Oct 31 - Nov 3" --> - <string name="same_year_md1_md2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of dates in the same year, with weekday. - Example: "Wed, Oct 31 - Sat, Nov 3" --> - <string name="same_year_wday1_md1_wday2_md2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of dates in the same year. - Example: "Oct 31 - Nov 3, 2007" --> - <string name="same_year_mdy1_mdy2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of dates in the same year, with weekdays. - Example: "Wed, Oct 31 - Sat, Nov 3, 2007" --> - <string name="same_year_wday1_mdy1_wday2_mdy2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of time from a time on one day to a time on another. - Example: "Oct 31, 8:00am - Nov 3, 5:00pm" --> - <string name="same_year_md1_time1_md2_time2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of time from a time on one day to a time on another, with weekdays. - Example: "Wed, Oct 31, 8:00am - Sat, Nov 3, 5:00pm" --> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id ="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of time from a time on one day to a time on another, with years and weekdays. - Example: "Oct 31, 2007, 8:00am - Nov 3, 2007, 5:00pm" --> - <string name="same_year_mdy1_time1_mdy2_time2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of time from a time on one day to a time on another. - Example: "Wed, Oct 31, 2007, 8:00am - Sat, Nov 3, 2007, 5:00pm" --> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - - <!-- Format indicating a range of (numeric) dates. - Example: "10/31 - 11/3" --> - <string name="numeric_md1_md2">"<xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates. - Example: "Wed, 10/31 - Sat, 11/3" --> - <string name="numeric_wday1_md1_wday2_md2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates. - Example: "10/31/2007 - 11/3/2007" --> - <string name="numeric_mdy1_mdy2">"<xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>/<xliff:g id="year1" example="2007">%4$s</xliff:g> \u2013 <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>/<xliff:g id="year2" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates. - Example: "Wed, 10/31/2007 - Sat, 11/3/2007" --> - <string name="numeric_wday1_mdy1_wday2_mdy2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>/<xliff:g id="year1" example="2007">%4$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>/<xliff:g id="year2" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates and times. - Example: "10/31, 8:00am - 11/3, 5:00pm" --> - <string name="numeric_md1_time1_md2_time2">"<xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates and times. - Example: "Wed, 10/31, 8:00am - Sat, 11/3, 5:00pm" --> - <string name="numeric_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates and times. - Example: "10/31/2007, 8:00am - 11/3/2007, 5:00pm" --> - <string name="numeric_mdy1_time1_mdy2_time2">"<xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>/<xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>/<xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of (numeric) dates and times. - Example: "Wed, 10/31/2007, 8:00am - Sat, 11/3/2007, 5:00pm" --> - <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="10">%2$s</xliff:g>/<xliff:g id="day1" example="31">%3$s</xliff:g>/<xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="11">%7$s</xliff:g>/<xliff:g id="day2" example="30">%8$s</xliff:g>/<xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - - <!-- Format indicating a range of dates. - Example: "Oct 9 - 10" --> - <string name="same_month_md1_md2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="day2" example="3">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of dates. - Example: "Tue, Oct 9 - Wed, Oct 10" --> - <string name="same_month_wday1_md1_wday2_md2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>"</string> - - <!-- Format indicating a range of dates. - Example: "Oct 9 - 10, 2007" --> - <string name="same_month_mdy1_mdy2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g> \u2013 <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of dates. - Example: "Tue, Oct 9, 2007 - Wed, Oct 10, 2007" --> - <string name="same_month_wday1_mdy1_wday2_mdy2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="year1" example="2007">%4$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>"</string> - - <!-- Format indicating a range of dates and times. - Example: "Oct 9, 8:00am - Oct 10, 5:00pm" --> - <string name="same_month_md1_time1_md2_time2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of dates and times. - Example: "Tue, Oct 9, 8:00am - Wed, Oct 10, 5:00pm" --> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of dates and times. - Example: "Oct 9, 2007, 8:00am - Oct 10, 2007, 5:00pm" --> - <string name="same_month_mdy1_time1_mdy2_time2">"<xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format indicating a range of dates and times. - Example: "Tue, Oct 9, 2007, 8:00am - Wed, Oct 10, 2007, 5:00pm" --> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">"<xliff:g id="weekday1" example="Wed">%1$s</xliff:g>, <xliff:g id="month1" example="Oct">%2$s</xliff:g> <xliff:g id="day1" example="31">%3$s</xliff:g>, <xliff:g id="year1" example="2007">%4$s</xliff:g>, <xliff:g id="time1" example="8:00am">%5$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Sat">%6$s</xliff:g>, <xliff:g id="month2" example="Nov">%7$s</xliff:g> <xliff:g id="day2" example="3">%8$s</xliff:g>, <xliff:g id="year2" example="2007">%9$s</xliff:g>, <xliff:g id="time2" example="5:00pm">%10$s</xliff:g>"</string> - - <!-- Format string for abbreviated month, day, and year. - Example: "Oct 9, 2007" --> - <string name="abbrev_month_day_year">"<xliff:g id="month" example="Oct">%b</xliff:g> <xliff:g id="day" example="9">%-d</xliff:g>, <xliff:g id="year" example="2007">%Y</xliff:g>"</string> - <!-- Format string for abbreviated month and year. - Example: "Oct 2007" --> - <string name="abbrev_month_year">"<xliff:g id="month" example="Oct">%b</xliff:g> <xliff:g id="year" example="2007">%Y</xliff:g>"</string> - <!-- Format string for abbreviated month and day. - Example: "Oct 9" --> - <string name="abbrev_month_day">"<xliff:g id="month" example="Oct">%b</xliff:g> <xliff:g id="day" example="31">%-d</xliff:g>"</string> - <!-- Format string for abbreviated month alone. - Example: "Oct" --> - <string name="abbrev_month">"<xliff:g id="month" example="Oct">%b</xliff:g>"</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_sunday">Sunday</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_monday">Monday</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_tuesday">Tuesday</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_wednesday">Wednesday</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_thursday">Thursday</string> - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_friday">Friday</string> - - <!-- The full spelled out version of the day of the week. --> - <string name="day_of_week_long_saturday">Saturday</string> - - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Sun" stands for Sunday --> - <string name="day_of_week_medium_sunday">Sun</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Mon" stands for Monday --> - <string name="day_of_week_medium_monday">Mon</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Tue" stands for Tuesday --> - <string name="day_of_week_medium_tuesday">Tue</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Wed" stands for Wednesday --> - <string name="day_of_week_medium_wednesday">Wed</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Thu" stands for Thursday --> - <string name="day_of_week_medium_thursday">Thu</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Fri" stands for Friday --> - <string name="day_of_week_medium_friday">Fri</string> - - <!-- An abbreviated day of the week. Three characters typically in western languages. - In US English: "Sat" stands for Saturday --> - <string name="day_of_week_medium_saturday">Sat</string> - - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Su" stands for Sunday --> - <string name="day_of_week_short_sunday">Su</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Mo" stands for Monday --> - <string name="day_of_week_short_monday">Mo</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Tu" stands for Tuesday --> - <string name="day_of_week_short_tuesday">Tu</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "We" stands for Wednesday --> - <string name="day_of_week_short_wednesday">We</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Th" stands for Thursday --> - <string name="day_of_week_short_thursday">Th</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Fr" stands for Friday --> - <string name="day_of_week_short_friday">Fr</string> - - <!-- An abbreviated day of the week. Two characters typically in western languages. - In US English: "Sa" stands for Saturday --> - <string name="day_of_week_short_saturday">Sa</string> - - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "Su" stands for Sunday --> - <string name="day_of_week_shorter_sunday">Su</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "M" stands for Monday --> - <string name="day_of_week_shorter_monday">M</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "Tu" stands for Tuesday --> - <string name="day_of_week_shorter_tuesday">Tu</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "W" stands for Wednesday --> - <string name="day_of_week_shorter_wednesday">W</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "Th" stands for Thursday --> - <string name="day_of_week_shorter_thursday">Th</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "F" stands for Friday --> - <string name="day_of_week_shorter_friday">F</string> - - <!-- An abbreviated day of the week. One character if that is unique. Two if necessary. - In US English: "Sa" stands for Saturday --> - <string name="day_of_week_shorter_saturday">Sa</string> - - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "S" stands for Sunday --> - <string name="day_of_week_shortest_sunday">S</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "M" stands for Monday --> - <string name="day_of_week_shortest_monday">M</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "T" stands for Tuesday --> - <string name="day_of_week_shortest_tuesday">T</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "W" stands for Wednesday --> - <string name="day_of_week_shortest_wednesday">W</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "T" stands for Thursday --> - <string name="day_of_week_shortest_thursday">T</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "F" stands for Friday --> - <string name="day_of_week_shortest_friday">F</string> - - <!-- An abbreviated day of the week. One character long if it makes sense. Does not have - to be unique. - In US English: "S" stands for Saturday --> - <string name="day_of_week_shortest_saturday">S</string> - - - <!-- The full spelled out version of the month. --> - <string name="month_long_january">January</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_february">February</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_march">March</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_april">April</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_may">May</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_june">June</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_july">July</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_august">August</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_september">September</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_october">October</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_november">November</string> - - <!-- The full spelled out version of the month. --> - <string name="month_long_december">December</string> - - - <!-- An abbreviated month name. - In US English: "Jan" stands for January. --> - <string name="month_medium_january">Jan</string> - - <!-- An abbreviated month name. - In US English: "Feb" stands for February. --> - <string name="month_medium_february">Feb</string> - - <!-- An abbreviated month name. - In US English: "Mar" stands for March. --> - <string name="month_medium_march">Mar</string> - - <!-- An abbreviated month name. - In US English: "Apr" stands for April. --> - <string name="month_medium_april">Apr</string> - - <!-- An abbreviated month name. - In US English: "May" stands for May. --> - <string name="month_medium_may">May</string> - - <!-- An abbreviated month name. - In US English: "Jun" stands for June. --> - <string name="month_medium_june">Jun</string> - - <!-- An abbreviated month name. - In US English: "Jul" stands for July. --> - <string name="month_medium_july">Jul</string> - - <!-- An abbreviated month name. - In US English: "Aug" stands for August. --> - <string name="month_medium_august">Aug</string> - - <!-- An abbreviated month name. - In US English: "Sep" stands for September. --> - <string name="month_medium_september">Sep</string> - - <!-- An abbreviated month name. - In US English: "Oct" stands for October. --> - <string name="month_medium_october">Oct</string> - - <!-- An abbreviated month name. - In US English: "Nov" stands for November. --> - <string name="month_medium_november">Nov</string> - - <!-- An abbreviated month name. - In US English: "Dec" stands for December. --> - <string name="month_medium_december">Dec</string> - - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "J" stands for January --> - <string name="month_shortest_january">J</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "F" stands for February. --> - <string name="month_shortest_february">F</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "M" stands for March. --> - <string name="month_shortest_march">M</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "A" stands for April. --> - <string name="month_shortest_april">A</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "M" stands for May. --> - <string name="month_shortest_may">M</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "J" stands for June. --> - <string name="month_shortest_june">J</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "J" stands for July. --> - <string name="month_shortest_july">J</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "A" stands for August. --> - <string name="month_shortest_august">A</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "S" stands for September. --> - <string name="month_shortest_september">S</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "O" stands for October. --> - <string name="month_shortest_october">O</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "N" stands for November. --> - <string name="month_shortest_november">N</string> - - <!-- An abbreviated month name. One character long if it makes sense. Does not have - to be unique. - In US English: "D" stands for December. --> - <string name="month_shortest_december">D</string> <!-- Format string for durations like "01:23" (1 minute, 23 seconds) --> <string name="elapsed_time_short_format_mm_ss"><xliff:g id="minutes" example="1">%1$02d</xliff:g>:<xliff:g id="seconds" example="23">%2$02d</xliff:g></string> @@ -2153,6 +1662,8 @@ <string name="anr_process">Process <xliff:g id="process">%1$s</xliff:g> is not responding.</string> <!-- Button allowing the user to close an application that is not responding. This will kill the application. --> <string name="force_close">Force close</string> + <!-- Button allowing the user to send a bug report for application which has encountered an error. --> + <string name="report">Report</string> <!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. --> <string name="wait">Wait</string> <!-- Button allowing a developer to connect a debugger to an application that is not responding. --> @@ -2278,6 +1789,11 @@ <!-- See EXTMEDIA_FORMAT. This is the button text to format the sd card. --> <string name="extmedia_format_button_format">Format</string> + <!-- Title of notification shown when ADB is actively connected to the phone. --> + <string name="adb_active_notification_title">USB debugging connected</string> + <!-- Message of notification shown when ADB is actively connected to the phone. --> + <string name="adb_active_notification_message">A computer is connected to your phone.</string> + <!-- Used to replace %s in urls retreived from the signin server with locales. For Some --> <!-- devices we don't support all the locales we ship to and need to replace the '%s' with a --> <!-- locale string based on mcc values. By default (0-length string) we don't replace the %s --> @@ -2296,15 +1812,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> @@ -2312,11 +1828,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> @@ -2366,10 +1882,12 @@ <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs. This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array --> <string-array translatable="false" name="carrier_locales"> - </string-array> -</resources> - - + </string-array> + <!-- Title for the selected state of a CompoundButton. --> + <string name="accessibility_compound_button_selected">checked</string> + <!-- Title for the unselected state of a CompoundButton. --> + <string name="accessibility_compound_button_unselected">not checked</string> +</resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 8160c9c9298f..7d235ec8d4ef 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -124,6 +124,12 @@ <item name="windowExitAnimation">@anim/shrink_fade_out_from_bottom</item> </style> + <!-- {@hide} --> + <style name="Animation.SlidingCard"> + <item name="windowEnterAnimation">@anim/slide_in_up</item> + <item name="windowExitAnimation">@anim/slide_out_down</item> + </style> + <!-- Window animations that are applied to input method overlay windows. {@hide Pending API council approval} --> <style name="Animation.InputMethod"> @@ -171,6 +177,23 @@ <item name="android:fadingEdge">vertical</item> </style> + <style name="Widget.GestureOverlayView"> + <item name="android:gestureStrokeWidth">12.0</item> + <item name="android:gestureColor">#ffffff00</item> + <item name="android:uncertainGestureColor">#48ffff00</item> + <item name="android:fadeOffset">420</item> + <item name="android:fadeDuration">150</item> + <item name="android:gestureStrokeLengthThreshold">50.0</item> + <item name="android:gestureStrokeSquarenessThreshold">0.275</item> + <item name="android:gestureStrokeAngleThreshold">40.0</item> + <item name="android:eventsInterceptionEnabled">true</item> + </style> + + <style name="Widget.GestureOverlayView.White"> + <item name="android:gestureColor">#ff00ff00</item> + <item name="android:uncertainGestureColor">#4800ff00</item> + </style> + <style name="Widget.Button"> <item name="android:background">@android:drawable/btn_default</item> <item name="android:focusable">true</item> @@ -220,7 +243,7 @@ <style name="Widget.ProgressBar"> <item name="android:indeterminateOnly">true</item> - <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_medium_white</item> <item name="android:indeterminateBehavior">repeat</item> <item name="android:indeterminateDuration">3500</item> <item name="android:minWidth">48dip</item> @@ -230,7 +253,7 @@ </style> <style name="Widget.ProgressBar.Large"> - <item name="android:indeterminateDrawable">@android:drawable/progress_large</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_large_white</item> <item name="android:minWidth">76dip</item> <item name="android:maxWidth">76dip</item> <item name="android:minHeight">76dip</item> @@ -238,13 +261,25 @@ </style> <style name="Widget.ProgressBar.Small"> - <item name="android:indeterminateDrawable">@android:drawable/progress_small</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item> <item name="android:minWidth">16dip</item> <item name="android:maxWidth">16dip</item> <item name="android:minHeight">16dip</item> <item name="android:maxHeight">16dip</item> </style> + <style name="Widget.ProgressBar.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item> + </style> + + <style name="Widget.ProgressBar.Large.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_large</item> + </style> + + <style name="Widget.ProgressBar.Small.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_small</item> + </style> + <style name="Widget.ProgressBar.Small.Title"> <item name="android:indeterminateDrawable">@android:drawable/progress_small_titlebar</item> </style> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 9567523445e8..be836ebec4e5 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -44,12 +44,14 @@ <item name="textColorSecondaryInverse">@android:color/secondary_text_light</item> <item name="textColorTertiaryInverse">@android:color/tertiary_text_light</item> <item name="textColorPrimaryDisableOnly">@android:color/primary_text_dark_disable_only</item> + <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_light_disable_only</item> <item name="textColorPrimaryNoDisable">@android:color/primary_text_dark_nodisable</item> <item name="textColorSecondaryNoDisable">@android:color/secondary_text_dark_nodisable</item> <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_light_nodisable</item> <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_light_nodisable</item> <item name="textColorHint">@android:color/hint_foreground_dark</item> <item name="textColorHintInverse">@android:color/hint_foreground_light</item> + <item name="textColorSearchUrl">@android:color/search_url_text</item> <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item> <item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item> @@ -141,7 +143,8 @@ <item name="editTextStyle">@android:style/Widget.EditText</item> <item name="expandableListViewStyle">@android:style/Widget.ExpandableListView</item> <item name="galleryStyle">@android:style/Widget.Gallery</item> - <item name="gridViewStyle">@android:style/Widget.GridView</item> + <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView</item> + <item name="gridViewStyle">@android:style/Widget.GridView</item> <item name="imageButtonStyle">@android:style/Widget.ImageButton</item> <item name="imageWellStyle">@android:style/Widget.ImageWell</item> <item name="listViewStyle">@android:style/Widget.ListView</item> @@ -152,6 +155,9 @@ <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item> <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item> <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item> + <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> <item name="seekBarStyle">@android:style/Widget.SeekBar</item> <item name="ratingBarStyle">@android:style/Widget.RatingBar</item> <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item> @@ -181,6 +187,9 @@ <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item> <item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item> <item name="preferenceLayoutChild">@android:layout/preference_child</item> + + <!-- Search widget styles --> + <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item> </style> <!-- Variant of the default (dark) theme with no title bar --> @@ -212,6 +221,7 @@ <item name="textColorSecondaryInverse">@android:color/secondary_text_dark</item> <item name="textColorTertiaryInverse">@android:color/tertiary_text_dark</item> <item name="textColorPrimaryDisableOnly">@android:color/primary_text_light_disable_only</item> + <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_dark_disable_only</item> <item name="textColorPrimaryNoDisable">@android:color/primary_text_light_nodisable</item> <item name="textColorSecondaryNoDisable">@android:color/secondary_text_light_nodisable</item> <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_dark_nodisable</item> @@ -224,9 +234,17 @@ <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item> <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item> + <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item> <item name="listViewStyle">@android:style/Widget.ListView.White</item> <item name="listDivider">@drawable/divider_horizontal_bright</item> <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item> + + <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item> + <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item> + <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> </style> <!-- Variant of the light theme with no title bar --> @@ -365,6 +383,12 @@ <item name="android:backgroundDimEnabled">true</item> <item name="windowContentOverlay">@null</item> </style> + + <!-- Theme for the search input bar when doing global search. The only + difference from non-global search is that we do not dim the background. --> + <style name="Theme.GlobalSearchBar" parent="Theme.Panel"> + <item name="windowContentOverlay">@null</item> + </style> <!-- Menu Themes --> <eat-comment /> diff --git a/core/res/res/xml/eri.xml b/core/res/res/xml/eri.xml new file mode 100644 index 000000000000..cd66f1447a59 --- /dev/null +++ b/core/res/res/xml/eri.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- Note that IconMode can be only 0, ON or 1, FLASHING + The icon is turned OFF if then IconIndex = 1 --> + +<EriFile VersionNumber="1357" + NumberOfEriEntries="12" + EriFileType="1"> + + <CallPromptId Id="0" + CallPromptText="CallPromptId0"/> + + <CallPromptId Id="1" + CallPromptText="CallPromptId1"/> + + <CallPromptId Id="2" + CallPromptText="CallPromptId2"/> + + <EriInfo RoamingIndicator="64" + IconIndex="1" + IconMode="0" + EriText="T-CDMA 64" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="65" + IconIndex="65" + IconMode="0" + EriText="T-CDMA 65" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="66" + IconIndex="1" + IconMode="0" + EriText="T-CDMA Ext 66" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="67" + IconIndex="67" + IconMode="0" + EriText="T-CDMA Ext 67" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="68" + IconIndex="68" + IconMode="0" + EriText="T-CDMA Roam 68" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="69" + IconIndex="69" + IconMode="1" + EriText="T-CDMA Ext 69" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="70" + IconIndex="70" + IconMode="1" + EriText="T-CDMA Roam 70" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="71" + IconIndex="1" + IconMode="0" + EriText="T-CDMA Ext 71" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="72" + IconIndex="72" + IconMode="0" + EriText="T-CDMA Ext 72" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="73" + IconIndex="73" + IconMode="0" + EriText="T-CDMA Roam 73" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="74" + IconIndex="74" + IconMode="1" + EriText="T-CDMA Ext 74" + CallPromptId="0" + AlertId="0"/> + + <EriInfo RoamingIndicator="75" + IconIndex="75" + IconMode="1" + EriText="T-CDMA Roam 75" + CallPromptId="0" + AlertId="0"/> + +</EriFile> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml new file mode 100644 index 000000000000..859902e73d9e --- /dev/null +++ b/core/res/res/xml/power_profile.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<device name="Android"> + <item name="none">0</item> + <item name="screen.on">0.1</item> + <item name="bluetooth.active">0.1</item> + <item name="bluetooth.on">0.1</item> + <item name="screen.full">0.1</item> + <item name="wifi.on">0.1</item> + <item name="wifi.active">0.1</item> + <item name="wifi.scan">0.1</item> + <item name="cpu.idle">0.1</item> + <item name="cpu.normal">0.2</item> + <item name="cpu.full">1</item> + <item name="dsp.audio">0.1</item> + <item name="dsp.video">0.1</item> + <item name="radio.active">1</item> + <item name="gps.on">1</item> + <array name="radio.on"> <!-- Strength 0 to BINS-1 --> + <value>1</value> + <value>0.1</value> + </array> +</device> diff --git a/data/etc/platform.xml b/data/etc/platform.xml index f80bd6b00cdc..0bd32767cba9 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -54,7 +54,7 @@ <group gid="log" /> </permission> - <permission name="android.permission.SDCARD_WRITE" > + <permission name="android.permission.WRITE_EXTERNAL_STORAGE" > <group gid="sdcard_rw" /> </permission> @@ -84,6 +84,24 @@ others should have a fairly open environment in which to interact with the system. --> + <!-- Standard permissions granted to the shell. --> + <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" /> + <assign-permission name="android.permission.SEND_SMS" uid="shell" /> + <assign-permission name="android.permission.CALL_PHONE" uid="shell" /> + <assign-permission name="android.permission.READ_CONTACTS" uid="shell" /> + <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" /> + <assign-permission name="android.permission.READ_OWNER_DATA" uid="shell" /> + <assign-permission name="android.permission.WRITE_OWNER_DATA" uid="shell" /> + <assign-permission name="android.permission.READ_CALENDAR" uid="shell" /> + <assign-permission name="android.permission.WRITE_CALENDAR" uid="shell" /> + <assign-permission name="android.permission.READ_USER_DICTIONARY" uid="shell" /> + <assign-permission name="android.permission.WRITE_USER_DICTIONARY" uid="shell" /> + <assign-permission name="android.permission.ACCESS_FINE_LOCATION" uid="shell" /> + <assign-permission name="android.permission.ACCESS_COARSE_LOCATION" uid="shell" /> + <assign-permission name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" uid="shell" /> + <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" /> + <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" /> + <assign-permission name="android.permission.BLUETOOTH" uid="shell" /> <!-- System tool permissions granted to the shell. --> <assign-permission name="android.permission.GET_TASKS" uid="shell" /> <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" /> @@ -114,11 +132,14 @@ <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="shell" /> <assign-permission name="android.permission.READ_FRAME_BUFFER" uid="shell" /> <assign-permission name="android.permission.DEVICE_POWER" uid="shell" /> + <assign-permission name="android.permission.INSTALL_LOCATION_PROVIDER" uid="shell" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" /> <assign-permission name="android.permission.ACCESS_DRM" uid="media" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" /> + <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> + <!-- This is a list of all the libraries available for application code to link against. --> diff --git a/data/fonts/DroidSansJapanese.ttf b/data/fonts/DroidSansJapanese.ttf Binary files differindex ca7922122ab5..412fa3de05e8 100755 --- a/data/fonts/DroidSansJapanese.ttf +++ b/data/fonts/DroidSansJapanese.ttf diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk index f24d05c4f425..aea3f0b3007b 100644 --- a/data/sounds/AudioPackage2.mk +++ b/data/sounds/AudioPackage2.mk @@ -7,90 +7,91 @@ # that have larger internal flash. # -local_path:= frameworks/base/data/sounds +LOCAL_PATH:= frameworks/base/data/sounds PRODUCT_COPY_FILES += \ - $(local_path)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \ - $(local_path)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \ - $(local_path)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \ - $(local_path)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \ - $(local_path)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \ - $(local_path)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \ - $(local_path)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \ - $(local_path)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \ - $(local_path)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \ - $(local_path)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \ - $(local_path)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ - $(local_path)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ - $(local_path)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ - $(local_path)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ - $(local_path)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ - $(local_path)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ - $(local_path)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \ - $(local_path)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \ - $(local_path)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \ - $(local_path)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \ - $(local_path)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \ - $(local_path)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \ - $(local_path)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \ - $(local_path)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \ - $(local_path)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \ - $(local_path)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \ - $(local_path)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \ - $(local_path)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \ - $(local_path)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \ - $(local_path)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \ - $(local_path)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \ - $(local_path)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ - $(local_path)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \ - $(local_path)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \ - $(local_path)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \ - $(local_path)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \ - $(local_path)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \ - $(local_path)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \ - $(local_path)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \ - $(local_path)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \ - $(local_path)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \ - $(local_path)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \ - $(local_path)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \ - $(local_path)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \ - $(local_path)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \ - $(local_path)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \ - $(local_path)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \ - $(local_path)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \ - $(local_path)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \ - $(local_path)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \ - $(local_path)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \ - $(local_path)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \ - $(local_path)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ - $(local_path)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \ - $(local_path)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ - $(local_path)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ - $(local_path)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ - $(local_path)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \ - $(local_path)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \ - $(local_path)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \ - $(local_path)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \ - $(local_path)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \ - $(local_path)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \ - $(local_path)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \ - $(local_path)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \ - $(local_path)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \ - $(local_path)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \ - $(local_path)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \ - $(local_path)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \ - $(local_path)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \ - $(local_path)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \ - $(local_path)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \ - $(local_path)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \ - $(local_path)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \ - $(local_path)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \ - $(local_path)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \ - $(local_path)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \ - $(local_path)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \ - $(local_path)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \ - $(local_path)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \ - $(local_path)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \ - $(local_path)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \ - $(local_path)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg + $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \ + $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \ + $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \ + $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \ + $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \ + $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \ + $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \ + $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \ + $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \ + $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \ + $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ + $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ + $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ + $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \ + $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ + $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ + $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ + $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \ + $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \ + $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \ + $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \ + $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \ + $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \ + $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \ + $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \ + $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \ + $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \ + $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \ + $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \ + $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ + $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \ + $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \ + $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \ + $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \ + $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \ + $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \ + $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \ + $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \ + $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \ + $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \ + $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \ + $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \ + $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \ + $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \ + $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \ + $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \ + $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \ + $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \ + $(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \ + $(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \ + $(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ + $(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \ + $(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ + $(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ + $(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \ + $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \ + $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \ + $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \ + $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \ + $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \ + $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \ + $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \ + $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \ + $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \ + $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \ + $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \ + $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \ + $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \ + $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \ + $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \ + $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \ + $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \ + $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \ + $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \ + $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \ + $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \ + $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \ + $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \ + $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \ + $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk index 291f0b6b997e..8c8fc329c430 100644 --- a/data/sounds/OriginalAudio.mk +++ b/data/sounds/OriginalAudio.mk @@ -22,6 +22,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ + $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \ $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ diff --git a/data/sounds/Silence.ogg b/data/sounds/Silence.ogg Binary files differnew file mode 100644 index 000000000000..6d789071a525 --- /dev/null +++ b/data/sounds/Silence.ogg diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd index a3cc0ff09692..db5a15e10126 100644 --- a/docs/html/guide/appendix/media-formats.jd +++ b/docs/html/guide/appendix/media-formats.jd @@ -2,8 +2,9 @@ page.title=Android Supported Media Formats @jd:body <p>The <a href="#core">Core Media Formats</a> table below describes the media format support built into the Android platform. Note that any given mobile device may provide support for additional formats or file types not listed in the table. </p> -<p>For your convenience, the table <a href="#g1">T-Mobile G1 Media Formats</a> describes additional media format details for the T-Mobile G1 device. </p> +<p>For your convenience, the table <a href="#g1">T-Mobile G1 Media Formats</a> describes additional media formats and characteristics provided by the T-Mobile G1 device. Other devices may support additional formats not listed on this page. </p> +<p>As an application developer, you are free to make use of any media codec that is available on any Android-powered device, including those provided by the Android platform and those that are device-specific.</p> <h2 id="core">Core Media Formats</h2> @@ -130,7 +131,7 @@ page.title=Android Supported Media Formats <td style="text-align: center;">X</td> <td style="text-align: center;">X</td> <td> </td> -<td>3GPP (.3gp)</td> +<td>3GPP (.3gp) and MPEG-4 (.mp4)</td> </tr> <tr> @@ -151,9 +152,9 @@ page.title=Android Supported Media Formats </tbody></table> -<h2 id="g1">T-Mobile G1 Media Formats</h2> +<h2 id="g1">T-Mobile G1 Media Formats and Characteristics</h2> -<p>In addition to the core media formats supported in the Android platform, the T-Mobile G1 also supports the formats listed below.</p> +<p>The table below lists media formats supported by the T-Mobile G1 in addition to those provided as part of the Android platform. This table also details G1-specific performance characteristics of some Android core media formats.</p> <table> <tbody> @@ -163,7 +164,7 @@ page.title=Android Supported Media Formats <th>Format</th> <th>Encoder</th> <th>Decoder</th> -<th>Details</th> +<th>Comment</th> <th>File Type(s) Supported</th> </tr> @@ -178,13 +179,13 @@ page.title=Android Supported Media Formats <li>L2: <=161 kbps <=48 kHz</li> <li>L3: <385 kbps <=48 kHz</li> </ul> -Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA Pro, Lossless, or Speech codecs. +Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA Pro, Lossless, or Speech codecs. </td> <td>Windows Media Audio (.wma)</td> </tr> <tr> -<td rowspan="3">Video</td> +<td rowspan="2">Video</td> <td>WMV</td> <td> </td> <td style="text-align: center;">X</td> @@ -193,22 +194,16 @@ Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA P </tr> <tr> -<td>H.263</td> -<td style="text-align: center;">X</td> -<td style="text-align: center;">X</td> -<td> </td> -<td>3GPP (.3gp) and MPEG-4 (.mp4)</td> -</tr> - -<tr> <td>H.264 AVC</td> <td> </td> <td style="text-align: center;">X</td> -<td>Limited to baseline profile up to 480x320, and 600 kbps average bitrate for the video stream.</td> +<td>On the G1, this decoder is limited to baseline profile up to 480x320, and 600 kbps average bitrate for the video stream.</td> <td>3GPP (.3gp) and MPEG-4 (.mp4)</td> </tr> </tbody></table> -<p>Note that Windows Media codecs are not part of the Android platform and require special licensing from Microsoft or an authorized developer such as Packet Video.</p> + + + diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd index 75f3d78f4809..3b3bb38a476d 100644 --- a/docs/html/guide/developing/eclipse-adt.jd +++ b/docs/html/guide/developing/eclipse-adt.jd @@ -38,15 +38,15 @@ manifest and resource files.</li> <p>To begin developing Android applications in the Eclipse IDE with ADT, you first need to download the Eclipse IDE and then download and install the ADT plugin. To do so, follow the -steps given in <a href="{@docRoot}sdk/1.5_r1/installing.html#installingplugin">Installing +steps given in <a href="{@docRoot}sdk/{@sdkCurrent}/installing.html#installingplugin">Installing the ADT Plugin</a>.</p> <p>If you are already developing applications using a version of ADT earlier than 0.9, make sure to upgrade to the latest version before continuing. See the guide to -<a href="{@docRoot}sdk/1.5_r1/upgrading.html#UpdateAdt">Update Your Eclipse ADT Plugin</a>.</p> +<a href="{@docRoot}sdk/{@sdkCurrent}/upgrading.html#UpdateAdt">Updating Your Eclipse ADT Plugin</a>.</p> <p class="note"><strong>Note:</strong> This guide assumes you are using the latest version of -the ADT plugin (0.9). While most of the information covered also applies to previous +the ADT plugin. While most of the information covered also applies to previous versions, if you are using an older version, you may want to consult this document from the set of documentation included in your SDK package (instead of the online version).</p> @@ -138,9 +138,9 @@ folders and files in your new project:</p> <p><em>Wait!</em> Before you can run your application on the Android Emulator, you <strong>must</strong> create an Android Virtual Device (AVD). An AVD is a configuration that specifies the Android platform to be used on the emulator. -You can read more about AVDs in the <a href="{@docRoot}guide/developing/index.html#avd">Developing -Overview</a>, but if you just want to get started, follow the simple guide below to create -an AVD.</p> +You can read more in the <a href="{@docRoot}guide/developing/tools/avd.html">Android Virtual +Devices</a> document, but if you just want to get started, follow the simple guide below to +create an AVD.</p> <p>If you will be running your applications only on actual device hardware, you do not need an AVD — see diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index a044ceaecd3b..da4a2c33c5cf 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -146,7 +146,9 @@ <li class="toggle-list"> <div><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/index.html">UI Guidelines</a></div> <ul> + <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design.html">Icon Design</a></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/widget_design.html">App Widget Design</a></li> + <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/activity_task_design.html">Activity and Task Design</a></li> </ul> </li> <li><a href="<?cs var:toroot ?>guide/practices/design/performance.html">Designing for Performance</a></li> diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd new file mode 100644 index 000000000000..e2fc89c6ddba --- /dev/null +++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd @@ -0,0 +1,1238 @@ +page.title=Activity and Task Design Guidelines +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Activity and task design quickview</h2> + +<ul> +<li>Activities are the main building blocks of Android applications. </li> +<li>In addition to writing your own activities, you are free to re-use activities from many other applications through intents.</li> +<li>You can enable activities in your application to be started from intents in other applications.</li> +<li>In nearly all cases, the activity stack just works as expected.</li> + In a couple of cases you might need to ensure the right thing happens by setting a string or flag.</li> +</ul> + +<h2>In this document</h2> + +<ol> + <li><a href=#applications_activities>Applications, Activities, Activity Stack and Tasks</a> + </li> + <li><a href=#tour>A Tour of Activities and Tasks + <ol> + <li><a href=#starting_an_activity_from_home>Starting an Activity from Home</a></li> + <li><a href=#navigating_away_from_an_activity>Navigating Away from an Activity</a></li> + <li><a href=#reusing_an_activity>Re-using an Activity</a></li> + <li><a href=#replacing_an_activity>Replacing an Activity</a></li> + <li><a href=#multitasking>Multitasking</a></li> + <li><a href=#launching_from_two_entry_points>Launching from Two Entry Points</a></li> + <li><a href=#intents>Intents</a></li> + <li><a href=#switching_between_tasks>Switching Between Tasks</a></li> + </ol> + </li> + <li><a href=#tips>Design Tips + <ol> + <li><a href=#activity_not_reused_tip>Don't specify intent filters in an activity that won't be re-used</a></li> + <!-- <li><a href=#others_to_reuse_tip>Don't define your own URI schemes</a></li> --> + <li><a href=#reusing_tip>Handle case where no activity matches</a></li> + <li><a href=#activity_launching_tip>Consider how to launch your activities</a></li> + <li><a href=#activities_added_to_task_tip>Allow activities to add to current task</a></li> + <li><a href=#notifications_get_back_tip>Notifications should let user easily get back</li> + <li><a href=#use_notification_tip>Use the notification system</a></li> + <li><a href=#taking_over_back_key>Don't take over BACK key unless you absolutely need to</a></li> + </ol> + </li> +</ol> + +<h2>See also</h2> + +<ol> + <li><a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a></li> +</ol> + +</div> +</div> + +<p> + This document describes core principles of the Android application + framework, from a high-level, user-centric perspective useful to + interaction and application designers as well as application + developers. +</p> + +<p> + It illustrates activities and tasks with examples, and describes some + of their underlying principles and mechanisms, such as navigation, + multitasking, activity re-use, intents, and the activity stack. + The document also highlights design decisions that are available to you + and what control they give you over the UI of your application. +</p> + +<p> + This document draws examples from several Android applications, + including default applications (such as Dialer) and Google + applications (such as Maps). You can try out the examples yourself in + the Android emulator or on an Android-powered device. If you are using + a device, note that your device may not offer all of the example + applications used in this document. +</p> + +<p> + Be sure to look at the <a href="#design_tips">Design Tips</a> section + for guidelines, tips, and things to avoid. This document is a + complement to <a href={@docRoot}guide/topics/fundamentals.html + title="Application Fundamentals">Application Fundamentals</a>, + which covers the underlying mechanics for programmers. +</p> + +<h2 id="applications_activities">Applications, Activities, Activity Stack and Tasks</h2> + +<p> + Four fundamental concepts in the Android system that are helpful for you to understand are: +</p> + +<ul> + <li>Applications + <li>Activities + <li>Activity Stack + <li>Tasks +</ul> + +<h3 id=applications>Applications</h3> + +<p> + An Android <em>application</em> typically consists of one or more + related, loosely bound activities <!--(and possibly + <a href=#services_broadcast_receivers title="other components">other + components</a>)--> for the user to interact with, typically bundled up + in a single file (with an .apk suffix). Android ships with a rich set + of applications that may include email, calendar, browser, maps, text + messaging, contacts, camera, dialer, music player, settings and + others. +</p> + +<p> + Android has an application launcher available at the Home screen, + typically in a sliding drawer which displays applications as icons, + which the user can pick to start an application. +</p> + + +<h3 id=activities>Activities</h3> + +<p> + <em>Activities</em> are the main building blocks of Android + applications. When you create an application, you can assemble it from + activities that you create and from activities you re-use from other + applications. These activities are bound at runtime, so that newly + installed applications can take advantage of already installed + activities. Once assembled, activities work together to form a + cohesive user interface. An activity has a distinct visual user + interface designed around a single, well-bounded purpose, such as + viewing, editing, dialing the phone, taking a photo, searching, + sending data, starting a voice command, or performing some other type + of user action. Any application that presents anything on the display + must have at least one activity responsible for that display. +</p> + +<p> + When using an Android device, as the user moves through the user + interface they start activities one after the other, totally oblivious + to the underlying behavior — to them the experience should be + seamless, activity after activity, <a href="#tasks">task</a> after + task. +</p> + +<p> + An activity handles a particular type of content (data) and accepts a + set of related user actions. In general, each activity has a + <a href={@docRoot}guide/topics/fundamentals.html#actlife + title=lifecycle>lifecycle</a> that is independent of the other + activities in its application or task — each activity is + launched (started) independently, and the user or system can start, + run, pause, resume, stop and restart it as needed. Because of this + independence, activities can be re-used and replaced by other + activities in a variety of ways. +</p> + +<p> + The Dialer application is an example of an application that consists + basically of four activities: dialer, contacts list, view contact, and + new contact, as shown in the following screenshots: +</p> + + <table style="border: none;"> + <tbody> + <tr> + <td style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/ContactsDialer.png> + <div style=TEXT-ALIGN:center> + Dialer + </div> + </td> + <td style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/ContactsList.png> + <div style=TEXT-ALIGN:center> + Contacts + </div> + </td> + <td style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/ContactView.png> + <div style=TEXT-ALIGN:center> + View Contact + </div> + </td> + <td style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/ContactNew.png> + <div style=TEXT-ALIGN:center> + New Contact + </div> + </td> + </tr> + </tbody> + </table> + +<p> + Here are other examples of applications and the activities they might contain: +</p> + + <ul> + <li> + Email - activities to view folders, view list of messages, + view a message, compose a message, and set up an account + </li> + <li> + Calendar - activities to view day, view week, view month, view + agenda, edit an event, edit preferences, and view an alert + </li> + <li> + Camera - activities for running the camera, viewing the list + of pictures, viewing a picture, cropping a picture, running + the camcorder, viewing the list of movies, and viewing a movie + </li> + <li> + Game - one activity to play the game, typically another for setup + </li> + <li> + Maps - one activity to view a location on a map, a second for lists + (such as turn list or friend list), and a third for details + (friend location, status, photo) + </li> + </ul> + + +<p> + An activity is the most prominent of four <em>components</em> of an + application. The other components are service, content provider and + broadcast receiver. For more details on activities, see Activity in + <a href={@docRoot}guide/topics/fundamentals.html#appcomp + title="Application Components">Application Components</a>. +</p> + + +<h3 id="activity_stack">Activity Stack</h3> + +<p> + As the user moves from activity to activity, across applications, the + Android system keeps a linear navigation history of activities the + user has visited. This is the <em>activity stack</em>, also known as the + back stack. In general, when a user starts a new activity, it is added + to the activity stack, so that pressing BACK displays the previous + activity on the stack. However, the user cannot use the BACK key to go + back further than the last visit to Home. The adding of an activity to + the current stack happens whether or not that activity begins a new + <a href=#tasks title=task>task</a> (as long as that task was started + without going Home), so going back can let the user go back to + activities in previous tasks. The user can get to tasks earlier than + the most recent Home by selecting its root activity from the + application launcher, a shortcut, or the "Recent tasks" screen. +</p> + +<p> + Activities are the only things that can be added to the activity stack + — views, windows, menus, and dialogs cannot. That is, when + designing the navigation, if you have screen A and you want the user + to be able go to a subsequent screen B and then use the BACK key to go + back to screen A, then the screen A needs to be implemented as an + activity. The one exception to this rule is if your application + <a href=#taking_over_back_key title="takes control of the BACK key" + takes control of the BACK key</a> and manages the navigation itself. +</p> + + + +<h3 id=tasks>Tasks</h3> + +<p> + A <em>task</em> is the sequence of activities the user follows to + accomplish an objective, regardless of which applications the + activities belong to. Until a new task is explicitly specified (see + "Interrupting the Task"), all activities the user starts are + considered to be part of the current task. It's notable that these + activities can be in any application — that is, all in the same + application or in different ones. That is, a task that starts out in + contacts can continue, by choosing an email address, to an email + activity and then, by attaching a file, to a picture gallery to pick + from. Contacts, email and picture gallery are all separate + applications. +</p> + +<p> + The activity that starts a task is called the <em>root activity</em>. + It is often, but not necessarily, started from the application + launcher, Home screen shortcut or "Recent tasks" switcher (a long + press on Home on some devices). The user can return to a task by + choosing the icon for its root activity the same way they started the + task. Once inside a task, the BACK key goes to previous activities in + that task. The activity stack is made up of one or more tasks. +</p> + +<p> + Here are some examples of tasks: +</p> + + <ul> + <li> + Send a text message with an attachment + </li> + <li> + View a YouTube video and share it by email with someone else + </li> + </ul> + +<p> + <b>Interrupting the Task</b> - An important property of a task is that + the user can interrupt what they're doing (their task) to perform a + different task, then are able to return to where they left off to + complete the original task. The idea is that users can run multiple + tasks simultaneously and switch between them. There are two primary + ways to jump off to that other task — in both cases the user + should be able to return to where they were before the interruption: +</p> + + + <ul> + <li> + User is interrupted by a notification – a notification appears and the user wants to act on it + </li> + <li> + User deciding to perform another task – user just presses Home and starts an application + </li> + </ul> + +<p> + Of course, there are exceptions to the rules. Beyond the two ways just + mentioned, there is a third way to start a task, and that is by + starting an activity that defines itself as a new task. Maps and + Browser are two applications that do this. For example, choosing an + address in an email starts the Maps activity as a new task, and + choosing a link in an email starts the Browser activity as a new + task. In these cases, the BACK key will return to the previous + activity in a different task (Email), because it was not started from + Home. +</p> + + +<h2 id="tour">A Tour of Activities and Tasks</h2> + +<p> + The following examples illustrate basic principles for applications, + activities, the activity stack, the BACK key, tasks and intents. It + shows how the system responds to user actions such as starting + activities and switching between tasks. With most of these examples + you can follow along, launching activities on your device as + indicated. +</p> + + +<h3 id=starting_an_activity_from_home>Starting an Activity from Home</h3> + +<p> + Home is the starting place for most applications. (Some applications + can be launched only from other applications.) When the user touches + an icon in the application launcher (or a shortcut on the Home + screen), the main activity for that application is launched into the + foreground where it has user focus. As shown in the following figure, + the user action of going Home and touching the Email icon launches the + List Messages activity of the Email application. The Home activity + remains stopped in the background, ready to restart when called on by + the user. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/HomeTaskBasics1a.png> +</p> + +<h3 id=navigating_away_from_an_activity>Navigating Away from an Activity with BACK and HOME keys</h3> + +<p> + An activity can keep or lose its state depending on how the user + leaves the activity — by the HOME or BACK key. +</p> + +<p> + By default, pressing the BACK key finishes (destroys) the current + activity and displays the previous activity to the user. In the + following figure, the user starts email by touching the Email icon in + the Home screen, which displays a list of email messages. The user + scrolls down the list (changing its initial state). Pressing BACK + destroys the List Messages activity and returns to the previous + activity, which is Home. If the user re-launches Email, it would + re-load the messages and display its initial, non-scrolled state. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/HomeTaskBasics1b.png> +</p> + +<p> + In the above example, pressing BACK goes to Home because it was the + last activity the user was viewing. But if the user had gotten to List + Message from some other activity, then pressing BACK would have + returned there. +</p> + +<p> + By contrast, the next figure shows the user leaving List Messages by + pressing HOME instead of BACK — the List Messages activity is + stopped and moved to the background rather than being + destroyed. Starting Email again from its icon would simply bring the + List Messages activity to the foreground (changing it from stopped to + running) in the same scrolled state the user last left it. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/HomeTaskBasics1c.png> +</p> + +<p> + <b>Exceptions.</b> Some background activities return to their initial + screen (they lose any state, such as scrolling) when they are brought + to the foreground. This is true for Contacts and Gallery. If the user + chooses Home > Contacts then chooses a contact, they are viewing + the details of a contact. If they start again by choosing Home > + Contacts, they are presented with the initial list of contacts rather + than the contact they were last viewing. Contacts is designed this way + because this initial screen is the main entry point for the + application with four tabs for accessing the full range of features. +</p> + +<p> + In addition, not all activities have the behavior that they are + destroyed when BACK is pressed. When the user starts playing music in + the Music application and then presses BACK, the application overrides + the normal back behavior, preventing the player activity from being + destroyed, and continues playing music, even though its activity is no + longer visible — as a visual substitute, the Music application + places a notification in the status bar so the user still has an easy + way to get to the application to stop or control the music. Note that + you can write an activity to stop when its screen is no longer + visible, or to continue running in the background — the latter + was chosen for the music player. +</p> + + +<h3 id=reusing_an_activity>Re-using an Activity</h3> + +<p> + When activity A starts activity B in a different application, activity + B is said to be <em>re-used</em>. This use case normally takes place + because activity A is lacking a capability and can find it in activity B. +</p> + +<p> + <b>Contacts Re-Uses Gallery to Get a Picture</b> - The Contacts + activity has a field for a picture of a contact, but the Gallery is + normally where pictures are kept. So Contacts can re-use the Gallery + activity to get a picture. This is a good example of re-use of the + Gallery activity. The following figure illustrates the sequence of + activities to do this (up to crop). This is how it's done: The user + chooses Contacts, selects the contact for viewing, chooses MENU > + Edit contact and touches the picture field, which launches the Gallery + activity. The user then chooses the picture they want, crops and saves + it. Saving it causes the picture to be inserted into the picture field + in the contact. +</p> + +<p> + Notice the Gallery returns a picture to the Contacts application that + started it. The next example illustrates re-use of an activity that + does not return a result. Also notice that the following figure is + illustrates the navigation history through the activities, or the + activity stack — the user can back up through each activity all + the way to Home. +</p> + +<p> + When designing an application, it's good to think about how it can + re-use activities in other applications, and how your activities might + be re-used by other applications. If you add an activity with the same + <a href=#intents title="intent filter">intent filter</a> as an + exisiting activity, then the system presents the user with a choice + between the activities. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/ReusingAnActivity1.png> +</p> + +<p> + <b>Gallery Re-Uses Messaging for Sharing a Picture</b> - Sharing is + another good example of one application re-using an activity from a + different application. As shown in the following figure, the user + starts Gallery, picks a picture to view, chooses MENU > Share, and + picks "Messaging". This starts the Messaging activity, creates a new + message and attaches the original picture to it. The user then fills + in the "To" field, writes a short message and sends it. User focus + remains in the Messaging program. If the user wants to go back to the + Gallery, they must press the BACK key. (The user can back up through + each activity all the way to Home.) +</p> + +<p> + In contrast to the previous example, this re-use of the Messaging + activity does not return anything to the Gallery activity that started it. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/ReusingAnActivity2.png> +</p> + +<p> + Both of these examples illustrate tasks — a sequence of + activities that accomplish an objective. Each case uses activities + from two different applications to get the job done. +</p> + + +<h3 id=replacing_an_activity>Replacing an Activity</h3> + +<p> + This is the use case where activity A replaces activity B in a + different application. This situation normally happens because + activity A is better at doing the job than activity B. In other words, + A and B are equivalent enough that A can replace B. This case stands + in contrast with re-using an activity, where A and B are quite + different activities and supplement each other. +</p> + +<p> + In this example, the user has downloaded a replacement for the Phone + Ringtone activity, called Rings Extended. Now when they go to + Settings, Sound & Display, Phone Ringtone, the system presents + them with a choice between the Android System's ringtone activity and + the new one. This dialog box has an option to remember their choice + "Use by default for this action". When they choose "Rings Extended", + that activity loads, replacing the original Android ringtone activity. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/ReplacingAnActivity.png> +</p> + +<h3 id=multitasking>Multitasking</h3> + +<p> + As previously noted, when an activity has been launched, the user can + go to Home and launch a second activity without destroying the first + activity. This scenario demonstrates launching the Maps application. +</p> + + <ul> + <li> + State 1 - The user launches the View Map activity and searches + for a map location. Let's say the network is slow, so the map is + taking an unusually long taking time to draw. + </li> + </ul> + <ul> + <li> + State 2 - The user wants to do something else while they're + waiting, so they press HOME, which does not interrupt the map's + network connection and allows the map to continue loading in the + background. + + <p> + Note that when you write an activity, you can make it stop or + continue running when it is moved to the background (see + onStop() in <a href={@docRoot}guide/topics/fundamentals.html#actlife + title="Activity Lifecycle">Activity Lifecycle</a>). + For activities that download data from the network, it's recommended + to let them continue downloading so the user can multi-task. + </p> + + </li> + + <li> + State 3 - The map activity is now running in the background, + with Home in the foreground. The user then launches the Calendar + activity, which launches into the foreground, taking user focus, + where they view today's calendar (as indicated by the heavy + outline). + </li> + </ul> + +<p> +<img src={@docRoot}images/activity_task_design/HomeTaskBasics1d.png> +</p> + <ul> + <li> + State 4 - The user presses Home, then Maps to return to the map, which by now has fully loaded. + </li> + </ul> + +<p> + <img src={@docRoot}images/activity_task_design/HomeTaskBasics1e.png> +</p> + +<p> + The application launcher at Home has launched "View Map" and "Day + View" activities into separate <em>tasks</em>, hence the system is + multitasking — running multiple <a href=#tasks + title=tasks>tasks</a>. +</p> + + +<h3 id=launching_from_two_entry_points>Launching from Two Entry Points</h3> + +<p> + Every application must have at least one entry point — a way + for the user or system to access activities inside the + application. Each icon in the application launcher at home + represents an entry point. Applications can also from another + application. Each activity is a potential entry point into the + application. +</p> + +<p> + The phone application has two entry points: Contacts and Dialer. A + user entering from Contacts can choose a phone number to launch the + Dialer. As shown in the following figure, a user could choose the + Contacts icon to launch the Contacts activity, then pick a phone + number to launch the Dialer activity and dial the phone. +</p> + +<p> + Once the user is inside the application, they can access other + activities, such as New Contact and Edit Contact, through tabs, menu + items, list items, onscreen buttons, or other user interface + controls. +</p> + +<p> +<img src={@docRoot}images/activity_task_design/PhoneActivitiesDiagram.png> +</p> + +<h3 id=intents>Intents</h3> + +<p> + When the user takes an action on some data, such as touching a + mailto:info@example.com link, they are actually initiating an Intent + object, or just an <em>intent</em>, which then gets resolved to a + particular component (we consider only activity components here). + So, the result of a user touching a mailto: link is an Intent object + that the system tries to match to an activity. If that Intent object was + written explicitly naming an activity (an <em>explicit intent</em>), + then the system immediately launches that activity in response to the user + action. However, if that Intent object was written without naming an + activity (an <em>implicit intent</em>), the system compares the Intent + object to the <em>intent filters</em> of available activities. If more + than one activity can handle the action and data, the system + displays an activity chooser for the user to choose from. +</p> + +<p> + This example of touching the mailto: link is shown in the following + figure. If the device has two email applications set up, when a user + touches a mailto: email address on a web page, the result is an + Intent object which displays a dialog box with a choice between the + two activities to compose an email (Gmail and Email). +</p> + +<p> + <img src={@docRoot}images/activity_task_design/IntentsDiagram.png> +</p> + +<p> + Here are some examples of Intent objects and the activities they resolve to: +</p> + + <ul> + <li> + View the list of contacts - resolves to a contact list viewer activity + </li> + + <li> + View a particular contact - resolves to a contact viewer activity + </li> + + <li> + Edit a particular contact - resolves to a contact editor activity + </li> + + <li> + Send to a particular email - resolves to an email activity + </li> + + <li> + Dial a phone number - resolves to a phone dialer activity + </li> + + <li> + View the list of images - resolves to an image list viewer activity + </li> + + <li> + View a particular image - resolves to an image viewer activity + </li> + + <li> + Crop a particular image - resolves to an image cropper activity + </li> + </ul> + +<p> + Notice that an Intent object specifies two things, an action and data: +</p> + + <ul> + <li> + A generic action to be performed. In these examples: view, edit, dial or crop + </li> + + <li> + The specific data to be acted on. In these examples: the list of contacts, a particular contact, a phone number, the list of images, or a particular image + </li> + </ul> + + <p> + Note that any user action to start an activity from the + application launcher at Home is an explicit intent to a specific + activity. Likewise, some activities launch private activities + within their application as explicit intents so no other activity + can access them. + </p> + + <p> + For more on intents, see {@link android.content.Intent Intent class} and + <a href={@docRoot}guide/topics/fundamentals.html#ifilters + title="intent filters">intent filters</a>. + </p> + + +<h3 id=switching_between_tasks>Switching Between Tasks</h3> + +<p> + This scenario shows how the user can switch between two tasks. In + this example, the user writes a text message, attaches a picture, + but before they are done they glance at their calendar. They then + return to where they left off, attaching the picture and sending the + message. +</p> + + <ol> + <li> + <b>Start first task.</b> You want to send a text message and attach a photo. You would choose: + + <p> + Home > Messaging > New message > MENU > Attach + > Pictures. This last step launches the picture gallery + for picking a photo. Notice that picture gallery is an + activity in a separate application. + </p> + + + <table> + <tbody> + <tr> + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching1a.png> + </td> + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching1b.png> + </td> + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching1c.png> + </td> + </tr> + </tbody> + </table> + + <p> + At this point, before you have picked a picture, you decide + to stop and glance at your calendar, which is a separate + task. Because the current activity has no button to go + directly to the Calendar, you need to start from Home. + </p> + + </li> + <li> + <b>Start second task.</b> You choose Home > Calendar to + look at a calendar event. Calendar launches from Home as a new + task because the application launcher creates a new task for + each application it launches. + + <p> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching2.png> + </p> + </li> + + <li> + <b>Switch to first task and complete it.</b> When done looking + at the Calendar, you can return to attaching the picture by + starting the root activity again for that task: choose Home + > Messaging, which takes you not to Messaging, but directly + to the Picture gallery, where you left off. You can then pick + a photo, which is added to the message, you send the message + and you're done with the first task. + + <table> + <tbody> + <tr> + + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching3a.png> + </td> + + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching3b.png> + </td> + + <td valign=top style="border: none !important;"> + <img src={@docRoot}images/activity_task_design/HomeTaskSwitching3c.png> + </td> + + </tr> + </tbody> + </table> + </li> + </ol> + + +<h2 id="tips">Design Tips</h2> + +<p> + The following are tips and guidelines for application designers and developers. +</p> + +<h3 id=activity_not_reused_tip>When writing an activity that won't be re-used, don't specify intent filters — use explicit intents</h3> + +<p> + If you're writing an activity that you don't want other activities + to use, be sure not to add any intent filters to that activity. This + applies to an activity that will be launched only from the + application launcher or from other activities inside your + application. Instead, just create intents specifying the explicit + component to launch — that is, explicit intents. In this case, + there's just no need for intent filters. Intent filters are + published to all other applications, so if you make an intent + filter, what you're doing is publishing access to your activity, + which means you can cause unintentional security holes. +</p> + +<!-- +<h3 id="others_to_reuse_tip">When writing an activity for others to re-use, don't define your own URI schemes</h3> + +<p> + If publishing to others, don't define your own URI schemes in an + Intent type. Schemes (such as http: and mailto:) are an Internet + standard with a universal namespace outside of just Android, so you + aren't allowed to just make up your own. Instead, you should just + define your own actions. The action namespace is designed to not + have conflicts. Typically, your activity has one scheme with many + different actions. +</p> + +<p> + Example: You want to show the user a bar code for some text. The + wrong way to do this is for the intent filter protocol to be + <action android:name="android.intent.action.VIEW" /> and + <data android:scheme="barcode" />. Do not do this. +</p> + +<p> + Instead you should define <action + android:name="com.example.action.SHOW_BARCODE" /> and have the + invoker supply the data as an extra field in the Intent object. +</p> + +<p> + Be aware this intent filter protocol + ("com.example.action.SHOW_BARCODE", in this example) is a public API + that you can't change once it's defined. You must support it in the + future because others are going to be relying on it. If you want to + add new features that are incompatible with the current protocol, + just define a new protocol and continue to support the old one. +</p> +--> + +<h3 id="reusing_tip"> When reusing an activity owned by others, handle the case where no activity matches</h3> + +<p> + Your applications can re-use activities made available from other + applications. In doing so, you cannot presume your intent will always + be resolved to a matching external activity — you must handle the case + where no application installed on the device can handle the intent. +</p> + +<p> + You can either test that an activity matches the intent, which you can do + before starting the activity, or catch an exception if starting the + activity fails. Both approaches are descibed in the blog posting + <a href="http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html">Can + I use this Intent?</a>. +</p> + +<p> + To test whether an intent can be resolved, your code can query the package manager. + The blog post provides an example in the isIntentAvailable() helper method. + You can perform this test when initializing the user interface. + For instance, you could disable the user control that initiates + the Intent object, or display a message to the user that lets them go + to a location, such as the Market, to download its application. + In this way, your code can start the activity (using either startActivity() + or startActivityForResult()) only if the intent has tested to resolve + to an activity that is actually present. +</p> + +<h3 id=activity_launching_tip>Consider how you want your activities to be launched or used by other applications</h3> + +<p> + As a designer or developer, it's up to you to determine how users + start your application and the activities in it. As an application + is a set of activities, the user can start these activities from + Home or from another application. +</p> + + <ul> + <li> + <b>Launch your main activity from an icon at Home </b>- If + your application can run standalone, it should probably be + started by the user touching an icon in <em>application + launcher</em> (typically implemented as a sliding drawer on the + Home screen), or from a shortcut icon on the Home screen, or + from the task switcher. (The mechanism for this is for the + activity to have an + <a href={@docRoot}guide/topics/fundamentals.html#ifilters + title="Intent filter">intent filter</a> with action MAIN and + category LAUNCHER.) + </li> + </ul> + + <ul> + <li> + <b>Launch your activity from within another application</b> - + Perhaps your activities are meant for re-use. For example, + many applications have data they want to share with other + users. Activities that can share data with other users include + email, text messaging and uploading to a public website. <p> + If one or more of your activities can be an alternative to an + existing activity in another application, you can make it + available to users at the point they request that + activity. For example, if your activity can send data to + others (such as by email, text messaging, or uploading), + consider setting up that activity to appear as a choice to the + user. To give a specific example, Gallery enables a user to + view and share pictures. When the user chooses "Share" from + the menus, the system compares the "Share" request (an Intent + object) to available activities (by looking at their intent + filters) and displays choices to share. In this case, it + matches Email, Gmail, Messaging and Picassa. If your activity + can send a picture or upload it to a website, all it needs to + do is make itself available for sharing (by setting its intent + filter). + </p> +<p> + Another activity can start your activity either with or without expecting a result back. +</p> + </li> + + <ul> + <li> + <b>Start an activity expecting a result</b> - This approach + is closed loop, where the activity being started must either + return a valid result or be canceled. In the previous + examples of sharing a photo from a Gallery, the user ends up + back in the Gallery after completing the send or upload + procedure. These are examples of starting an activity + external to the Gallery. (Such an activity is started with + {@link + android.app.Activity#startActivityForResult(android.content.Intent, + int) startActivityForResult()}.) + </li> + + <li> + <b>Start an activity not expecting a result</b> - This + approach is open-ended. An example is choosing an house + address in an email message (or web page), where the Maps + activity is started to map the location. No result from maps + is expected to be returned to the email message; the user + can return by pressing the BACK key. (Such an activity is + started with {@link + android.content.Context#startActivity(android.content.Intent) + startActivity()}.) + </li> + </ul> + + <li> + <b>Launch your activity <em>only</em> from within another + application</b> - The previous cases of sharing by way of + Email, Gmail, Messaging and Picassa (from within Gallery) are + all activities that can also be started from icons in the + application launcher at Home. In contrast, the activities for + cropping a picture and attaching a file cannot be started from + Home, because they do not stand alone and require a + context. + </li> + +<p> + In fact, not all applications have icons and can be started from + Home. Take for example a small app that is infrequently used and + replaces existing functionality, that already has a natural entry + point inside an existing application. For example, an Android phone + typically has a built-in ringtone picker that can be selected from + the sound settings of the Settings application. A custom ringtone + picker application that you write could be launched by an intent + identical to the built-in ringtone picker. At the point where the + user chooses "Phone ringtone", they are presented with a dialog + letting them choose between "Android System" and your ringtone + picker (and letting them save their choice) as shown in the + following figure. A ringtone is something you set infrequently, and + already has a well-defined starting point, so probably does not need + an application icon at Home. +</p> + +<p> + <img src={@docRoot}images/activity_task_design/ActivityChooser.png> +</p> + + <li> + <b>Launch two or more main activities within a single + application from separate icon at Home</b> - As we have + defined it, all the code in a single .apk file is considered + to be one <em>application.</em> You can write an application + that contains two main activities launchable from Home. + </li> + +<p> + The Camera.apk application is a good example of an application that + contains two independent main activities — Camera and + Camcorder — that each have their own icons in application + launcher, that can be launched separately, and so appear to the user + as separate applications. They both share use of the same lens, and + both store their images (still and moving) in the Gallery. +</p> + +<p> + In order for your application to contain two different, independent + activities launchable from Home, you must define them to be + associated with different tasks. (This means setting the main + activity for each task to a different <!--a href=#affinities + title=affinity-->task affinity<!--/a--> — in this case, + "com.android.camera" and "com.android.videocamera".) +</p> + +<p> + Contacts and Dialer are another example of two main activities + launchable from Home that reside in the same application. +</p> + + <li> + <b>Making your application available as a widget</b> - An + application can also display a portion of itself as an <a + href={@docRoot}guide/topics/appwidgets/index.html title="app + widget">app widget</a>, embedded in Home or another + application, and receive periodic updates. + </li> + + </ul> + + +<h3 id=activities_added_to_task_tip>Allow your activities to be added to the current task</h3> + +<p> + If your activities can be started from another application, allow + them to be added to the current <a href=#tasks title=Tasks>task</a> + (or an existing task it has an affinity with). Having activities + added to a task enables the user to switch between a task that + contains your activities and other tasks. <!--See <a href=#tasks + title=Tasks>Tasks</a> for a fuller explanation.--> Exceptions are + your activities that have only one instance. +</p> + +<p> + For this behavior, your activity should have a <!--a + href=launch_modes title="standard or singleTop"-->launch + mode<!--/a--> of standard or singleTop rather than singleTask or + singleInstance. These modes also enable multiple instances of your + activity to be run. +</p> + + +<h3 id="notifications_get_back_tip">Notifications should let the user easily get back to the previous activity</h3> +<p> + Applications that are in the background or not running can have + services that send out notifications to the user letting them know about + events of interest. Two examples are Calendar, which can send out notifications of + upcoming events, and Email, which can send out notifications when new + messages arrive. One of the user interface guidelines is that when the + user is in activity A, gets a notification for activity B and + picks that notification, when they press the BACK key, they should + go back to activity A. +</p> + +<p> + The following scenario shows how the activity stack should work + when the user responds to a notification. +</p> + +<ol> + <li> + User is creating a new event in Calendar. They realize they + need to copy part of an email message into this event + </li> + <li> + The user chooses Home > Gmail + </li> + <li> + While in Gmail, they receive a notification from Calendar for an upcoming meeting + </li> + <li> + So they choose that notification, which takes them to a + dedicated Calendar activity that displays brief details of the + upcoming meeting + </li> + <li> + The user chooses this short notice to view further details + </li> + <li> + When done viewing the event, the user presses the BACK + key. They should be taken to Gmail, which is where they were + when they took the notification + </li> +</ol> + +<p> +This behavior doesn't necessarily happen by default. +</p> + +<p> +Notifications generally happen primarily in one of two ways: +</p> + + <ul> + <li> + <b>The chosen activity is dedicated for notification only</b> - + For example, when the user receives a + Calendar notification, choosing that + notification starts a special activity that displays a list + of upcoming calendar events — this view is available only + from the notification, not through the Calendar's own user + interface. After viewing this upcoming event, to ensure that + the user pressing the BACK key will return to the activity + the user was in when they picked the notification, you would + make sure this dedicated activity does not have the same + task affinity as the Calendar or any other activity. (You do + this by setting task affinity to the empty string, which + means it has no affinity to anything.) The explanation for + this follows. + + <p> + Because of the way tasks work, if the taskAffinity of the + dedicated activity is kept as its default, then pressing the + BACK key (in step 6, above) would go to Calendar, rather + than Gmail. The reason is that, by default, all activities + in a given application have the same task + affinity. Therefore, the task affinity of the dedicated + activity matches the Calendar task, which is already running + in step 1. This means in step 4, choosing the notification + brings the existing Calendar event (in step 1) forward and + starts the dedicated activity on top of it. This is not + what you want to have happen. Setting the dedicated + activity's taskAffinity to empty string fixes this. + </p> + </li> + + <li> + <b>The chosen activity is not dedicated, but always comes to + the foreground in its initial state</b> - For example, in + response to a notification, when the Gmail application comes + to the foreground, it always presents the list of conversations. + You can ensure this happens by setting a "clear top" flag in the + intent that the notification triggers. This ensures that when the + activity is launched, it displays its initial activity, preventing + Gmail from coming to the foreground in whatever state the user last + happened to be viewing it. (To do this, you put {@link + android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to startActivity()). + </li> + </ul> + +<p> + There are other ways to handle notifications, such as bringing the + activity to the foreground, set to display specific data, such as + displaying the text message thread for the person who just sent a + new text message. +</p> + +<p> + A notification always starts an activity as a new task (that is, it + puts <font size=1>FLAG_ACTIVITY_NEW_TASK</font> in the intent it + passes to startActivity()). This is done because interruptions to a + task should not become part of that task. +</p> + +<h3 id=use_notification_tip>Use the notification system — don't use dialog boxes in place of notifications</h3> + +<p> + If your background service needs to notify a user, use the standard + notification system — don't use a dialog or toast to notify + them. A dialog or toast would immediately take focus and interrupt + the user, taking focus away from what they were doing: the user + could be in the middle of typing text the moment the dialog appears + and could accidentally act on the dialog. Users are used to dealing + with notifications and can pull down the notification shade at their + convenience to respond to your message. +</p> + +<h3 id=taking_over_back_key>Don't take over the BACK key unless you absolutely need to</h3> + +<p> + As a user navigates from one activity to the next, the system adds + them to the activity stack. This forms a navigation history that is + accessible with the BACK key. Most activities are relatively limited + in scope, with just one set of data, such as viewing a list of + contacts, composing an email, or taking a photo. But what if your + application is one big activity with several pages of content and + needs finer-grained control of the BACK key? Examples of such Google + applications are the Browser, which can have several web pages open + at once, and Maps, which can have several layers of geographic data + to switch between. Both of these applications take control of the + BACK key and maintain their own internal back stacks that operate + only when these applications have focus. +</p> + +<p> + For example, Maps uses <em>layers</em> to present different + information on a map to the user: displaying the location of a + search result, displaying locations of friends, and displaying a + line for a street path providing direction between points. Maps + stores these layers in its own history so the BACK key can return to + a previous layer. +</p> + +<p> + Similarly, Browser uses <em>browser windows</em> to present different + web pages to the user. Each window has its own navigation history, + equivalent to tabs in a browser in a desktop operating system (such + as Windows, Macintosh or Linux). For example, if you did a Google + web search in one window of the Android Browser, clicking on a link + in the search results displays a web page in that same window, and + then pressing BACK would to the search results page. Pressing + BACK goes to a previous window only if the current window was + launched from that previous window. If the user keeps pressing + back, they will eventually leave the browser activity and return + Home. +</p> + diff --git a/docs/html/guide/practices/ui_guidelines/icon_design.jd b/docs/html/guide/practices/ui_guidelines/icon_design.jd new file mode 100644 index 000000000000..155684a25b93 --- /dev/null +++ b/docs/html/guide/practices/ui_guidelines/icon_design.jd @@ -0,0 +1,1406 @@ +page.title=Icon Design Guidelines +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Icon design quickview</h2> + +<ul> +<li>You can use several types of icons in an Android application.</li> +<li>Your icons should follow the specification in this document.</li> +<li>A set of standard icons is provided by the Android platform. Your +application can use the standard icons by referencing them as resources.</li> +</ul> + +<h2>In this document</h2> + +<ol> +<li><a href="#launcherstructure">Launcher icon</a></li> +<li><a href="#menustructure">Menu icon</a></li> +<li><a href="#statusbarstructure">Status bar icon</a></li> +<li><a href="#tabstructure">Tab icon</a></li> +<li><a href="#dialogstructure">Dialog icon</a></li> +<li><a href="#listviewstructure">List view icon</a></li> + +<li style="margin-top:4px;"><a href="#dodonts">General guidelines</a></li> +<li><a href="#templatespack">Using the Icon Templates Pack</a></li> +<li><a href="#file">Icon appendix</a> + <ol> + <li><a href="#launcherapx">Launcher icons</a></li> + <li><a href="#menuapx">Menu icons</a></li> + <li><a href="#statusbarapx">Status bar icons</a></li> + </ol> +</li> + +</ol> + +<h2>See also</h2> + +<ol> +<li><a href="{@docRoot}shareables/icon_templates-v1.0.zip">Android Icon +Templates Pack »</a></li> +</ol> + +</div> +</div> + +<p>Creating a unified look and feel throughout a user interface adds value to +your product. Streamlining the graphic style will also make the UI seem more +professional to the user.</p> + +<p>This document shows you how to create icons for various parts +of your application’s user interface that fit the style set by the Android UI +team. Following these guidelines will help you to create a polished and unified +experience for the user.</p> + +<p>To get started creating conforming icons more quickly, you can download +the Android Icon Templates Pack. For more information, see +<a href="#templatespack">Using the Android Icon Template Pack</a>.</p> + +<h2 id="launcherstructure">Launcher icon</h2> + +<p>A launcher icon is the graphic that represents your application on an Android +device’s Home screen. It is a simplified 3D icon with a fixed perspective. The +required perspective is shown in Figure 1.</p> + +<h4 id="launcherstructure">Structure</h4> + +<ul> +<li>The base of a launcher icon can face either the top view or the front +view.</li> + +<li>The majority of a launcher icon’s surface should be created using the +launcher icon <a href="#launcherpalette">color palette</a>. To add emphasis, use +one or more bright accent colors to highlight specific characteristics.</li> + +<li>All launcher icons must be created with rounded corners to make them look +friendly and simple—as shown in Figure 2.</li> + +<li>All dimensions specified are based on a 250x250 pixel artboard size +in a vector graphics editor like Adobe Illustrator, where the icon fits within +the artboard boundaries.</li> + +<li><strong>Final art must be scaled down and exported as a transparent 48x48 px +PNG file using a raster image editor such as Adobe Photoshop.</strong></li> + +<li>Templates for creating launcher icons in Adobe Illustrator and Photoshop are +available in the Icon Templates Pack.</li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/launcher_structure.png" alt="A view of +launcher icon corners and perspective angles" /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 1.</strong> Perspective angles for launcher icons (90° is +vertical).</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>92°</td></tr> + <tr><td><em>2.</em></td><td>92°</td></tr> + <tr><td><em>3.</em></td><td>173°</td></tr> + <tr><td><em>4.</em></td><td>171°</td></tr> + <tr><td><em>5.</em></td><td>49°</td></tr> + <tr><td><em>6.</em></td><td>171°</td></tr> + <tr><td><em>7.</em></td><td>64°</td></tr> + <tr><td><em>8.</em></td><td>97°</td></tr> + <tr><td><em>9.</em></td><td>75°</td></tr> + <tr><td><em>10.</em></td><td>93°</td></tr> + <tr><td><em>11.</em></td><td>169°</td></tr> + </table> + </div> + </div> + <div class="caption grad-rule-top"> + <p><strong>Figure 2.</strong> Rounded corners for launcher icons.</p> + </div> +</td> +</tr> +</table> + +<h4 id="launcherlight">Light, effects, and shadows</h4> + +<p>Launcher icons are simplified 3D icons using light and shadows for +definition. A light source is placed slightly to the left in front of the icon, +and therefore the shadow expands to the right and back.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/launcher_light.png" alt="A view of +light, effects, and shadows for launcher icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 3. </strong>Light, effects, and shadows for launcher icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Edge highlight:</td><td>white</td></tr> + <tr><td><em>2.</em></td><td>Icon shadow:</td><td>black | 20px blur<br>50% opacity | angle 67°</td></tr> + <tr><td><em>3.</em></td><td>Front part:</td><td>Use light gradient from color palette</td></tr> + <tr><td><em>4.</em></td><td>Detail shadow:</td><td>black | 10px blur<br>75% opacity</td></tr> + <tr><td><em>5.</em></td><td> Side part:</td><td>Use medium gradient from color palette</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="launcherpalette">Launcher icon color palette</h4> + +<table style="margin:0px;padding:0px;"> +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/launcher_palette_white.png" alt="Color palette, white" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">White<br>r 0 | g 0 | b 0<br>Used for highlights on edges.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/launcher_palette_gradient_light.png" alt="Color palette, light gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Light gradient<br><em>1: </em>r 0 | g 0 | b 0<br><em>2: </em>r 217 | g 217 | b 217<br>Used on the front (lit) part of the icon.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/launcher_palette_gradient_medium.png" alt="Color palette, medium gradien" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Medium gradient<br><em>1: </em>r 190 | g 190 | b 190<br><em>2: </em>r 115 | g 115 | b 115<br>Used on the side (shaded) part of the icon.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/launcher_palette_gradient_dark.png" alt="Color palette, dark gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Dark gradient<br><em>1: </em>r 100 | g 100 | b 100<br><em>2: </em>r 25 | g 25 | b 25<br>Used on details and parts in the shade of the icon.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/launcher_palette_black.png" alt="Color palette, black" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Black<br>r 255 | g 255 | b 255<br>Used as base color in shadows.</td> +</tr> + +</table> + +</td> + +<td style="border:0;width:350px"> + +<h4 id="launchersteps">Step by step</h4> + +<ol> + <li>Create the basic shapes with a tool like Adobe Illustrator, using the +angles described in <a href="#launcherstructure">Launcher icon: structure</a>. +The shapes and effects must fit within a 250x250 pixel artboard.</li> + <li>Add depth to shapes by extruding them and create the rounded corners as +described for the launcher icon structure.</li> + <li>Add details and colors. Gradients should be treated as if there is a light +source placed slightly to the left in front of the icon.</li> + <li>Create the shadows with the correct angle and blur effect.</li> + <li>Import the icon into a tool like Adobe Photoshop and scale to fit an image +size of 48x48 px on a transparent background.</li> + <li>Export the icon at 48x48 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + +<h2 id="menustructure">Menu icon</h2> + +<p>Menu icons are graphical elements placed in the pop-up menu shown to users +when they press the Menu button. They are drawn in a flat-front perspective. +Elements in a menu icon must not be visualized in 3D or perspective.</p> + +<h4>Structure</h4> + +<ul> +<li>In order to maintain consistency, all menu icons must use the same +primary palette and the same effects. For more information, see the +menu icon <a href="#menupalette">color palette</a>. </li> + +<li>Menu icons should include rounded corners, but only when logically +appropriate. For example, in Figure 3 the logical place for rounded corners is +the roof and not the rest of the building.</span></li> + +<li>All dimensions specified on this page are based on a 48x48 pixel artboard +size with a 6 pixel safeframe.</li> + +<li>The menu icon effect (the outer glow) described in <a +href="#menulight">Light, effects, and shadows</a> can overlap the 6px safeframe, +but only when necessary. The base shape must always stay inside the +safeframe.</li> + +<li><strong>Final art must be exported as a transparent PNG file.</strong></li> + +<li>Templates for creating menu icons in Adobe Photoshop are available in the +Icon Templates Pack.</li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/menu_structure.png" alt="A view of menu +icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 4. </strong>Safeframe and corner-rounding for menu +icons. Icon size is 48x48.</p> + </div> +</td> +</tr> +</table> + + +<h4 id="menulight">Light, effects, and shadows</h4> + +<p>Menu icons are flat and pictured face on. A slight deboss and some other +effects, which are shown below, are used to create depth.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/menu_light.png" alt="A view of light, effects, and shadows for launcher icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 5. </strong>Light, effects, and shadows for launcher icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Front part:</td><td>Use fill gradient from primary color palette</td></tr> + <tr><td><em>2.</em></td><td>Inner shadow:</td><td>black | 20 % opacity<br>angle 90° | distance 2px<br>size 2px</td></tr> + <tr><td><em>3.</em></td><td>Outer glow:</td><td>white | 55% opacity <br>spread 10% | size 3px</td></tr> + <tr><td><em>5.</em></td><td>Inner bevel:</td><td>depth 1% | direction down size 0px<br>angle 90° | altitude 10°<br>highlight white 70% opacity<br>shadow black 25% opacity</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menupalette">Color palette</h4> + +<table style="margin:0px;padding:0px;"> +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/menu_palette_white.png" alt="Color palette, white" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">White<br>r 0 | g 0 | b 0<br>Used for outer glow and bevel highlight.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/menu_palette_gradient_medium.png" alt="Color palette, medium gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Fill gradient<br><em>1: </em>r 163 | g 163 | b 163<br><em>2: </em>r 120 | g 120 | b 120<br>Used as color fill.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/menu_palette_black.png" alt="Color palette, black" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Black<br>r 255 | g 255 | b 255<br>Used for inner shadow and bevel shadow.</td> +</tr> + +</table> + +</td> + +<td style="border:0;width:350px"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>Create the basic shapes using a tool like Adobe Illustrator.</li> +<li>Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 48x48 px on a transparent background. Mind the safeframe.</li> +<li>Add the effects seen as described in Figure 5.</li> +<li>Export the icon at 48x48 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + + +<h2 id="statusbarstructure">Status bar icon</h2> + +<p>Status bar icons are used to represent notifications from your application in +the status bar. Graphically, they are very similar to menu icons, but are +smaller and higher in contrast.</p> + +<h4>Structure</h4> + +<ul> +<li>Rounded corners must always be applied to the base shape and to the details +of a status bar icon shown Figure 7.</li> + +<li>All dimensions specified are based on a 25x25 pixel artboard size with a 2 +pixel safeframe.</li> + +<li>Status bar icons can overlap the safeframe to the left and right when +necessary, but must not overlap the safeframe at the top and bottom.</li> + +<li><strong>Final art must be exported as a transparent PNG file.</strong></li> + +<li>Templates for creating status bar icons using Adobe Photoshop are available +in the Icon Templates Pack.</li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/statusbar_structure.png" alt="A view of +status bar icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 6. </strong>Safeframe and corner-rounding for status bar +icons. Icon size is 25x25.</p> + </div> +</td> +</tr> +</table> + + +<h4 id="statusbarlight">Light, effects, and shadows</h4> + +<p>Status bar icons are slightly debossed, high in contrast, and pictured +face-on to enhance clarity at small sizes.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/statusbar_light.png" alt="A view of +light, effects, and shadows for launcher icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 7. </strong>Light, effects, and shadows for launcher icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Front part:</td><td>Use fill gradient from primary color palette</td></tr> + <tr><td><em>2.</em></td><td>Inner bevel:</td><td>depth 100% | direction down<br>size 0px | angle 90° |<br>altitude 30°<br>highlight white 75% opacity<br>shadow black 75% opacity</td></tr> + <tr><td><em>3.</em></td><td>Detail:</td><td>white</td></tr> + <tr><td><em>4.</em></td><td>Disabled detail:</td><td>grey gradient from palette<br>+ inner bevel: smooth | depth 1% | direction down | size 0px | angle 117° | <br>altitude 42° | highlight white 70% | no shadow</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menupalette">Color palette</h4> + +<p>Only status bar icons related to the phone function use full color; all other status bar icons should remain monochromatic.</p> + +<table style="margin:0px;padding:0px;"> +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/statusbar_palette_white.png" alt="Color palette, white" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">White<br>r 0 | g 0 | b 0<br>Used for details within the icons and bevel highlight.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/statusbar_palette_fill.png" alt="Color palette, grey gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Grey gradient<br><em>1: </em>r 169 | g 169 | b 169<br><em>2: </em>r 126 | g 126 | b 126<br>Used for disabled details within the icon.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/statusbar_palette_grey.png" alt="Color palette, fill gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Fill gradient<br><em>1: </em>1 r 105 | g 105 | b 105<br><em>2: </em>r 10 | g 10 | b 10<br>Used as color fill.</td> +</tr> + +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/statusbar_palette_black.png" alt="Color palette, black" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Black<br>r 255 | g 255 | b 255<br>Used for bevel shadow.</td> +</tr> + +</table> + +</td> + +<td style="border:0;width:350px"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>In a tool like Adobe Photoshop, create the base shape within a 25x25 px +image on a transparent background. Mind the safeframe, and keep the upper and +lower 2 pixels free.</li> +<li>Add rounded corners as specified in Figure 6.</li> +<li>Add light, effects, and shadows as specified in Figure 7.</li> +<li>Export the icon at 25x25 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + + +<h2 id="tabstructure">Tab icon</h2> + +<p>Tab icons are graphical elements used to represent individual tabs in a +multi-tab interface. Each tab icon has two states: unselected and selected.</p> + +<h4>Structure</h4> + +<ul> +<li>Unselected tab icons have the same fill gradient and effects as menu icons, +but with no outer glow.</li> + +<li>Selected tab icons look just like unselected tab icons, but with a fainter +inner shadow, and have the same front part gradient as dialog icons.</li> + +<li>Tab icons have a 1 px safeframe which should only be overlapped for the edge +of the anti-alias of a round shape.</li> + +<li>All dimensions specified on this page are based on a 32x32 px artboard size. +Keep 1 px of padding around the bounding box inside the Photoshop template.</li> + +<li><strong>Final art must be exported as a 32x32 px transparent PNG +file.</strong></li> + +<li>Templates for creating tab icons in Adobe Photoshop are available in the +Icon Templates Pack.</li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/tab_icon_unselected.png" alt="A view of +unselected tab icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 8. </strong>Safeframe and fill gradient for unselected tab +icons. Icon size is 32x32.</p> + </div> +</td> +</tr> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/tab_icon_selected.png" alt="A view of +selected tab icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 9. </strong>Safeframe and fill gradient for tab icons in +selected state. Icon size is 32x32.</p> + </div> +</td> +</tr> +</table> + +<h3 id="unselectedtabdetails">Unselected tab icon</h3> + +<h4 id="unselectedtablight">Light, effects, and shadows</h4> + +<p>Unselected tab icons look just like the selected tab icons, but with a +fainter inner shadow, and the same front part gradient as the dialog icons.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/tab_unselected_light.png" alt="A view +of light, effects, and shadows for unselected tab icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 10. </strong>Light, effects, and shadows for unselected +tab icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Front part:</td><td>gradient overlay | angle 90°<br>bottom color: r 223 | g 223 | b 223<br>top color: r 249 | g 249 | b 249<br>bottom color location: 0%<br>top color location: 75%</td></tr> + <tr><td><em>2.</em></td><td>Inner shadow:</td><td>black | 10 % opacity | angle 90° distance 2px | size 2px</td></tr> + <tr><td><em>3.</em></td><td>Inner bevel:</td><td>depth 1% | direction down | size 0px | angle 90° | altitude 10°<br>highlight white 70% opacity<br>shadow black 25% opacity</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>Create the basic shapes using a tool like Adobe Illustrator.</li> +<li>Import the shape to a tool like Adobe Photoshop and scale to fit an image of +32x32 px on a transparent background.</li> +<li>Add the effects seen in Figure 10 for the unselected state filter.</li> +<li>Export the icon at 32x32 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + +<h3 id="selectedtabdetails">Selected tab icon</h3> + +<p>The selected tab icons have the same fill gradient and effects as the menu +icon, but with no outer glow.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/tab_selected_light.png" alt="A view of +light, effects, and shadows for selected tab icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 11. </strong>Light, effects, and shadows for selected tab +icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Front part:</td><td>Use fill gradient from color palette.</td></tr> + <tr><td><em>2.</em></td><td>Inner shadow:</td><td>black | 20% opacity | <br>angle 90° | distance 2px | <br>size 2px</td></tr> + <tr><td><em>3.</em></td><td>Inner bevel:</td><td>depth 1% | direction down | size 0px | angle 90° | <br>altitude 10°<br>highlight white 70% opacity<br>shadow black 25% opacity</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menupalette">Color palette</h4> + +<table style="margin:0px;padding:0px;"> +<tr> +<td class="image-caption-i"><img src="{@docRoot}images/icon_design/menu_palette_gradient_medium.png" alt="Color palette, fill gradient" style="margin:.5em 0 0 0;" /></td> +<td class="image-caption-c" style="padding-top:.5em;">Fill gradient<br><em>1: </em>r 163 | g 163 | b 163<br><em>2: </em>r 120 | g 120 | b 120<br>Used as color fill on unselected tab icons.</td> +</tr> + +</table> + +</td> + +<td style="border:0;width:350px"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>Create the basic shape using a tool like Adobe Illustrator.</li> +<li>Import the shape into a tool like Adobe Photoshop and scale to fit a 32x32 +px artboard with a transparent background. </li> +<li>Add the effects seen in Figure 11 for the selected state filter.</li> +<li>Export the icon at 32x32 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + + +<h2 id="dialogstructure">Dialog icon</h2> + +<p>Dialog icons are shown in pop-up dialog boxes that prompt the user for +interaction. They use a light gradient and inner +shadow in order to stand out against a dark background.</p> + +<h4>Structure</h4> + +<ul> +<li>Dialog icons have a 1 pixel safeframe. The base shape must fit within the +safeframe, but the anti-alias of a round shape can overlap the safeframe. <span +class="body-copy"></li> + +<li>All dimensions specified on this page are based on a 32x32 pixel artboard size +in Adobe Photoshop. Keep 1 pixel of padding around the bounding box inside the +Photoshop template.</li> + +<li><strong>Final art must be exported as a transparent PNG file.</strong></li> + +<li>Templates for creating dialog icons in Adobe Photoshop are available in the +Icon Templates Pack.</li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/dialog_icon.png" alt="A view of dialog +icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 12. </strong>Safeframe and fill gradient for dialog icons. +Icon size is 32x32.</p> + </div> +</td> +</tr> +</table> + + +<h4 id="dialoglight">Light, effects, and shadows</h4> + +<p>Dialog icons are flat and pictured face-on. In order to stand out against a +dark background, they are built up using a light gradient and inner shadow.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/dialog_light.png" alt="A view of light, +effects, and shadows for dialog icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 13. </strong>Light, effects, and shadows for dialog +icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Front part:</td><td>gradient overlay | angle 90°<br>bottom: r 223 | g 223 | b 223<br>top: r 249 | g 249 | b 249<br>bottom color location: 0%<br>top color location: 75%</td></tr> + <tr><td><em>2.</em></td><td>Inner shadow:</td><td>black | 25% opacity | <br>angle -90° | distance 1px | size 0px</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>Create the basic shapes using a tool like Adobe Illustrator.</li> +<li>Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 32x32 px on a transparent background. </li> +<li>Add the effects seen in Figure 13 for the proper filter.</li> +<li>Export the icon at 32x32 as a PNG file with transparency enabled.</li> +</ol> + +</td> +</tr> +</table> + + +<h2 id="listviewstructure">List view icon</h2> + +<p>List view icons look a lot like dialog icons, but they use an inner shadow +effect where the light source is above the object. They are also designed to be +used only in a list view. Examples include the Android Market application home +screen and the driving directions screen in the Maps application.</p> + +<h4>Structure</h4> + +<ul> +<li>A list view icon normally has a 1 px safeframe, but it is OK to use the +safeframe area for the edge of the anti-alias of a round shape. </li> + +<li>All dimensions specified are based on a 32x32 pixel artboard size in +Photoshop. Keep 1 pixel of padding around the bounding box inside the template. + </li> + +<li><strong>Final art must be exported as a transparent PNG file.</strong></li> + +<li>Templates for creating list view icons in Adobe Photoshop are available in +the Icon Templates Pack. </li> +</ul> + +<table class="image-caption"> +<tr> +<td class="image-caption-i" style="padding-right:0"> + <img src="{@docRoot}images/icon_design/listview_icon.png" alt="A view of list +view icon structure." /> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 14. </strong>Safeframe and fill gradient for list view +icons. Icon size is 32x32.</p> + </div> +</td> +</tr> +</table> + +<h4 id="listviewlight">Light, effects, and shadows</h4> + +<p>List view icons are flat and pictured face-on with an inner shadow. Built up +by a light gradient and inner shadow, they stand out well on a dark +background.</p> + +<table class="image-caption"> +<tr> +<td class="image-caption-i"> + <img src="{@docRoot}images/icon_design/listview_icon_details.png" alt="A view +of light, effects, and shadows for list view icons."/> +</td> +<td class="image-caption-c"> + <div class="caption grad-rule-top"> + <p><strong>Figure 15. </strong>Light, effects, and shadows for list view +icons.</p> + <div class="image-caption-nested"> + <table style="margin-top:0;"> + <tr><td style="padding-right:1em"><em>1.</em></td><td>Inner shadow:</td><td>black | 57 % opacity | angle 120° | blend mode normal | distance 1px | size 1px <td></tr> + <tr><td><em>2.</em></td><td>Background:</td><td>black | standard system color <br>These icons are displayed in list views only.</td></tr> + <tr><td colspan="2">Note: The list view icon sits on 32x32 px artboard in Photoshop, without a safeframe.</td></tr> + </table> + </div> + </div> +</td> +</tr> +</table> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4 id="menusteps">Step by step</h4> + +<ol> +<li>Add the effects seen in Figure 15 for the proper filter.</li> +<li>Export the icon at 32x32 as a PNG file with transparency enabled.</li> +<li>Create the basic shapes using a tool like Adobe Illustrator.</li> +<li>Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 32x32 px on a transparent background. </li> +</ol> + +</td> +</tr> +</table> + + +<h2 id="dodonts">General guidelines</h2> + +<p>Below are some "do and don't" guidelines to consider when creating icons for +your application. By following the guidelines, you can ensure that your icons +will work well with other parts of the Android platform UI and will meet the +expectations of your application's users. </p> + +<table style="margin:0px;padding:0px;"> +<tr> +<td style="border:0;width:350px;"> + +<h4>Do...</h4> + +<ul> +<li>Use a normal perspective. The depth of an object should be realistic.</li> +<li>Keep it simple! By overdoing an icon, it loses it purpose and +readability.</li> +<li>Use colors only when necessary. Mind that the base of a launcher icon should +be grey and feel solid. </li> +<li>Use the correct angles for the specific icon types.</li> +</ul> +</td> +<td style="border:0;width:350px;"> + +<h4>Don’t...</h4> + +<ul> +<li>Use open elements like text alone as icons. Instead place those elements on +a base shape.</li> +<li>Use colors for your status bar notifications. Those are reserved for +specific phone-only functions.</li> +</ul> +</td> +</tr> +<tr> +<td colspan="2" style="border:0;"> +<img src="{@docRoot}images/icon_design/do_dont.png" alt="Side-by-side examples +of good/bad icon design."/> +</td> +</table> + +<h2 id="templatespack">Using the Android Icon Templates Pack</h2> + +<p>The Android Icon Templates Pack is a collection of template designs, filters, +and settings that make it easier for you to create icons that conform to the +general specifications given in this document. We recommend downloading the +template pack archive before you get started with your icon design.</p> + +<p>The icon templates are provided in Adobe Photoshop and Adobe Illustrator file +formats, which preserves the layers and design treatments we used when creating the +standard icons for the Android platform. You can load the template files into any +compatible image-editing program, although your ability to work directly with the +layers and treatments may vary based on the program you are using.</p> + +<p>You can obtain the Icon Templates Pack archive using the link below: </p> + +<p style="margin-left:2em"><a +href="{@docRoot}shareables/icon_templates-v1.0.zip">Download the Icon Templates +Pack »</a> + + +<h2 id="iconappendix">Icon appendix</p> + +<h3 id="launcherapx">Standard launcher icons</h3> + +<p>Shown below are examples of launcher icons used by Android applications. The +icons are provided for your reference only — please do not reuse these +icons in your applications.</code>. + +<table class="image-caption"> +<tr> + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_alarmclock.png" alt="Android asset" /> + <div class="caption">Alarm Clock</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_browser.png" alt="Android asset" /> + <div class="caption">Browser</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_calculator.png" alt="Android asset" /> + <div class="caption">Calculator</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_calendar.png" alt="Android asset" /> + <div class="caption">Calendar</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_video_camera.png" alt="Android asset" /> + <div class="caption">Camcorder</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_camera.png" alt="Android asset" /> + <div class="caption">Camera</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_contacts.png" alt="Android asset" /> + <div class="caption">Contacts</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_phone_dialer.png" alt="Android asset" /> + <div class="caption">Dialer</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_email_generic.png" alt="Android asset" /> + <div class="caption">Email</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_gallery.png" alt="Android asset" /> + <div class="caption">Gallery</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_generic_application.png" alt="Android asset" /> + <div class="caption">Generic application</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_email.png" alt="Android asset" /> + <div class="caption">Gmail</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_google_talk.png" alt="Android asset" /> + <div class="caption">Google Talk</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_IM.png" alt="Android asset" /> + <div class="caption">IM</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_maps.png" alt="Android asset" /> + <div class="caption">Maps</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_marketplace.png" alt="Android asset" /> + <div class="caption">Market </div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_sms_mms.png" alt="Android asset" /> + <div class="caption">Messaging </div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_musicplayer_2.png" alt="Android asset" /> + <div class="caption">Music</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_settings.png" alt="Android asset" /> + <div class="caption">Settings</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_voicedial.png" alt="Android asset" /> + <div class="caption">Voice Dialer</div></td> + + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_voicesearch.png" alt="Android asset" /> + <div class="caption">Voice Search</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="/images/icon_design/ic_launcher_youtube.png" alt="Android asset" /> + <div class="caption">YouTube</div></td> +</tr> +</table> + +<h3 id="menuapx">Standard menu icons</h3> + +<p>Shown below are standard menu icons that are included in the Android platform +(as of Android 1.5). You can reference any of these icon resources from your +application as needed, but make sure that the action you assign to the icon is +consistent with that listed. Note that this is not a complete list of icons and +that the actual appearance of standard icons may change across platform +versions.</p> + +<p>To reference one of the icons from your code, use +<code>android.R.drawable.<icon_resource_identifier></code>. For example, +you can call a menu item's {@link android.view.MenuItem#setIcon(android.graphics.drawable.Drawable) setIcon()} +method and pass the resource name:</p> + +<p style="margin-left:2em"><code>.setIcon(android.R.drawable.ic_menu_more);</code>. + +<p>You could reference the same icon from a layout file using +<code>android:icon="@android:drawable/ic_menu_more"></code>.</p> + +<p>To determine the resource ID for an icon listed below, hover over the icon or +simply look at image filenames, which use the format +"<icon_resource_identifier>.png".</p> + +<table class="image-caption"> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_add.png" title="ic_menu_add" alt="Android asset" /> + <div class="caption">Add</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_archive.png" title="ic_menu_archive" alt="Android asset" /> + <div class="caption">Archive</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_attachment.png" title="ic_menu_attachment" alt="Android asset" /> + <div class="caption">Attach</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_back.png" title="ic_menu_back" alt="Android asset" /> + <div class="caption">Back</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_call.png" title="ic_menu_call" alt="Android asset" /> + <div class="caption">Call</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_camera.png" title="ic_menu_camera" alt="Android asset" /> + <div class="caption">Camera</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_close_clear_cancel.png" title="ic_menu_close_clear_cancel" alt="Android asset" /> + <div class="caption">Clear / Close / Cancel / Discard </div></td> + +</tr> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_compass.png" title="ic_menu_compass" alt="Android asset" /> + <div class="caption">Compass</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_delete.png" title="ic_menu_delete" alt="Android asset" /> + <div class="caption">Delete</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_directions.png" title="ic_menu_directions" alt="Android asset" /> + <div class="caption">Directions</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_edit.png" title="ic_menu_edit" alt="Android asset" /> + <div class="caption">Edit</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_favorite.png" title="ic_menu_favorite" alt="Android asset" /> + <div class="caption">Favorite</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_forward.png" title="ic_menu_forward" alt="Android asset" /> + <div class="caption">Forward</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_gallery.png" title="ic_menu_gallery" alt="Android asset" /> + <div class="caption">Gallery</div></td> + +</tr> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_goto.png" title="ic_menu_goto" alt="Android asset" /> + <div class="caption">Go to</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_help.png" title="ic_menu_help" alt="Android asset" /> + <div class="caption">Help</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_recent_history.png" title="ic_menu_recent_history" alt="Android asset" /> + <div class="caption">History</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_home.png" title="ic_menu_home" alt="Android asset" /> + <div class="caption">Home</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_info_details.png" title="ic_menu_info_details" alt="Android asset" /> + <div class="caption">Info / details</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_mapmode.png" title="ic_menu_mapmode" alt="Android asset" /> + <div class="caption">Map mode</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_mark.png" title="ic_menu_mark" alt="Android asset" /> + <div class="caption">Mark</div></td> + +</tr> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_mylocation.png" title="ic_menu_mylocation" alt="Android asset" /> + <div class="caption">My Location</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_more.png" title="ic_menu_more" alt="Android asset" /> + <div class="caption">More</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_play_clip.png" title="ic_menu_play_clip" alt="Android asset" /> + <div class="caption">Play</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_preferences.png" title="ic_menu_preferences" alt="Android asset" /> + <div class="caption">Preferences</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_refresh.png" title="ic_menu_refresh" alt="Android asset" /> + <div class="caption">Refresh</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_rotate.png" title="ic_menu_rotate" alt="Android asset" /> + <div class="caption">Rotate</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_save.png" title="ic_menu_save" alt="Android asset" /> + <div class="caption">Save</div></td> + +</tr> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_send.png" title="ic_menu_send" alt="Android asset" /> + <div class="caption">Send</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_search.png" title="ic_menu_search" alt="Android asset" /> + <div class="caption">Search</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_share.png" title="ic_menu_share" alt="Android asset" /> + <div class="caption">Share</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_shuffle.png" title="ic_menu_shuffle" alt="Android asset" /> + <div class="caption">Shuffle</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_upload.png" title="ic_menu_upload" alt="Android asset" /> + <div class="caption">Upload</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_view.png" title="ic_menu_view" alt="Android asset" /> + <div class="caption">View</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_camera_video_view.png" title="ic_menu_camera_video_view" alt="Android asset" /> + <div class="caption">Video</div></td> + +</tr> +<tr> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/ic_menu_zoom.png" title="ic_menu_zoom" alt="Android asset" /> + <div class="caption">Zoom</div></td> + +</tr> +</table> + + +<h3 id="statusbarapx">Standard status bar icons</h3> + +<p>Shown below are standard status bar icons included in the Android platform +(as of Android 1.5). You can reference any of these icon resources from your +application as needed, but make sure that the meaning of the icon is consistent +with the standard meaning listed. Note that this is not a complete list of icons +and that the actual appearance of standard icons may change across platform +versions.</p> + +<p>To reference one of the icons from your code, use +<code>android.R.drawable.<icon_resource_identifier></code>. For example, +you can construct a simple notification that references one of the icons like +this: </p> + +<p style="margin-left:2em"><code>new Notification(R.drawable.stat_notify_calendar, +"sample text", System.currentTimeMillis());</code></p> + +<p>To determine the resource ID for an icon listed below, hover over the icon +or simply look at the image filename, which use the format +"<icon_resource_identifier>.png".</p> + + +<table class="image-caption"> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_connected_3g.png" title="stat_sys_data_connected_3g" alt="Android asset" /> + <div class="caption">3G</div></td> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_signal_flightmode.png" title="stat_sys_signal_flightmode" alt="Android asset" /> + <div class="caption">Airplane mode</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_alarm.png" title="stat_notify_alarm" alt="Android asset" /> + <div class="caption">Alarm</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_bluetooth.png" title="stat_sys_data_bluetooth" alt="Android asset" /> + <div class="caption">Bluetooth</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_bluetooth_connected.png" title="stat_sys_data_bluetooth_connected" alt="Android asset" /> + <div class="caption">Bluetooth connected</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_calendar.png" title="stat_notify_calendar" alt="Android asset" /> + <div class="caption">Calendar</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_disk_full.png" title="stat_notify_disk_full" alt="Android asset" /> + <div class="caption">Disk full</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_connected_e.png" title="stat_sys_data_connected_e" alt="Android asset" /> + <div class="caption">EDGE</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_email_generic.png" title="stat_notify_email_generic" alt="Android asset" /> + <div class="caption">Email</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_email.png" title="stat_notify_email" alt="Android asset" /> + <div class="caption">Gmail</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_connected_g.png" title="stat_sys_data_connected_g" alt="Android asset" /> + <div class="caption">GPRS</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_chat.png" title="stat_notify_chat" alt="Android asset" /> + <div class="caption">IM</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_install_complete.png" title="stat_sys_install_complete" alt="Android asset" /> + <div class="caption">Installation complete</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_musicplayer.png" title="stat_notify_musicplayer" alt="Android asset" /> + <div class="caption">Music</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_r_signal_4.png" title="stat_sys_r_signal_4" alt="Android asset" /> + <div class="caption">Roaming</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_signal_4.png" title="stat_sys_signal_4" alt="Android asset" /> + <div class="caption">Signal</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_signal_null.png" title="stat_sys_signal_null" alt="Android asset" /> + <div class="caption">Signal unavailable</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_ringer_silent_old.png" title="stat_sys_ringer_silent_old" alt="Android asset" /> + <div class="caption">Silent mode</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_sms.png" title="stat_notify_sms" alt="Android asset" /> + <div class="caption">SMS/MMS</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_speakerphone.png" title="stat_sys_speakerphone" alt="Android asset" /> + <div class="caption">Speaker phone</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_sync_anim0.png" title="stat_notify_sync_anim0" alt="Android asset" /> + <div class="caption">Sync</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_sync_error.png" title="stat_notify_sync_error" alt="Android asset" /> + <div class="caption">Sync error</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_data_usb.png" title="stat_sys_data_usb" alt="Android asset" /> + <div class="caption">USB connected</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_ringer_vibrate.png" title="stat_sys_ringer_vibrate" alt="Android asset" /> + <div class="caption">Vibrate</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_voicemail.png" title="stat_notify_voicemail" alt="Android asset" /> + <div class="caption">Voicemail</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_warning.png" title="stat_sys_warning" alt="Android asset" /> + <div class="caption">Warning</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_wifi_signal_4.png" title="stat_sys_wifi_signal_4" alt="Android asset" /> + <div class="caption">WiFi</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_wifi_in_range.png" title="stat_notify_wifi_in_range" alt="Android asset" /> + <div class="caption">WiFi network available</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_wifi_unavailable.png" title="stat_sys_wifi_unavailable" alt="Android asset" /> + <div class="caption">WiFi unavailable</div></td> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_battery_100.png" title="stat_sys_battery_100" alt="Android asset" /> + <div class="caption">Battery 100%</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_battery_empty.png" title="stat_sys_battery_empty" alt="Android asset" /> + <div class="caption">Battery empty</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_phone_call.png" title="stat_sys_phone_call" alt="Android asset" /> + <div class="caption">Call</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_phone_call_forward.png" title="stat_sys_phone_call_forward" alt="Android asset" /> + <div class="caption">Call forward</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_phone_call_on_hold.png" title="stat_sys_phone_call_on_hold" alt="Android asset" /> + <div class="caption">Call on hold</div></td> + + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_sys_gps_on.png" title="stat_sys_gps_on" alt="Android asset" /> + <div class="caption">GPS on</div></td> + +</tr> +<tr> + +<td class="image-caption-i image-list"> + <img src="{@docRoot}images/icon_design/stat_notify_missed_call.png" title="stat_notify_missed_call" alt="Android asset" /> + <div class="caption">Missed call</div></td> + +</tr> +</table> + + diff --git a/docs/html/guide/practices/ui_guidelines/index.jd b/docs/html/guide/practices/ui_guidelines/index.jd index e19d5b49f3ff..0b9d2754d21a 100644 --- a/docs/html/guide/practices/ui_guidelines/index.jd +++ b/docs/html/guide/practices/ui_guidelines/index.jd @@ -6,18 +6,39 @@ page.title=User Interface Guidelines <p>The Android UI team has begun developing guidelines for the interaction and -design of Android applications. Look here for articles that describe these -visual guidelines as we release them.</p> +visual design of Android applications. Look here for articles that describe +these guidelines as we release them.</p> - <dl> - <dt><a href="widget_design.html">Widget Design Guidelines</a> </dt> - <dd>Widgets are a new feature introduced in Cupcake. A widget displays -an application's most important or timely information at a glance, on a user's -Home screen. These design guidelines describe how to design widgets that fit -with others on the Home screen. They include links to graphics files and -templates that will make your designer's life easier.</dd> - + <dt><a href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon +Design Guidelines</a> and <a +href="{@docRoot}shareables/icon_templates-v1.0.zip">Android Icon Templates Pack +» </a></dt> + <dd>Your applications need a wide variety of icons, from a launcher icon to +icons in menus, dialogs, tabs, the status bar, and lists. The Icon Guidelines +describe each kind of icon in detail, with specifications for the size, color, +shading, and other details for making all your icons fit in the Android system. +The Icon Templates Pack is an archive of Photoshop and Illustrator templates and +filters that make it much simpler to create conforming icons.</dd> +</dl> + <dl> + <dt><a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">Widget Design Guidelines</a> </dt> + <dd>A widget displays an application's most important or timely information +at a glance, on a user's Home screen. These design guidelines describe how to +design widgets that fit with others on the Home screen. They include links to +graphics files and templates that will make your designer's life easier.</dd> +</dl> + <dl> + <dt><a href="{@docRoot}guide/practices/ui_guidelines/activity_task_design.html">Activity and Task Design Guidelines</a> </dt> + <dd>Activities are the basic, independent building blocks of applications. + As you design your application's UI and feature set, you are free to + re-use activities from other applications as if they were yours, + to enrich and extend your application. These guidelines + describe how activities work, illustrates them with examples, and + describes important underlying principles and mechanisms, such as + multitasking, activity reuse, intents, the activity stack, and + tasks. It covers this all from a high-level design perspective. +</dd> </dl> 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 000000000000..d738b3f0c866 --- /dev/null +++ b/docs/html/guide/topics/resources/res-selection-flowchart.png diff --git a/docs/html/guide/topics/resources/resources-i18n.jd b/docs/html/guide/topics/resources/resources-i18n.jd index 4bbb44ac5e5a..2bcc5e33deea 100644..100755 --- a/docs/html/guide/topics/resources/resources-i18n.jd +++ b/docs/html/guide/topics/resources/resources-i18n.jd @@ -37,7 +37,7 @@ 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 +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> @@ -88,12 +88,12 @@ 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 +subfolder they are in, and the format of the file. Table 1 shows 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> - +<p class="caption">Table 1</p> <table width="100%" border="1"> <tr> <th scope="col">Directory</th> @@ -410,7 +410,7 @@ public class MyActivity extends Activity <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 +<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 @@ -436,7 +436,7 @@ MyApp/ 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> +resources for a fully specified configuration would look like this:</p> <pre> MyApp/ @@ -444,8 +444,7 @@ MyApp/ 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 +<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> @@ -457,41 +456,46 @@ MyApp/ 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>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> + <td>MCC and MNC</td> + <td>The mobile country code and mobile network code 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>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> + <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. </td> - </tr> + <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> </td> - </tr> + <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> @@ -506,6 +510,14 @@ MyApp/ <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, @@ -513,92 +525,84 @@ 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> +<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>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> + <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 Android finds the best matching directory </h3> +<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. The selection process -is as follows:</p> - +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 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> + 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> - -<a name="ResourcesTerminology"></a> -<h2>Terminology</h2> - +<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 @@ -671,7 +675,7 @@ 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 +orientation, etc. The current configuration is used to select which resource values are in effect when the resource table is loaded.</p> @@ -710,4 +714,3 @@ 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> - diff --git a/docs/html/guide/tutorials/hello-world.jd b/docs/html/guide/tutorials/hello-world.jd index 4d1e9cdf5b67..79b723d162d6 100644 --- a/docs/html/guide/tutorials/hello-world.jd +++ b/docs/html/guide/tutorials/hello-world.jd @@ -29,7 +29,7 @@ You can then return to this tutorial and ignore anything about Eclipse.</p> <p>Before you start, you should already have the very latest SDK installed, and if you're using Eclipse, you should have installed the ADT plugin as well. If you have not installed these, see -<a href="{@docRoot}sdk/1.1_r1/installing.html">Installing the Android SDK</a> and return +<a href="{@docRoot}sdk/{@sdkCurrent}/installing.html">Installing the Android SDK</a> and return here when you've completed the installation.</p> <h2 id="avd">Create an AVD</h2> @@ -80,7 +80,7 @@ Android project in Eclipse.</p> "Android XML File" will also be available.)</p> </li> - <li>Selected "Android Project" and click <strong>Next</strong>. + <li>Select "Android Project" and click <strong>Next</strong>.<br/> <a href="images/hello_world_0.png"><img src="images/hello_world_0.png" style="height:230px" alt="" /></a> </li> @@ -147,7 +147,7 @@ Android project in Eclipse.</p> built against the 1.1 platform library will run normally on the 1.5 platform. The reverse is not true.</p> </li> -</ul> +</ol> <p>Your Android project is now ready. It should be visible in the Package Explorer on the left. diff --git a/docs/html/images/activity_task_design/ActivityChooser.png b/docs/html/images/activity_task_design/ActivityChooser.png Binary files differnew file mode 100644 index 000000000000..6c20afbbd21c --- /dev/null +++ b/docs/html/images/activity_task_design/ActivityChooser.png diff --git a/docs/html/images/activity_task_design/ContactNew.png b/docs/html/images/activity_task_design/ContactNew.png Binary files differnew file mode 100644 index 000000000000..decaaeb0b1e4 --- /dev/null +++ b/docs/html/images/activity_task_design/ContactNew.png diff --git a/docs/html/images/activity_task_design/ContactView.png b/docs/html/images/activity_task_design/ContactView.png Binary files differnew file mode 100644 index 000000000000..5eff2ba6d1fc --- /dev/null +++ b/docs/html/images/activity_task_design/ContactView.png diff --git a/docs/html/images/activity_task_design/ContactsDialer.png b/docs/html/images/activity_task_design/ContactsDialer.png Binary files differnew file mode 100644 index 000000000000..28794b7f2576 --- /dev/null +++ b/docs/html/images/activity_task_design/ContactsDialer.png diff --git a/docs/html/images/activity_task_design/ContactsList.png b/docs/html/images/activity_task_design/ContactsList.png Binary files differnew file mode 100644 index 000000000000..ef1b83f48db1 --- /dev/null +++ b/docs/html/images/activity_task_design/ContactsList.png diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1a.png b/docs/html/images/activity_task_design/HomeTaskBasics1a.png Binary files differnew file mode 100644 index 000000000000..eca480705b12 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskBasics1a.png diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1b.png b/docs/html/images/activity_task_design/HomeTaskBasics1b.png Binary files differnew file mode 100644 index 000000000000..ce76d634f341 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskBasics1b.png diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1c.png b/docs/html/images/activity_task_design/HomeTaskBasics1c.png Binary files differnew file mode 100644 index 000000000000..95f48c1084b4 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskBasics1c.png diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1d.png b/docs/html/images/activity_task_design/HomeTaskBasics1d.png Binary files differnew file mode 100644 index 000000000000..bbb96d9b33d9 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskBasics1d.png diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1e.png b/docs/html/images/activity_task_design/HomeTaskBasics1e.png Binary files differnew file mode 100644 index 000000000000..09dd491cf335 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskBasics1e.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1a.png b/docs/html/images/activity_task_design/HomeTaskSwitching1a.png Binary files differnew file mode 100644 index 000000000000..de79aaf953ea --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching1a.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1b.png b/docs/html/images/activity_task_design/HomeTaskSwitching1b.png Binary files differnew file mode 100644 index 000000000000..bce77724a8be --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching1b.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1c.png b/docs/html/images/activity_task_design/HomeTaskSwitching1c.png Binary files differnew file mode 100644 index 000000000000..8209f2fbab0b --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching1c.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching2.png b/docs/html/images/activity_task_design/HomeTaskSwitching2.png Binary files differnew file mode 100644 index 000000000000..dee58a397a94 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching2.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3a.png b/docs/html/images/activity_task_design/HomeTaskSwitching3a.png Binary files differnew file mode 100644 index 000000000000..0c90a86a1ddb --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching3a.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3b.png b/docs/html/images/activity_task_design/HomeTaskSwitching3b.png Binary files differnew file mode 100644 index 000000000000..4a16e69ddb46 --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching3b.png diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3c.png b/docs/html/images/activity_task_design/HomeTaskSwitching3c.png Binary files differnew file mode 100644 index 000000000000..d7789aa8f31f --- /dev/null +++ b/docs/html/images/activity_task_design/HomeTaskSwitching3c.png diff --git a/docs/html/images/activity_task_design/IntentsDiagram.png b/docs/html/images/activity_task_design/IntentsDiagram.png Binary files differnew file mode 100644 index 000000000000..0ed366ff721d --- /dev/null +++ b/docs/html/images/activity_task_design/IntentsDiagram.png diff --git a/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png b/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png Binary files differnew file mode 100644 index 000000000000..8d346c178869 --- /dev/null +++ b/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png diff --git a/docs/html/images/activity_task_design/ReplacingAnActivity.png b/docs/html/images/activity_task_design/ReplacingAnActivity.png Binary files differnew file mode 100644 index 000000000000..03b4d929a747 --- /dev/null +++ b/docs/html/images/activity_task_design/ReplacingAnActivity.png diff --git a/docs/html/images/activity_task_design/ReusingAnActivity1.png b/docs/html/images/activity_task_design/ReusingAnActivity1.png Binary files differnew file mode 100644 index 000000000000..01c1729bcbc8 --- /dev/null +++ b/docs/html/images/activity_task_design/ReusingAnActivity1.png diff --git a/docs/html/images/activity_task_design/ReusingAnActivity2.png b/docs/html/images/activity_task_design/ReusingAnActivity2.png Binary files differnew file mode 100644 index 000000000000..288d2da757d0 --- /dev/null +++ b/docs/html/images/activity_task_design/ReusingAnActivity2.png diff --git a/docs/html/images/icon_design/dialog_icon.png b/docs/html/images/icon_design/dialog_icon.png Binary files differnew file mode 100644 index 000000000000..9f924223db05 --- /dev/null +++ b/docs/html/images/icon_design/dialog_icon.png diff --git a/docs/html/images/icon_design/dialog_light.png b/docs/html/images/icon_design/dialog_light.png Binary files differnew file mode 100644 index 000000000000..85056a9ff7a3 --- /dev/null +++ b/docs/html/images/icon_design/dialog_light.png diff --git a/docs/html/images/icon_design/do_dont.png b/docs/html/images/icon_design/do_dont.png Binary files differnew file mode 100644 index 000000000000..bc6d649c2c99 --- /dev/null +++ b/docs/html/images/icon_design/do_dont.png diff --git a/docs/html/images/icon_design/ic_launcher_IM.png b/docs/html/images/icon_design/ic_launcher_IM.png Binary files differnew file mode 100644 index 000000000000..afc35a2977fb --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_IM.png diff --git a/docs/html/images/icon_design/ic_launcher_alarmclock.png b/docs/html/images/icon_design/ic_launcher_alarmclock.png Binary files differnew file mode 100644 index 000000000000..30ff2671a2f6 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_alarmclock.png diff --git a/docs/html/images/icon_design/ic_launcher_browser.png b/docs/html/images/icon_design/ic_launcher_browser.png Binary files differnew file mode 100644 index 000000000000..f58b84a0bc13 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_browser.png diff --git a/docs/html/images/icon_design/ic_launcher_calculator.png b/docs/html/images/icon_design/ic_launcher_calculator.png Binary files differnew file mode 100644 index 000000000000..298c267ace33 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_calculator.png diff --git a/docs/html/images/icon_design/ic_launcher_calendar.png b/docs/html/images/icon_design/ic_launcher_calendar.png Binary files differnew file mode 100644 index 000000000000..92410907d70b --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_calendar.png diff --git a/docs/html/images/icon_design/ic_launcher_camera.png b/docs/html/images/icon_design/ic_launcher_camera.png Binary files differnew file mode 100644 index 000000000000..c2d760642613 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_camera.png diff --git a/docs/html/images/icon_design/ic_launcher_contacts.png b/docs/html/images/icon_design/ic_launcher_contacts.png Binary files differnew file mode 100644 index 000000000000..826656ffb528 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_contacts.png diff --git a/docs/html/images/icon_design/ic_launcher_email.png b/docs/html/images/icon_design/ic_launcher_email.png Binary files differnew file mode 100644 index 000000000000..2fb263787ee2 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_email.png diff --git a/docs/html/images/icon_design/ic_launcher_email_generic.png b/docs/html/images/icon_design/ic_launcher_email_generic.png Binary files differnew file mode 100644 index 000000000000..590ed705d3d3 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_email_generic.png diff --git a/docs/html/images/icon_design/ic_launcher_gallery.png b/docs/html/images/icon_design/ic_launcher_gallery.png Binary files differnew file mode 100644 index 000000000000..965fb714309c --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_gallery.png diff --git a/docs/html/images/icon_design/ic_launcher_generic_application.png b/docs/html/images/icon_design/ic_launcher_generic_application.png Binary files differnew file mode 100644 index 000000000000..75024841d327 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_generic_application.png diff --git a/docs/html/images/icon_design/ic_launcher_google_talk.png b/docs/html/images/icon_design/ic_launcher_google_talk.png Binary files differnew file mode 100644 index 000000000000..1618eb3dca8b --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_google_talk.png diff --git a/docs/html/images/icon_design/ic_launcher_maps.png b/docs/html/images/icon_design/ic_launcher_maps.png Binary files differnew file mode 100644 index 000000000000..f436b56d0a61 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_maps.png diff --git a/docs/html/images/icon_design/ic_launcher_marketplace.png b/docs/html/images/icon_design/ic_launcher_marketplace.png Binary files differnew file mode 100644 index 000000000000..f1f578ddfe4e --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_marketplace.png diff --git a/docs/html/images/icon_design/ic_launcher_musicplayer_2.png b/docs/html/images/icon_design/ic_launcher_musicplayer_2.png Binary files differnew file mode 100644 index 000000000000..0353b9115f42 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_musicplayer_2.png diff --git a/docs/html/images/icon_design/ic_launcher_phone_dialer.png b/docs/html/images/icon_design/ic_launcher_phone_dialer.png Binary files differnew file mode 100644 index 000000000000..4e613ecce9e9 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_phone_dialer.png diff --git a/docs/html/images/icon_design/ic_launcher_settings.png b/docs/html/images/icon_design/ic_launcher_settings.png Binary files differnew file mode 100644 index 000000000000..16db056f788e --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_settings.png diff --git a/docs/html/images/icon_design/ic_launcher_sms_mms.png b/docs/html/images/icon_design/ic_launcher_sms_mms.png Binary files differnew file mode 100644 index 000000000000..e2ac7843aeba --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_sms_mms.png diff --git a/docs/html/images/icon_design/ic_launcher_video_camera.png b/docs/html/images/icon_design/ic_launcher_video_camera.png Binary files differnew file mode 100644 index 000000000000..e80255a5d6f9 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_video_camera.png diff --git a/docs/html/images/icon_design/ic_launcher_voicedial.png b/docs/html/images/icon_design/ic_launcher_voicedial.png Binary files differnew file mode 100644 index 000000000000..0c84fbac8dc3 --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_voicedial.png diff --git a/docs/html/images/icon_design/ic_launcher_voicesearch.png b/docs/html/images/icon_design/ic_launcher_voicesearch.png Binary files differnew file mode 100644 index 000000000000..09d51995a15c --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_voicesearch.png diff --git a/docs/html/images/icon_design/ic_launcher_youtube.png b/docs/html/images/icon_design/ic_launcher_youtube.png Binary files differnew file mode 100644 index 000000000000..48d268da5d1b --- /dev/null +++ b/docs/html/images/icon_design/ic_launcher_youtube.png diff --git a/docs/html/images/icon_design/ic_menu_add.png b/docs/html/images/icon_design/ic_menu_add.png Binary files differnew file mode 100644 index 000000000000..6752bfd10072 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_add.png diff --git a/docs/html/images/icon_design/ic_menu_archive.png b/docs/html/images/icon_design/ic_menu_archive.png Binary files differnew file mode 100644 index 000000000000..a4599e37a063 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_archive.png diff --git a/docs/html/images/icon_design/ic_menu_attachment.png b/docs/html/images/icon_design/ic_menu_attachment.png Binary files differnew file mode 100644 index 000000000000..89d626f6cd68 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_attachment.png diff --git a/docs/html/images/icon_design/ic_menu_back.png b/docs/html/images/icon_design/ic_menu_back.png Binary files differnew file mode 100644 index 000000000000..5ce50ebf179f --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_back.png diff --git a/docs/html/images/icon_design/ic_menu_call.png b/docs/html/images/icon_design/ic_menu_call.png Binary files differnew file mode 100644 index 000000000000..a63f86b16e56 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_call.png diff --git a/docs/html/images/icon_design/ic_menu_camera.png b/docs/html/images/icon_design/ic_menu_camera.png Binary files differnew file mode 100644 index 000000000000..cdf7ca31b737 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_camera.png diff --git a/docs/html/images/icon_design/ic_menu_camera_video_view.png b/docs/html/images/icon_design/ic_menu_camera_video_view.png Binary files differnew file mode 100644 index 000000000000..f7e52c25bb60 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_camera_video_view.png diff --git a/docs/html/images/icon_design/ic_menu_close_clear_cancel.png b/docs/html/images/icon_design/ic_menu_close_clear_cancel.png Binary files differnew file mode 100644 index 000000000000..619858c2bace --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_close_clear_cancel.png diff --git a/docs/html/images/icon_design/ic_menu_compass.png b/docs/html/images/icon_design/ic_menu_compass.png Binary files differnew file mode 100644 index 000000000000..7717dde51db1 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_compass.png diff --git a/docs/html/images/icon_design/ic_menu_delete.png b/docs/html/images/icon_design/ic_menu_delete.png Binary files differnew file mode 100644 index 000000000000..7d954943d5da --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_delete.png diff --git a/docs/html/images/icon_design/ic_menu_directions.png b/docs/html/images/icon_design/ic_menu_directions.png Binary files differnew file mode 100644 index 000000000000..67d3ff21fceb --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_directions.png diff --git a/docs/html/images/icon_design/ic_menu_edit.png b/docs/html/images/icon_design/ic_menu_edit.png Binary files differnew file mode 100644 index 000000000000..41a9c2e20b24 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_edit.png diff --git a/docs/html/images/icon_design/ic_menu_favorite.png b/docs/html/images/icon_design/ic_menu_favorite.png Binary files differnew file mode 100644 index 000000000000..527d74ac670a --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_favorite.png diff --git a/docs/html/images/icon_design/ic_menu_forward.png b/docs/html/images/icon_design/ic_menu_forward.png Binary files differnew file mode 100644 index 000000000000..0936fac4e49a --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_forward.png diff --git a/docs/html/images/icon_design/ic_menu_gallery.png b/docs/html/images/icon_design/ic_menu_gallery.png Binary files differnew file mode 100644 index 000000000000..f61bbd8bae60 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_gallery.png diff --git a/docs/html/images/icon_design/ic_menu_goto.png b/docs/html/images/icon_design/ic_menu_goto.png Binary files differnew file mode 100644 index 000000000000..40183ebc2f7a --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_goto.png diff --git a/docs/html/images/icon_design/ic_menu_help.png b/docs/html/images/icon_design/ic_menu_help.png Binary files differnew file mode 100644 index 000000000000..7c55dfd6936b --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_help.png diff --git a/docs/html/images/icon_design/ic_menu_home.png b/docs/html/images/icon_design/ic_menu_home.png Binary files differnew file mode 100644 index 000000000000..34943f6607f5 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_home.png diff --git a/docs/html/images/icon_design/ic_menu_info_details.png b/docs/html/images/icon_design/ic_menu_info_details.png Binary files differnew file mode 100644 index 000000000000..1786d1e2280b --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_info_details.png diff --git a/docs/html/images/icon_design/ic_menu_mapmode.png b/docs/html/images/icon_design/ic_menu_mapmode.png Binary files differnew file mode 100644 index 000000000000..d85cab5d6b1d --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_mapmode.png diff --git a/docs/html/images/icon_design/ic_menu_mark.png b/docs/html/images/icon_design/ic_menu_mark.png Binary files differnew file mode 100644 index 000000000000..5e95da75a694 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_mark.png diff --git a/docs/html/images/icon_design/ic_menu_more.png b/docs/html/images/icon_design/ic_menu_more.png Binary files differnew file mode 100644 index 000000000000..20915277cbf3 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_more.png diff --git a/docs/html/images/icon_design/ic_menu_mylocation.png b/docs/html/images/icon_design/ic_menu_mylocation.png Binary files differnew file mode 100644 index 000000000000..14b0af882b44 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_mylocation.png diff --git a/docs/html/images/icon_design/ic_menu_play_clip.png b/docs/html/images/icon_design/ic_menu_play_clip.png Binary files differnew file mode 100644 index 000000000000..466994744c9d --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_play_clip.png diff --git a/docs/html/images/icon_design/ic_menu_preferences.png b/docs/html/images/icon_design/ic_menu_preferences.png Binary files differnew file mode 100644 index 000000000000..b8e71412d8ab --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_preferences.png diff --git a/docs/html/images/icon_design/ic_menu_recent_history.png b/docs/html/images/icon_design/ic_menu_recent_history.png Binary files differnew file mode 100644 index 000000000000..4ccae5d12fae --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_recent_history.png diff --git a/docs/html/images/icon_design/ic_menu_refresh.png b/docs/html/images/icon_design/ic_menu_refresh.png Binary files differnew file mode 100644 index 000000000000..77d70dd4f053 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_refresh.png diff --git a/docs/html/images/icon_design/ic_menu_rotate.png b/docs/html/images/icon_design/ic_menu_rotate.png Binary files differnew file mode 100644 index 000000000000..27368b2454f5 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_rotate.png diff --git a/docs/html/images/icon_design/ic_menu_save.png b/docs/html/images/icon_design/ic_menu_save.png Binary files differnew file mode 100644 index 000000000000..36d50b38766f --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_save.png diff --git a/docs/html/images/icon_design/ic_menu_search.png b/docs/html/images/icon_design/ic_menu_search.png Binary files differnew file mode 100644 index 000000000000..94446db976cf --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_search.png diff --git a/docs/html/images/icon_design/ic_menu_send.png b/docs/html/images/icon_design/ic_menu_send.png Binary files differnew file mode 100644 index 000000000000..74c096dc949d --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_send.png diff --git a/docs/html/images/icon_design/ic_menu_share.png b/docs/html/images/icon_design/ic_menu_share.png Binary files differnew file mode 100644 index 000000000000..44db9b16e245 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_share.png diff --git a/docs/html/images/icon_design/ic_menu_shuffle.png b/docs/html/images/icon_design/ic_menu_shuffle.png Binary files differnew file mode 100644 index 000000000000..cb7009deaa2c --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_shuffle.png diff --git a/docs/html/images/icon_design/ic_menu_upload.png b/docs/html/images/icon_design/ic_menu_upload.png Binary files differnew file mode 100644 index 000000000000..1c0dd3f67178 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_upload.png diff --git a/docs/html/images/icon_design/ic_menu_view.png b/docs/html/images/icon_design/ic_menu_view.png Binary files differnew file mode 100644 index 000000000000..69828a9ebb28 --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_view.png diff --git a/docs/html/images/icon_design/ic_menu_zoom.png b/docs/html/images/icon_design/ic_menu_zoom.png Binary files differnew file mode 100644 index 000000000000..0b8c4e8f99ab --- /dev/null +++ b/docs/html/images/icon_design/ic_menu_zoom.png diff --git a/docs/html/images/icon_design/icon_guidelines_logo.png b/docs/html/images/icon_design/icon_guidelines_logo.png Binary files differnew file mode 100644 index 000000000000..9362c8f5263a --- /dev/null +++ b/docs/html/images/icon_design/icon_guidelines_logo.png diff --git a/docs/html/images/icon_design/launcher_light.png b/docs/html/images/icon_design/launcher_light.png Binary files differnew file mode 100644 index 000000000000..8a94e1dcb59d --- /dev/null +++ b/docs/html/images/icon_design/launcher_light.png diff --git a/docs/html/images/icon_design/launcher_palette_black.png b/docs/html/images/icon_design/launcher_palette_black.png Binary files differnew file mode 100644 index 000000000000..fba096f301f9 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_black.png diff --git a/docs/html/images/icon_design/launcher_palette_dark.png b/docs/html/images/icon_design/launcher_palette_dark.png Binary files differnew file mode 100644 index 000000000000..37355427e872 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_dark.png diff --git a/docs/html/images/icon_design/launcher_palette_gradient_dark.png b/docs/html/images/icon_design/launcher_palette_gradient_dark.png Binary files differnew file mode 100644 index 000000000000..37355427e872 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_gradient_dark.png diff --git a/docs/html/images/icon_design/launcher_palette_gradient_light.png b/docs/html/images/icon_design/launcher_palette_gradient_light.png Binary files differnew file mode 100644 index 000000000000..f1121ebf87b8 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_gradient_light.png diff --git a/docs/html/images/icon_design/launcher_palette_gradient_medium.png b/docs/html/images/icon_design/launcher_palette_gradient_medium.png Binary files differnew file mode 100644 index 000000000000..1442b17b7393 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_gradient_medium.png diff --git a/docs/html/images/icon_design/launcher_palette_light.png b/docs/html/images/icon_design/launcher_palette_light.png Binary files differnew file mode 100644 index 000000000000..f1121ebf87b8 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_light.png diff --git a/docs/html/images/icon_design/launcher_palette_medium.png b/docs/html/images/icon_design/launcher_palette_medium.png Binary files differnew file mode 100644 index 000000000000..1442b17b7393 --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_medium.png diff --git a/docs/html/images/icon_design/launcher_palette_white.png b/docs/html/images/icon_design/launcher_palette_white.png Binary files differnew file mode 100644 index 000000000000..8d7ac41e5efc --- /dev/null +++ b/docs/html/images/icon_design/launcher_palette_white.png diff --git a/docs/html/images/icon_design/launcher_structure.png b/docs/html/images/icon_design/launcher_structure.png Binary files differnew file mode 100644 index 000000000000..53e4d9a971ca --- /dev/null +++ b/docs/html/images/icon_design/launcher_structure.png diff --git a/docs/html/images/icon_design/listview_icon.png b/docs/html/images/icon_design/listview_icon.png Binary files differnew file mode 100644 index 000000000000..5711d88b2b63 --- /dev/null +++ b/docs/html/images/icon_design/listview_icon.png diff --git a/docs/html/images/icon_design/listview_icon_details.png b/docs/html/images/icon_design/listview_icon_details.png Binary files differnew file mode 100644 index 000000000000..5a684162f7bf --- /dev/null +++ b/docs/html/images/icon_design/listview_icon_details.png diff --git a/docs/html/images/icon_design/menu_light.png b/docs/html/images/icon_design/menu_light.png Binary files differnew file mode 100644 index 000000000000..93ed38bf6785 --- /dev/null +++ b/docs/html/images/icon_design/menu_light.png diff --git a/docs/html/images/icon_design/menu_palette_black.png b/docs/html/images/icon_design/menu_palette_black.png Binary files differnew file mode 100644 index 000000000000..fba096f301f9 --- /dev/null +++ b/docs/html/images/icon_design/menu_palette_black.png diff --git a/docs/html/images/icon_design/menu_palette_fill.png b/docs/html/images/icon_design/menu_palette_fill.png Binary files differnew file mode 100644 index 000000000000..7079bda6f778 --- /dev/null +++ b/docs/html/images/icon_design/menu_palette_fill.png diff --git a/docs/html/images/icon_design/menu_palette_gradient_medium.png b/docs/html/images/icon_design/menu_palette_gradient_medium.png Binary files differnew file mode 100644 index 000000000000..a806adb09dbb --- /dev/null +++ b/docs/html/images/icon_design/menu_palette_gradient_medium.png diff --git a/docs/html/images/icon_design/menu_palette_white.png b/docs/html/images/icon_design/menu_palette_white.png Binary files differnew file mode 100644 index 000000000000..8d7ac41e5efc --- /dev/null +++ b/docs/html/images/icon_design/menu_palette_white.png diff --git a/docs/html/images/icon_design/menu_structure.png b/docs/html/images/icon_design/menu_structure.png Binary files differnew file mode 100644 index 000000000000..ab140154698a --- /dev/null +++ b/docs/html/images/icon_design/menu_structure.png diff --git a/docs/html/images/icon_design/stat_notify_alarm.png b/docs/html/images/icon_design/stat_notify_alarm.png Binary files differnew file mode 100644 index 000000000000..1b01b850619d --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_alarm.png diff --git a/docs/html/images/icon_design/stat_notify_calendar.png b/docs/html/images/icon_design/stat_notify_calendar.png Binary files differnew file mode 100644 index 000000000000..4433a16ae5b5 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_calendar.png diff --git a/docs/html/images/icon_design/stat_notify_chat.png b/docs/html/images/icon_design/stat_notify_chat.png Binary files differnew file mode 100644 index 000000000000..238f0437ed5b --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_chat.png diff --git a/docs/html/images/icon_design/stat_notify_disk_full.png b/docs/html/images/icon_design/stat_notify_disk_full.png Binary files differnew file mode 100644 index 000000000000..9120f0037509 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_disk_full.png diff --git a/docs/html/images/icon_design/stat_notify_email.png b/docs/html/images/icon_design/stat_notify_email.png Binary files differnew file mode 100644 index 000000000000..d84a2471c172 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_email.png diff --git a/docs/html/images/icon_design/stat_notify_email_generic.png b/docs/html/images/icon_design/stat_notify_email_generic.png Binary files differnew file mode 100644 index 000000000000..686033f8db0e --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_email_generic.png diff --git a/docs/html/images/icon_design/stat_notify_missed_call.png b/docs/html/images/icon_design/stat_notify_missed_call.png Binary files differnew file mode 100644 index 000000000000..fe746b3b10cb --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_missed_call.png diff --git a/docs/html/images/icon_design/stat_notify_musicplayer.png b/docs/html/images/icon_design/stat_notify_musicplayer.png Binary files differnew file mode 100644 index 000000000000..fd92c1888ec4 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_musicplayer.png diff --git a/docs/html/images/icon_design/stat_notify_sms.png b/docs/html/images/icon_design/stat_notify_sms.png Binary files differnew file mode 100644 index 000000000000..b437d5b77206 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_sms.png diff --git a/docs/html/images/icon_design/stat_notify_sync_anim0.png b/docs/html/images/icon_design/stat_notify_sync_anim0.png Binary files differnew file mode 100644 index 000000000000..0edf69200c68 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_sync_anim0.png diff --git a/docs/html/images/icon_design/stat_notify_sync_error.png b/docs/html/images/icon_design/stat_notify_sync_error.png Binary files differnew file mode 100644 index 000000000000..3078b8c56af5 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_sync_error.png diff --git a/docs/html/images/icon_design/stat_notify_voicemail.png b/docs/html/images/icon_design/stat_notify_voicemail.png Binary files differnew file mode 100644 index 000000000000..658fa0520cae --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_voicemail.png diff --git a/docs/html/images/icon_design/stat_notify_wifi_in_range.png b/docs/html/images/icon_design/stat_notify_wifi_in_range.png Binary files differnew file mode 100644 index 000000000000..e9c74b481b05 --- /dev/null +++ b/docs/html/images/icon_design/stat_notify_wifi_in_range.png diff --git a/docs/html/images/icon_design/stat_sys_battery_100.png b/docs/html/images/icon_design/stat_sys_battery_100.png Binary files differnew file mode 100644 index 000000000000..d280aebb08bd --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_battery_100.png diff --git a/docs/html/images/icon_design/stat_sys_battery_empty.png b/docs/html/images/icon_design/stat_sys_battery_empty.png Binary files differnew file mode 100644 index 000000000000..4a5e99e75cc6 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_battery_empty.png diff --git a/docs/html/images/icon_design/stat_sys_data_bluetooth.png b/docs/html/images/icon_design/stat_sys_data_bluetooth.png Binary files differnew file mode 100644 index 000000000000..7a8b78f6e8db --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_bluetooth.png diff --git a/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png b/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png Binary files differnew file mode 100644 index 000000000000..f09b83bfad67 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png diff --git a/docs/html/images/icon_design/stat_sys_data_connected_3g.png b/docs/html/images/icon_design/stat_sys_data_connected_3g.png Binary files differnew file mode 100644 index 000000000000..a1092807a9cf --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_connected_3g.png diff --git a/docs/html/images/icon_design/stat_sys_data_connected_e.png b/docs/html/images/icon_design/stat_sys_data_connected_e.png Binary files differnew file mode 100644 index 000000000000..c55264447967 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_connected_e.png diff --git a/docs/html/images/icon_design/stat_sys_data_connected_g.png b/docs/html/images/icon_design/stat_sys_data_connected_g.png Binary files differnew file mode 100644 index 000000000000..f7edb49954c4 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_connected_g.png diff --git a/docs/html/images/icon_design/stat_sys_data_usb.png b/docs/html/images/icon_design/stat_sys_data_usb.png Binary files differnew file mode 100644 index 000000000000..2d0da4c8ec1b --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_data_usb.png diff --git a/docs/html/images/icon_design/stat_sys_gps_on.png b/docs/html/images/icon_design/stat_sys_gps_on.png Binary files differnew file mode 100644 index 000000000000..a2c677d9ec39 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_gps_on.png diff --git a/docs/html/images/icon_design/stat_sys_install_complete.png b/docs/html/images/icon_design/stat_sys_install_complete.png Binary files differnew file mode 100644 index 000000000000..62dba5ba7de4 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_install_complete.png diff --git a/docs/html/images/icon_design/stat_sys_phone_call.png b/docs/html/images/icon_design/stat_sys_phone_call.png Binary files differnew file mode 100644 index 000000000000..ad5369399221 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_phone_call.png diff --git a/docs/html/images/icon_design/stat_sys_phone_call_forward.png b/docs/html/images/icon_design/stat_sys_phone_call_forward.png Binary files differnew file mode 100644 index 000000000000..ed4b6ec1289f --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_phone_call_forward.png diff --git a/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png b/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png Binary files differnew file mode 100644 index 000000000000..921644765daf --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png diff --git a/docs/html/images/icon_design/stat_sys_r_signal_4.png b/docs/html/images/icon_design/stat_sys_r_signal_4.png Binary files differnew file mode 100644 index 000000000000..f04fb11b4e73 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_r_signal_4.png diff --git a/docs/html/images/icon_design/stat_sys_ringer_silent_old.png b/docs/html/images/icon_design/stat_sys_ringer_silent_old.png Binary files differnew file mode 100644 index 000000000000..d125ce5bef36 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_ringer_silent_old.png diff --git a/docs/html/images/icon_design/stat_sys_ringer_vibrate.png b/docs/html/images/icon_design/stat_sys_ringer_vibrate.png Binary files differnew file mode 100644 index 000000000000..665ca38fefbd --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_ringer_vibrate.png diff --git a/docs/html/images/icon_design/stat_sys_signal_4.png b/docs/html/images/icon_design/stat_sys_signal_4.png Binary files differnew file mode 100644 index 000000000000..a3320cbb4fd2 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_signal_4.png diff --git a/docs/html/images/icon_design/stat_sys_signal_flightmode.png b/docs/html/images/icon_design/stat_sys_signal_flightmode.png Binary files differnew file mode 100644 index 000000000000..516ec2f4258e --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_signal_flightmode.png diff --git a/docs/html/images/icon_design/stat_sys_signal_null.png b/docs/html/images/icon_design/stat_sys_signal_null.png Binary files differnew file mode 100644 index 000000000000..5aa23f6c7348 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_signal_null.png diff --git a/docs/html/images/icon_design/stat_sys_speakerphone.png b/docs/html/images/icon_design/stat_sys_speakerphone.png Binary files differnew file mode 100644 index 000000000000..642dfd489153 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_speakerphone.png diff --git a/docs/html/images/icon_design/stat_sys_warning.png b/docs/html/images/icon_design/stat_sys_warning.png Binary files differnew file mode 100644 index 000000000000..be00f470ad6a --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_warning.png diff --git a/docs/html/images/icon_design/stat_sys_wifi_signal_4.png b/docs/html/images/icon_design/stat_sys_wifi_signal_4.png Binary files differnew file mode 100644 index 000000000000..2062aada3e3e --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_wifi_signal_4.png diff --git a/docs/html/images/icon_design/stat_sys_wifi_unavailable.png b/docs/html/images/icon_design/stat_sys_wifi_unavailable.png Binary files differnew file mode 100644 index 000000000000..53dd45b31c55 --- /dev/null +++ b/docs/html/images/icon_design/stat_sys_wifi_unavailable.png diff --git a/docs/html/images/icon_design/statusbar_light.png b/docs/html/images/icon_design/statusbar_light.png Binary files differnew file mode 100644 index 000000000000..ddebc2dcb1fa --- /dev/null +++ b/docs/html/images/icon_design/statusbar_light.png diff --git a/docs/html/images/icon_design/statusbar_palette_black.png b/docs/html/images/icon_design/statusbar_palette_black.png Binary files differnew file mode 100644 index 000000000000..fba096f301f9 --- /dev/null +++ b/docs/html/images/icon_design/statusbar_palette_black.png diff --git a/docs/html/images/icon_design/statusbar_palette_fill.png b/docs/html/images/icon_design/statusbar_palette_fill.png Binary files differnew file mode 100644 index 000000000000..bbf652c5af52 --- /dev/null +++ b/docs/html/images/icon_design/statusbar_palette_fill.png diff --git a/docs/html/images/icon_design/statusbar_palette_grey.png b/docs/html/images/icon_design/statusbar_palette_grey.png Binary files differnew file mode 100644 index 000000000000..0abb7f4b454a --- /dev/null +++ b/docs/html/images/icon_design/statusbar_palette_grey.png diff --git a/docs/html/images/icon_design/statusbar_palette_white.png b/docs/html/images/icon_design/statusbar_palette_white.png Binary files differnew file mode 100644 index 000000000000..8d7ac41e5efc --- /dev/null +++ b/docs/html/images/icon_design/statusbar_palette_white.png diff --git a/docs/html/images/icon_design/statusbar_structure.png b/docs/html/images/icon_design/statusbar_structure.png Binary files differnew file mode 100644 index 000000000000..e7243ee6713f --- /dev/null +++ b/docs/html/images/icon_design/statusbar_structure.png diff --git a/docs/html/images/icon_design/tab_icon_selected.png b/docs/html/images/icon_design/tab_icon_selected.png Binary files differnew file mode 100644 index 000000000000..66a847507352 --- /dev/null +++ b/docs/html/images/icon_design/tab_icon_selected.png diff --git a/docs/html/images/icon_design/tab_icon_unselected.png b/docs/html/images/icon_design/tab_icon_unselected.png Binary files differnew file mode 100644 index 000000000000..80ae9c1c9639 --- /dev/null +++ b/docs/html/images/icon_design/tab_icon_unselected.png diff --git a/docs/html/images/icon_design/tab_palette_selected_fill.png b/docs/html/images/icon_design/tab_palette_selected_fill.png Binary files differnew file mode 100644 index 000000000000..7079bda6f778 --- /dev/null +++ b/docs/html/images/icon_design/tab_palette_selected_fill.png diff --git a/docs/html/images/icon_design/tab_selected_light.png b/docs/html/images/icon_design/tab_selected_light.png Binary files differnew file mode 100644 index 000000000000..3a87c5b1ed63 --- /dev/null +++ b/docs/html/images/icon_design/tab_selected_light.png diff --git a/docs/html/images/icon_design/tab_unselected_light.png b/docs/html/images/icon_design/tab_unselected_light.png Binary files differnew file mode 100644 index 000000000000..f888161875c3 --- /dev/null +++ b/docs/html/images/icon_design/tab_unselected_light.png diff --git a/docs/html/index.jd b/docs/html/index.jd index 883170a3aa87..07d0abe7f6e4 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -5,32 +5,36 @@ home=true <div id="mainBodyFixed"> <div id="mainBodyLeft"> <div id="homeMiddle"> - <div id="homeTitle"> - <h2>Developer Announcements</h2> - </div><!-- end homeTitle --> - <div id="announcement-block"> - <!-- total max width is 520px --> - <img src="{@docRoot}assets/images/home/IO-logo.png" alt="Google I/O Developer Conference 2009" width="242px" /> - <div id="announcement" style="width:270px"> - <p>Google I/O is a two-day developer event that will take place May 27-28 at Moscone Center, San Francisco. The agenda includes a number of great sessions on Android topics by team engineers and other developers.</p> - <p><a href="http://code.google.com/events/io/">Learn more »</a></p> - </div> <!-- end annoucement --> - </div> <!-- end annoucement-block --> - <div id="carouselMain"> - <div id="bulletinImg"></div> - <div id="bulletinDesc"></div> - </div> - <div class="clearer"></div> - <div class="app-list-container" align="center"> - <a href="javascript:{}" id="arrow-left" onclick="" class="arrow-left-off"></a> - <div id="list-clip"> - <div style="left: 0px;" id="app-list"> - <!-- populated by buildCarousel() --> - </div> - </div><!-- end list-clip --> - <a href="javascript:page_right()" id="arrow-right" onclick="" class="arrow-right-on"></a> + <div id="topAnnouncement"> + <div id="homeTitle"> + <h2>Developer Announcements</h2> + </div><!-- end homeTitle --> + <div id="announcement-block"> + <!-- total max width is 520px --> + <img src="/assets/images/home/IO-logo.png" alt="Google I/O Developer Conference 2009" width="242px" /> + <div id="announcement" style="width:270px"> + <p>Google I/O is a two-day developer event that will take place May 27-28 at Moscone Center, San Francisco. The agenda includes a number of great sessions on Android topics by team engineers and other developers.</p> + <p><a href="http://code.google.com/events/io/">Learn more »</a></p> + </div> <!-- end annoucement --> + </div> <!-- end annoucement-block --> + </div><!-- end topAnnouncement --> + <div id="carousel"> + <div id="carouselMain"> + <div id="bulletinImg"></div> + <div id="bulletinDesc"></div> + </div> <div class="clearer"></div> - </div><!-- end app-list container --> + <div class="app-list-container" align="center"> + <a href="javascript:{}" id="arrow-left" onclick="" class="arrow-left-off"></a> + <div id="list-clip"> + <div style="left: 0px;" id="app-list"> + <!-- populated by buildCarousel() --> + </div> + </div><!-- end list-clip --> + <a href="javascript:page_right()" id="arrow-right" onclick="" class="arrow-right-on"></a> + <div class="clearer"></div> + </div><!-- end app-list container --> + </div><!-- end carousel --> </div><!-- end homeMiddle --> <div style="clear:both"> </div> @@ -116,10 +120,10 @@ home=true 'sdk': { 'layout':"imgLeft", 'icon':"sdk-small.png", - 'name':"SDK 1.5 r1", + 'name':"SDK 1.5 r2", 'img':"sdk-large.png", - 'title':"Android 1.5 SDK r1", - 'desc': "<p>The final version of the Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.</p><p><a href='{@docRoot}sdk/1.5_r1/index.html'>Download Android 1.5 SDK</a></p>" + 'title':"Android 1.5 SDK", + 'desc': "<p>Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.</p><p><a href='{@docRoot}sdk/1.5_r2/index.html'>Download Android 1.5 SDK</a></p>" }, 'mapskey': { diff --git a/docs/html/robots.txt b/docs/html/robots.txt index 085b79dc1b68..7046373e9570 100644 --- a/docs/html/robots.txt +++ b/docs/html/robots.txt @@ -1,7 +1,8 @@ -User-Agent: *
-Allow: /
-Disallow: /gae_shell/
-Disallow: /assets/
-Disallow: /images/
-Disallow: /sdk/preview/
-Sitemap: http://developer.android.com/sitemap.txt
+User-Agent: * +Allow: / +Disallow: /gae_shell/ +Disallow: /assets/ +Disallow: /images/ +Disallow: /sdk/preview/ +Disallow: /shareables/ +Sitemap: http://developer.android.com/sitemap.txt diff --git a/docs/html/sdk/1.5_r1/index.jd b/docs/html/sdk/1.5_r1/index.jd index 438ee4bb80ce..405f56ce960a 100644 --- a/docs/html/sdk/1.5_r1/index.jd +++ b/docs/html/sdk/1.5_r1/index.jd @@ -1,6 +1,7 @@ sdk.version=1.5 sdk.rel.id=1 sdk.date=April 2009 +sdk.not_latest_version=true sdk.win_download=android-sdk-windows-1.5_r1.zip sdk.win_bytes=176263368 diff --git a/docs/html/sdk/1.5_r2/index.jd b/docs/html/sdk/1.5_r2/index.jd new file mode 100644 index 000000000000..15342a433992 --- /dev/null +++ b/docs/html/sdk/1.5_r2/index.jd @@ -0,0 +1,87 @@ +sdk.version=1.5 +sdk.rel.id=2 +sdk.date=May 2009 + +sdk.win_download=android-sdk-windows-1.5_r2.zip +sdk.win_bytes=178346828 +sdk.win_checksum=ba54ac6bda45921d442b74b6de6ff6a9 + +sdk.mac_download=android-sdk-mac_x86-1.5_r2.zip +sdk.mac_bytes=169945128 +sdk.mac_checksum=f4e06a5194410243f213d0177713d6c9 + +sdk.linux_download=android-sdk-linux_x86-1.5_r2.zip +sdk.linux_bytes=165035130 +sdk.linux_checksum=1d3c3d099e95a31c43a7b3e6ae307ed3 + +page.title=Android 1.5 SDK, Release 2 +@jd:body + +<p>For more information on this SDK release, read the +<a href="{@docRoot}sdk/RELEASENOTES.html#1.5_r2">Release Notes</a>.</p> + +<h2>SDK Contents</h2> + +<h4>Development tools</h4> + +<p>The SDK includes a full set of tools for developing and debugging application code and designing an application UI. You can read about the tools in the +<a href="{@docRoot}guide/developing/tools/index.html">Dev Guide</a> and access them in the <code><sdk>/tools/</code> directory. + +<p>The tools package in this SDK includes updates from those provided in the previous SDK. The tools also require a different project structure. To use the new tools, you need to migrate your applications to the new development environment. For more information about how to migrate, see <a href="{@docRoot}sdk/1.5_r2/upgrading.html">Upgrading the SDK</a>. + +<p>For more information about the new tools features, see the <a href="{@docRoot}sdk/RELEASENOTES.html">SDK Release Notes</a>. + +<h4 id="system_images">Android Platforms</h4> + +<p>This SDK includes multiple Android platform versions that you use to develop applications. For each version, both a fully compliant Android library and system image are provided. The table below lists the platform versions included in this SDK. For more information about a platform version — features, applications included, localizations, API changes, and so on — see its Version Notes. </p> + +<table style="margin-right:1em;" width="80%"> +<tr> +<th><nobr>Platform</nobr></th><th><nobr>API Level</nobr></th><th>Notes</th><th>Description</th> +</tr> + +<tr> +<td width="5%"><nobr>Android 1.5</nobr></td> +<td width="5%">3</td> +<td width="5%"><nobr><a href="{@docRoot}sdk/android-1.5.html">Version Notes</a></nobr></td> +<td>Includes a standard Android 1.5 library and system image with a set of development applications. Does not include any external libraries (such as the Maps external library).</td> +</tr> +<tr> +<td width="5%"><nobr>Android 1.1</nobr></td> +<td width="5%">2</td> +<td width="5%"><nobr><a href="{@docRoot}sdk/android-1.1.html">Version Notes</a></nobr></td> +<td>Includes a compliant Android 1.1 library and system image with a set of development applications. Also includes the Maps external library (due to legacy build system issues).</td> +</tr> +</table> + +<h4 id="system_images">SDK Add-Ons</h4> + +<p>An SDK add-on provides a development environment for an Android external library or a customized (but fully compliant) Android system image. This SDK includes the SDK add-on listed below. The Android system API Level required by the add-on is noted.</p> + +<table style="margin-right:1em;" width="80%"> +<tr> +<th><nobr>Add-On</nobr></th><th><nobr>API Level</nobr></th><th>Notes</th><th>Description</th> +</tr> +<tr> +<td width="5%"><nobr>Google APIs</nobr></td> +<td width="5%">3</td> +<td width="5%"> </td> +<td>Includes the com.google.android.maps external library, a compliant +system image, a {@link android.location.Geocoder Geocoder} +backend service implementation, documentation, and sample code. </td> +</tr> +</table> + +<h4>Sample Code and Applications</h4> + +<p>You can look at a variety of tutorials and samples in the <a href="{@docRoot}guide/samples/index.html">Dev Guide</a> and access the sample code itself +in the <code><sdk>/platforms/android-1.5/samples/</code> directory of the SDK package. Note the new location — the SDK now includes multiple platform versions that you can develop against and each has its own sample code directory. </p> + +<h4>Documentation</h4> + +<p>The SDK package includes a full set of local documentation. To view it, open the <code><sdk>/documentation.html</code> file in a web browser. If you are developing in an IDE such as Eclipse, you can also view the reference documentation directly in the IDE. </p> + +<p>The most current documentation is always available on the Android Developers site:</p> + +<p style="margin-left:2em;"><a href="http://developer.android.com/index.html">http://developer.android.com/</a></p> + diff --git a/docs/html/sdk/1.5_r2/installing.jd b/docs/html/sdk/1.5_r2/installing.jd new file mode 100644 index 000000000000..69b2c1bef533 --- /dev/null +++ b/docs/html/sdk/1.5_r2/installing.jd @@ -0,0 +1,332 @@ +sdk.version=1.5 +sdk.rel.id=2 +sdk.date=April 2009 + +page.title=Installing the Android SDK +@jd:body + + +<p>This page describes how to install the Android SDK and set up your +development environment. If you haven't downloaded the SDK, you can +do so from the +<a href="{@docRoot}sdk/1.5_r2/index.html">Download</a> page. Once you've downloaded +the SDK, return here.</p> + +<p>If you encounter any problems during installation, see the +<a href="#installnotes">Installation Notes</a> at the bottom of +this page.</p> + +<h4 style="margin-top">Upgrading?</h4> +<p>If you have already developed applications using an earlier version +of the SDK, please read +<a href="{@docRoot}sdk/1.5_r2/upgrading.html"><strong>Upgrading the +SDK</strong></a></b>, instead. +</p> + + +<h2 id="setup">Preparing for Installation</h2> + +<p>Before you begin, take a moment to confirm that your development machine meets the +<a href="{@docRoot}sdk/1.5_r2/requirements.html">System Requirements</a>. +</p> + +<p>If you will be developing on Eclipse with the Android Development +Tools (ADT) Plugin — the recommended path if you are new to +Android — make sure that you have a suitable version of Eclipse +installed on your computer (3.3 or newer). If you need to install Eclipse, you can +download it from this location: </p> + +<p style="margin-left:2em;"><a href= +"http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a +></p> + +<p>A Java or RCP version of Eclipse is recommended. </p> + +<h2 id="installingsdk">Installing the SDK</h2> + +<p>After downloading the SDK, unpack the .zip archive to a suitable location on your machine. +By default, the SDK files are unpacked into a directory named +<code>android_sdk_<em><platform</em>>_<em><release></em></code>. +The directory contains a local copy of the documentation (accessible by opening +<code>documentation.html</code> in your browser) and the subdirectories +<code>tools/</code>, <code>add-ons/</code>, <code>platforms/</code>, and others. Inside +each subdirectory of <code>platforms/</code> you'll find <code>samples/</code>, which includes +code samples that are specific to each version of the platform.</p> + +<p>Make a note of the name and location of the unpacked SDK directory on your system — you +will need to refer to the SDK directory later, when setting up the Android plugin or when +using the SDK tools.</p> + +<p>Optionally, you may want to add the location of the SDK's primary <code>tools</code> directory +to your system PATH. The primary <code>tools/</code> directory is located at the root of the +SDK folder. Adding <code>tools</code> to your path lets you run Android Debug Bridge (adb) and +the other command line <a href="{@docRoot}guide/developing/tools/index.html">tools</a> without +needing to supply the full path to the tools directory. </p> +<ul> + <li>On Linux, edit your <code>~/.bash_profile</code> or <code>~/.bashrc</code> file. Look + for a line that sets the PATH environment variable and add the + full path to the <code>tools/</code> directory to it. If you don't + see a line setting the path, you can add one:</li> + + <ul><code>export PATH=${PATH}:<em><your_sdk_dir></em>/tools</code></ul> + + <li>On a Mac, look in your home directory for <code>.bash_profile</code> and + proceed as for Linux. You can create the <code>.bash_profile</code> if + you haven't already set one up on your machine. </li> + + <li>On Windows, right-click on My Computer, and select Properties. + Under the Advanced tab, hit the Environment Variables button, and in the + dialog that comes up, double-click on Path (under System Variables). Add the full path to the + <code>tools/</code> directory to the path. </li> + </ul> + +<p>Note that, if you update your SDK in the future, you +should remember to update your PATH settings to point to the new location, if different.</p> + +<p>If you will be using the Eclipse IDE as your development environment, +the next section describes how to install the Android Development Tools plugin and set up Eclipse. +If you choose not to use Eclipse, you can +develop Android applications in an IDE of your choice and then compile, debug and deploy using +the tools included in the SDK (skip to <a href="#next">Next Steps</a>).</p> + + +<h2 id="installingplugin">Installing the ADT Plugin for Eclipse</h2> + +<p>Android offers a custom plugin for the Eclipse IDE, called Android +Development Tools (ADT), that is designed to give you a powerful, +integrated environment in which to build Android applications. It +extends the capabilites of Eclipse to let you quickly set up new Android +projects, create an application UI, add components based on the Android +Framework API, debug your applications using the Android SDK tools, and even export +signed (or unsigned) APKs in order to distribute your application.</p> + +<p>In general, using Eclipse with ADT is a highly recommended +approach to Android development and is the fastest way to get started. +(If you prefer to work in an IDE other than Eclipse, +you do not need to install Eclipse or ADT, instead, you can directly +use the SDK tools to build and debug your application.)</p> + +<p>Once you have Eclipse installed, as described in <a href="#setup">Preparing for +Installation</a>, follow the steps below to +download the ADT plugin and install it in your respective Eclipse +environment. </p> + +<table style="font-size:100%"> +<tr><th>Eclipse 3.3 (Europa)</th><th>Eclipse 3.4 (Ganymede)</th></tr> +<tr> +<td width="45%"> +<!-- 3.3 steps --> +<ol> + <li>Start Eclipse, then select <strong>Help</strong> > <strong>Software Updates</strong> +> <strong>Find and Install...</strong>. </li> + <li>In the dialog that appears, select <strong>Search for new features to install</strong> +and click <strong>Next</strong>. </li> + <li>Click <strong>New Remote Site</strong>. </li> + <li>In the resulting dialog box, enter a name for the remote site (e.g. "Android Plugin") and + enter the URL: + <pre>https://dl-ssl.google.com/android/eclipse/</pre> + <p>If you have trouble aqcuiring the plugin, try using "http" in the URL, + instead of "https" (https is preferred for security reasons).</p> + <p>Click <strong>OK</strong>.</p> </li> + <li>You should now see the new site added to the search list (and checked). + Click <strong>Finish</strong>. </li> + <li>In the subsequent Search Results dialog box, select the checkbox for the + "Android Plugin". + This will select the nested tools: "Android DDMS" and "Android Development Tools". + Click <strong>Next</strong>.</li> + <li>Read and accept the license agreement, then click <strong>Next</strong>. </li> + <li>On the following Installation window, click <strong>Finish</strong>. </li> + <li>The ADT plugin is not digitally signed. Accept the installation anyway + by clicking <strong>Install All</strong>. </li> + <li>Restart Eclipse. </li> +</ol> + +</td> +<td> + +<!-- 3.4 steps --> +<ol> + <li>Start Eclipse, then select <strong>Help</strong> > <strong>Software Updates...</strong>.</li> + <li>In the dialog that appears, click the <strong>Available Software</strong> tab. </li> + <li>Click <strong>Add Site...</strong> </li> + <li>Enter the Location: + <pre>https://dl-ssl.google.com/android/eclipse/</pre> + <p>If you have trouble aqcuiring the plugin, try using "http" in the Location URL, + instead of "https" (https is preferred for security reasons).</p> + <p>Click <strong>OK</strong>.</p></li> + <li>Back in the Available Software view, you should see the plugin listed by the URL, + with "Developer Tools" nested within it. Select the checkbox next to + Developer Tools and click <strong>Install...</strong></li> + <li>On the subsequent Install window, "Android DDMS" and "Android Development Tools" + should both be checked. Click <strong>Next</strong>. </li> + <li>Read and accept the license agreement, then click <strong>Finish</strong>.</li> + <li>Restart Eclipse. </li> +</ol> + +</td> +</tr> +</table> + +<p>Now modify your Eclipse preferences to point to the Android SDK directory:</p> +<ol> + <li>Select <strong>Window</strong> > <strong>Preferences...</strong> to open the Preferences + panel (Mac: <strong>Eclipse</strong> > <strong>Preferences</strong>).</li> + <li>Select <strong>Android</strong> from the left panel. </li> + <li>For the <em>SDK Location</em> in the main panel, click <strong>Browse...</strong> and +locate your downloaded SDK directory. </li> + <li>Click <strong>Apply</strong>, then <strong>OK</strong>.</li> +</ol> + +<p>Done! If you haven't encountered any problems, then you're ready to +begin developing Android applications. See the +<a href="#next">Next Steps</a> section for suggestions on how to start. </p> + + +<h3 id="troubleshooting">Troubleshooting ADT Installation</h3> +<p> +If you are having trouble downloading the ADT plugin after following the steps above, here are +some suggestions: </p> + +<ul> + <li>If Eclipse can not find the remote update site containing the ADT plugin, try changing + the remote site URL to use http, rather than https. That is, set the Location for the remote site to: + <pre>http://dl-ssl.google.com/android/eclipse/</pre></li> + <li>If you are behind a firewall (such as a corporate firewall), make + sure that you have properly configured your proxy settings in Eclipse. + In Eclipse 3.3/3.4, you can configure proxy information from the main + Eclipse menu in <strong>Window</strong> (on Mac, <strong>Eclipse</strong>) > + <strong>Preferences</strong> > <strong>General</strong> > + <strong>Network Connections</strong>.</li> +</ul> +<p> +If you are still unable to use Eclipse to download the ADT plugin as a remote update site, you +can download the ADT zip file to your local machine and manually install the it: +</p> +<ol> + <li><a href="{@docRoot}sdk/adt_download.html">Download the ADT zip file</a> (do not unpack it).</li> + <li>Follow steps 1 and 2 in the default install instructions (above).</li> + <li>In Eclipse 3.3, click <strong>New Archive Site...</strong>. <br/> + In Eclipse 3.4, click <strong>Add Site...</strong>, then <strong>Archive...</strong></li> + <li>Browse and select the downloaded zip file.</li> + <li>Follow the remaining procedures, above, starting from steps 5.</li> +</ol> +<p>To update your plugin once you've installed using the zip file, you will have to +follow these steps again instead of the default update instructions.</p> + +<h4>Other install errors</h4> + +<p>Note that there are features of ADT that require some optional +Eclipse components (for example, WST). If you encounter an error when +installing ADT, your Eclipse installion might not include these components. +For information about how to quickly add the necessary components to your +Eclipse installation, see the troubleshooting topic +<a href="{@docRoot}guide/appendix/faq/troubleshooting.html#installeclipsecomponents">ADT +Installation Error: "requires plug-in org.eclipse.wst.sse.ui"</a>.</p> + +<h4>For Linux users</h4> +<p>If you encounter this error when installing the ADT Plugin for Eclipse: +<pre> +An error occurred during provisioning. +Cannot connect to keystore. +JKS</pre> +<p> +...then your development machine lacks a suitable Java VM. Installing Sun +Java 6 will resolve this issue and you can then reinstall the ADT +Plugin.</p> + + +<h2 id="next">Next Steps</h2> +<p>Once you have completed installation, you are ready to +begin developing applications. Here are a few ways you can get started: </p> + +<p><strong>Learn about Android</strong></p> +<ul> + <li>Take a look at the <a href="{@docRoot}guide/index.html">Dev + Guide</a> and the types of information it provides</li> + <li>Read an introduction to Android as a platform in <a + href="{@docRoot}guide/basics/what-is-android.html">What is + Android?</a></li> + <li>Learn about the Android framework and how applications run on it in + <a href="{@docRoot}guide/topics/fundamentals.html">Application + Fundamentals</a></li> + <li>Take a look at the Android framework API specification in the <a + href="{@docRoot}reference/packages.html">Reference</a> tab</li> +</ul> + +<p><strong>Explore the SDK</strong></p> +<ul> + <li>Get an overview of the <a + href="{@docRoot}guide/developing/tools/index.html">development + tools</a> that are available to you</li> + <li>Read how to develop <a + href="{@docRoot}guide/developing/eclipse-adt.html">in Eclipse/ADT</a> or + <a href="{@docRoot}guide/developing/other-ide.html">in other IDEs</a> + </li> +</ul> + +<p><strong>Explore some code</strong></p> +<ul> + <li>Set up a <a href="{@docRoot}guide/tutorials/hello-world.html">Hello + World application</a> (highly recommended, especially for Eclipse users)</li> + <li>Follow the <a href="{@docRoot}guide/tutorials/notepad/index.html"> + Notepad Tutorial</a> to build a full Android application </li> + <li>Create a new project for one of the other sample applications + included in <code><em><sdk></em>/platforms/<em><platfrom></em>/samples</code>, + then compile and run it in your development environment</li> +</ul> + +<p><strong>Visit the Android developer groups</strong></p> +<ul> + <li>Take a look at the <a + href="{@docRoot}community/index.html">Community</a> tab to see a list of + Android developers groups. In particular, you might want to look at the + <a href="http://groups.google.com/group/android-developers">Android + Developers</a> group to get a sense for what the Android developer + community is like.</li> +</ul> + + +<h2 id="installnotes">Installation Notes</h2> + +<h3>Ubuntu Linux Notes</h3> + +<ul> + <li>If you need help installing and configuring Java on your + development machine, you might find these resources helpful: + <ul> + <li><a href="https://help.ubuntu.com/community/Java">https://help.ubuntu.com/community/Java </a></li> + <li><a href="https://help.ubuntu.com/community/Java">https://help.ubuntu.com/community/JavaInstallation</a></li> + </ul> + </li> + <li>Here are the steps to install Java and Eclipse, prior to installing + the Android SDK and ADT Plugin. + <ol> + <li>If you are running a 64-bit distribution on your development + machine, you need to install the <code>ia32-libs</code> package using + <code>apt-get:</code>: + <pre>apt-get install ia32-libs</pre> + </li> + <li>Next, install Java: <pre>apt-get install sun-java6-bin</pre></li> + <li>The Ubuntu package manager does not currently offer an Eclipse 3.3 + version for download, so we recommend that you download Eclipse from + eclipse.org (<a + href="http://www.eclipse.org/downloads/">http://www.eclipse.org/ + downloads/</a>). A Java or RCP version of Eclipse is recommended.</li> + <li>Follow the steps given in previous sections to install the SDK + and the ADT plugin. </li> + </ol> + </li> +</ul> + +<h3>Other Linux Notes</h3> + +<ul> + <li>If JDK is already installed on your development computer, please + take a moment to make sure that it meets the version requirements listed + in the <a href="{@docRoot}sdk/1.1_r1/requirements.html">System Requirements</a>. + In particular, note that some Linux distributions may include JDK 1.4 or Gnu + Compiler for Java, both of which are not supported for Android development.</li> +</ul> + + + diff --git a/docs/html/sdk/1.5_r2/requirements.jd b/docs/html/sdk/1.5_r2/requirements.jd new file mode 100644 index 000000000000..4ed38a741261 --- /dev/null +++ b/docs/html/sdk/1.5_r2/requirements.jd @@ -0,0 +1,39 @@ +page.title=System Requirements +@jd:body + +<p>The sections below describe the system and software requirements for developing Android applications using the Android SDK tools included in Android <?cs var:sdk.version ?> SDK, Release <?cs var:sdk.rel.id ?>. </p> + +<h3>Supported Operating Systems</h3> +<ul> + <li>Windows XP (32-bit) or Vista (32- or 64-bit)</li> + <li>Mac OS X 10.4.8 or later (x86 only)</li> + <li>Linux (tested on Linux Ubuntu Dapper Drake)</li> +</ul> + +<h3>Supported Development Environments</h3> +<ul> + <li>Eclipse IDE + <ul> + <li><a href="http://www.eclipse.org/downloads/">Eclipse</a> 3.3 (Europa), 3.4 (Ganymede) + <ul> + <li>Recommended Eclipse IDE packages: Eclipse IDE for Java EE Developers, Eclipse IDE for Java Developers, Eclipse for RCP/Plug-in Developers</li> + <li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included in most Eclipse IDE packages) </li> + <li>Eclipse Classic IDE package is not supported.</li> + </ul> + </li> + <li><a href="http://java.sun.com/javase/downloads/index.jsp">JDK 5 or JDK 6</a> (JRE alone is not sufficient)</li> + <li><a href="installing.html#installingplugin">Android Development Tools plugin</a> (optional)</li> + <li><strong>Not</strong> compatible with Gnu Compiler for Java (gcj)</li> + </ul> + </li> + <li>Other development environments or IDEs + <ul> + <li><a href="http://java.sun.com/javase/downloads/index.jsp">JDK 5 or JDK 6</a> (JRE alone is not sufficient)</li> + <li><a href="http://ant.apache.org/">Apache Ant</a> 1.6.5 or later for Linux and Mac, 1.7 or later for Windows</li> + <li><strong>Not</strong> compatible with Gnu Compiler for Java (gcj)</li> + </ul> + </li> +</ul> + +<p class="note"><strong>Note:</strong> If JDK is already installed on your development computer, please take a moment to make sure that it meets the version requirements listed above. In +particular, note that some Linux distributions may include JDK 1.4 or Gnu Compiler for Java, both of which are not supported for Android development. </p>
\ No newline at end of file diff --git a/docs/html/sdk/1.5_r2/upgrading.jd b/docs/html/sdk/1.5_r2/upgrading.jd new file mode 100644 index 000000000000..bb5fc60e67f8 --- /dev/null +++ b/docs/html/sdk/1.5_r2/upgrading.jd @@ -0,0 +1,395 @@ +page.title=Upgrading the SDK +sdk.version=1.5_r2 +@jd:body + + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>Upgrading the SDK</h2> + <ul> + <li>The Android 1.5 SDK uses a new project structure and a new ADT plugin (ADT 0.9). </li> + <li>To move existing projects into the SDK, you must make some minor changes in your + development environment.</li> + <li>The new ADT plugin (ADT 0.9) <em>is not compatible</em> with projects created in previous SDKs.</li> + <li>You need to uninstall your existing ADT plugin, before installing ADT 0.9.</li> + </ul> + + <h2>In this document</h2> + <ol> + <li><a href="#Install">Install the SDK</a></li> + <li><a href="#UpdateAdt">Update Your Eclipse ADT Plugin</a></li> + <li><a href="#UpdateYourProjects">Update Your Projects</a> + <ol> + <li><a href="#EclipseUsers">Eclipse Users</a></li> + <li><a href="#AntUsers">Ant Users</a></li> + </ol> + </li> + <li><a href="#MigrateYourApplications">Migrate Your Applications</a> + <ol><li><a href="#FutureProofYourApps">Future-proof your apps</a></li></ol> + </li> + </ol> + + <h2>Migrating references</h2> + <ol> + <li><a href="{@docRoot}sdk/api_diff/3/changes.html">Android 1.5 API Differences</a></li> + <li><a +href="http://android-developers.blogspot.com/2009/04/future-proofing-your-apps.html">Future-Proofing +Your Apps »</a></li> + <li><a +href="http://android-developers.blogspot.com/2009/04/ui-framework-changes-in-android-15.html">UI +framework changes in Android 1.5 »</a></li> + </ol> + +</div> +</div> + +<p>This document describes how to move your development environment and existing +Android applications from an Android 1.0 or 1.1 SDK to the Android 1.5 SDK. +If you are migrating applications from an SDK older than 1.0, please also read the upgrading +document available in the Android 1.0 SDK package.</p> + +<p>There are several compelling reasons to upgrade, such as new SDK tools +that make developing more efficient and new APIs that allow you to expand the feature-set +of your applications. However, even if you or your applications don't require these enhancements, +it's important that you upgrade to ensure that your applications run properly on the +Android 1.5 platform.</p> + +<p>The Android 1.5 platform will soon be deployable to devices around the world. +If you have already released Android applications to the public, you should +test the forward-compatibility of your applications on the latest version of the platform +as soon as possible. It's unlikely that you'll encounter breakage in your applications, but +in the interest of maintaining the best user experience, you should take no risks. +So, please install the new Android SDK and test your applications on Android 1.5.</p> + +<p>For more information on new SDK features and system changes, +see the <a href="{@docRoot}sdk/android-1.5.html">Android 1.5 Version Notes</a>.</p> + + +<h2 id="Install">Install the SDK</h2> + +<p>If you haven't yet downloaded the SDK, <a href="{@docRoot}sdk/1.5_r2/index.html">download from here</a> +and unpack it into a safe location.</p> + +<p><strong>Before you begin:</strong> +If you had previously setup your PATH variable to point to the SDK tools directory, +then you need to update it to point to the new SDK. For example, for a +<code>.bashrc</code> or <code>.bash_profile</code> file:</p> +<pre>export PATH=$PATH:<em><your_sdk_dir></em>/tools</pre> + +<p>If you don't use Eclipse for development, +skip to <a href="#updateYourProjects">Update Your Projects</a>.</p> + + +<h2 id="UpdateAdt">Update Your Eclipse ADT Plugin</h2> + +<p><em>If you installed ADT-0.9_pre with the early look 1.5 SDK, there have been +additional changes, so please continue with this guide and update to the final ADT 0.9.</em></p> + +<p>A new ADT plugin (version 0.9) is required for the Android 1.5 SDK. +Because the component structure has been changed since Android 1.1, +the Android 1.5 SDK does not work with ADT 0.8 (or older) and previously installed SDKs will not +work with ADT 0.9. However, the Android 1.5 SDK includes an Android 1.1 SDK image that you +can build against while using ADT 0.9. </p> + +<p class="note">For information about using different system images (such as Android 1.1) +while running this SDK, see Developing <a href="{@docRoot}guide/developing/eclipse-adt.html"> +In Eclipse, with ADT</a> or <a href="{@docRoot}guide/developing/other-ide.html">In +Other IDEs</a>, as appropriate for your development environment.</p> + +<p>In order to upgrade your Eclipse IDE to use the new 0.9 ADT, follow the steps below +for your respective version of Eclipse.</p> + +<h3 id="uninstallAdt">Uninstall your previous ADT plugin</h3> + +<p>You must uninstall your existing ADT plugin (0.8 or older). If you do not uninstall it, +you will get a conflict with the Android Editors when installing the new ADT. +(If you have already installed ADT-0.9_pre with the early look 1.5 SDK, you can skip this +uninstall procedure and continue to <a href="#installAdt">Install the 0.9 ADT plugin</a>).</p> + +<table style="font-size:100%"> +<tr><th>Eclipse 3.3 (Europa)</th><th>Eclipse 3.4 (Ganymede)</th></tr> +<tr> +<td width="50%"> +<!-- 3.3 steps --> +<ol> + <li>Select <strong>Help</strong> > <strong>Software Updates</strong> > + <strong>Manage Configuration</strong>. </li> + <li>Expand the list in the left panel to reveal the installed tools.</li> + <li>Right-click "Android Editors" and click <strong>Uninstall</strong>. Click <strong>OK</strong> + to confirm.</li> + <li>Restart Eclipse. + <p>(Do not uninstall "Android Development Tools".)</p></li> +</ol> +</td> +<td> +<!-- 3.4 steps --> +<ol> + <li>Select <strong>Help</strong> > <strong>Software Updates</strong>.</li> + <li>Select the <strong>Installed Software</strong> tab.</li> + <li>Select "Android Editors". Click <strong>Uninstall</strong>.</li> + <li>In the next window, be sure "Android Editors" is checked, then click <strong>Finish</strong> + to uninstall.</li> + <li>Restart Eclipse. + <p>(Do not uninstall "Android Development Tools".)</p></li> +</ol> +</td> +</tr> +</table> + + +<h3 id="installAdt">Install the 0.9 ADT plugin</h3> + +<p>Only install the new plugin once you've completed the procedure to +<a href="#uninstallAdt">Uninstall your previous ADT plugin</a>.</p> + +<table style="font-size:100%"> +<tr><th>Eclipse 3.3 (Europa)</th><th>Eclipse 3.4 (Ganymede)</th></tr> +<tr> +<td width="50%"> +<!-- 3.3 steps --> +<ol> + <li>Select <strong>Help</strong> > <strong>Software Updates</strong> > + <strong>Find and Install</strong>. </li> + <li>Select <strong>Search for new features to install</strong>.</li> + <li>Select the Android plugin entry by checking the box next to it, + then click <strong>Finish</strong>. + <p>(Your original entry for the plugin should still be here. If not, see the guide + to <a href="{@docRoot}sdk/1.5_r2/installing.html#installingplugin">Installing the ADT Plugin</a>.) + </p></li> + <li>In the results, expand the entry for the Android plugin and + be sure that "Developer Tools" is checked, then click <strong>Next</strong>. + (This will install "Android DDMS" and "Android Development Tools".)</li> + <li>Read and accept the license agreement, then click <strong>Next</strong>. + <li>In the next window, click <strong>Finish</strong> to start installation.</li> + <li>The ADT plugin is not digitally signed. Accept the installation anyway by clicking + <strong>Install All</strong>.</li> + <li>Restart Eclipse.</li> +</ol> +</td> +<td> +<!-- 3.4 steps --> +<ol> + <li>Select <strong>Help</strong> > <strong>Software Updates</strong>.</li> + <li>Select the <strong>Available Software</strong> tab.</li> + <li>Expand the entry for the Andriod plugin (may be listed as the location URL) + and select "Developer Tools" by checking the box next to it, then click + <strong>Install</strong>.</li> + <li>On the next window, "Android DDMS" and "Android Development Tools" + should both be checked. Click <strong>Finish</strong>.</li> + <li>Restart Eclipse.</li> +</ol> +</td> +</tr> +</table> + +<p>If you encounter problems, ensure your ADT is fully uninstalled and then +follow the guide to +<a href="{@docRoot}sdk/1.5_r2/installing.html#installingplugin">Installing the ADT Plugin +for Eclipse</a>.</p> + +<h3 id="updateEclipsePrefs">Update your Eclipse SDK Preferences</h3> + +<p>The last step is to update your Eclipse preferences to point to the new SDK directory:</p> + <ol> + <li>Select <strong>Window</strong> > <strong>Preferences</strong> to open the Preferences + panel (Mac: <strong>Eclipse</strong> > <strong>Preferences</strong>).</li> + <li>Select <strong>Android</strong> from the left panel.</li> + <li>For the <em>SDK Location</em> in the main panel, click <strong>Browse</strong> + and locate your SDK directory.</li> + <li>Click <strong>Apply</strong>, then <strong>OK</strong>.</li> + </ol> + + +<h2 id="UpdateYourProjects">Update Your Projects</h2> + +<p>You will now need to update any and all Android projects that you have +developed using a previous version of the Android SDK.</p> + + +<h3 id="EclipseUsers">Eclipse users</h3> + +<p>If you use Eclipse to develop applications, use the following procedure to +update each project:</p> + +<ol> + <li>Right-click on the individual project (in the Package Explorer) + and select <strong>Properties</strong>.</li> + <li>In the properties, open the Android panel and select a "build target" to compile + against. This SDK offers the Android 1.1 and Android 1.5 platforms to choose from. When + you are initially updating your projects to the new SDK, we recommend that you select a build + target with the Android 1.1 platform. Click <strong>Apply</strong>, then + <strong>OK</strong>.</li> +</ol> + +<p>The new plugin creates a <code>gen/</code> folder in your project, in which it puts the +<code>R.java</code> file +and all automatically generated AIDL java files. If you get an error such as +<code>The type R is already defined</code>, +then you probably need to delete your old <code>R.java</code> or your old auto-generated +AIDL Java files in the <code>src/</code> folder. +(This <em>does not</em> apply to your own hand-crafted parcelable AIDL java files.)</p> + +<p>Note that, with the Android 1.5 SDK, there is a new process for running +applications in the Android Emulator. +Specifically, you must create an Android Virtual Device (AVD) before you can launch an instance +of the Emulator. Before attempting to run your applications with the new SDK, +please continue with the section below to +<a href="#MigrateYourApplications">Migrate Your Applications</a>.</p> + + +<h3 id="AntUsers">Ant users</h3> + +<p>If you build your projects using the Ant tool (rather than with Eclipse), note the +following changes with the new SDK tools.</p> + +<h4>build.xml has changed</h4> + +<p>You must re-create your <code>build.xml</code> file.</p> + +<p>If you had customized your <code>build.xml</code>, first make a copy of it:</p> + +<pre> +$ cd <em>my-project</em> +$ cp build.xml build.xml.old +</pre> + +<p>Now use the new <code>android</code> tool (located in <code><em>your_sdk</em>/tools/</code>) +to create a new <code>build.xml</code> that references +a specific platform target:</p> + +<pre>$ android update project --path /path/to/my-project --target 1</pre> + +<p>The "target" corresponds to an Android platform library (including any add-ons, such as +Google APIs) that you would like to build your project against. You can view a list of available +targets (and their corresponding integer ID) with the command, <code>android list targets</code>. +When you are initially updating your projects to the new SDK, we recommend that you select the +first target ("1"), which uses the Android 1.1 platform library.</p> + +<p>A <code>gen/</code> folder will be created the first time you build and your <code>R.java</code> and +your AIDL Java files will be generated in here. You <strong>must</strong> remove +the old <code>R.java</code> and old auto-generated AIDL java files from the +<code>src/</code> folder. (This +does not apply to your own hand-crafted parcelabe AIDL java files.)</p> + +<p class="note"><strong>Note:</strong> The "activitycreator" tool has been replaced +by the new "android" tool. For information on creating new projects with the android tool, +see the documentation about <a href="{@docRoot}guide/developing/other-ide.html">Developing +In Other IDEs</a>.</p> + +<p>Note that, with the Android 1.5 SDK, there is a new process for running +applications in the Android Emulator. +Specifically, you must create an Android Virtual Device (AVD) before you can launch an instance +of the Emulator. Before attempting to run your applications with the new SDK, +please continue with the section below to +<a href="#MigrateYourApplications">Migrate Your Applications</a>.</p> + + +<h2 id="MigrateYourApplications">Migrate Your Applications</h2> + +<p>After you have completed the process above to <a href="#UpdateYourProjects">Update Your +Projects</a>, you are strongly encouraged to run each of your applications in an instance +of the emulator running the Android 1.5 system image. It's possible (however, unlikely) +that you'll encounter some breakage in your application when you run your applications on +the Android 1.5 system image. Whether you believe your application will be affected by +platform changes or not, it's very important that you test the application's +forward-compatibility on Android 1.5.</p> + +<p>To test forward-compatibility, simply run your existing application (as-is) on an Android +Emulator that's running the Android 1.5 system image. The following procedure will guide +you through the process to running your existing applications on an emulator. <em>Please read +the following guide completely before you begin</em>.</p> + +<p>To test your application on an emulator running Android 1.5:</p> +<ol> + <li><a href="#UpdateYourProjects">Update Your Project</a> (you should have done this + already, in the section above).</li> + <li>Run your existing project, as-is, on an emulator running the Android 1.5 system image. + <p>As mentioned in the guide to <a href="#UpdateYourProjects">Update Your Projects</a>, + you should have selected a "build + target" of "1", which compiles your application against the Android 1.1 system image, so there + should be no new errors in your code.</p> + <p>Eclipse users: follow the + <a href="{@docRoot}guide/developing/eclipse-adt.html#Running">Eclipse guide to + Running Your Application</a>.</p> + <p>Ant users: follow the + <a href="{@docRoot}guide/developing/other-ide.html#Running">Ant guide to + Running Your Application</a> + <p>During the procedure to Running Your Application, select a "deployment target" + for the AVD that includes the Android 1.5 platform. + If your application utilizes the Google Maps APIs (i.e., + MapView), be certain to select a target that includes the Google APIs.</p> + <p>Once you complete the procedures to run your application in your respective environment, + linked above, return here.</p> + </li> + <li>With your application running in the emulator, perform all regular testing on the application + to ensure that it functions normally (in both landscape and portrait orientations).</li> +</ol> + +<p>Chances are, your application runs just fine on the Android 1.5 platform — +new devices will be able to safely install and run your application and +current users who update their devices will be able to continue using your application as usual. +However, if something doesn't work the way you expect, then you might need to revisit +your project and make any necessary changes to your code.</p> + +<p>You can check for code breakages caused by API changes by opening your project +in Eclipse, changing the "build target" to one using the Android 1.5 platform, +and see where the ADT identifies errors in your code.</p> + + +<h3 id="FutureProofYourApps">Future-proof your apps</h3> + +<p>There have been several API additions made for this release, but there have been +very few actual API <em>changes</em>. Only a couple (relatively unused) elements +have been removed and a few have been deprecated, so your applications written with the +Android 1.1 system library should work just fine. However, +your application is more likely to encounter problems on Android 1.5 +if it performs any of the following:</p> + +<ul> + <li>Uses internal APIs. That is, APIs that are not officially supported + and not available in the reference documentation. Any un-official APIs are always subject + to change (which is why they're un-official) and some have indeed changed. + </li> + <li>Directly manipulates system settings. There are some settings (such as + GPS, data roaming, bluetooth and others) that used to be writable by + applications but have been changed so that they can only be explicitly modified by the user + through the system settings. Refer to {@link android.provider.Settings.Secure} + to see which settings are now secured and cannot be directly changed by your application. + </li> + <li>Uses View hierarchies that are unreasonably deep (more than 10 or so levels) or + broad (more than 30 total). View hierarchies this big have always been troublesome, but + Android 1.5 is much more efficient at exposing this and your application may crash. + </li> + <li>Makes assumptions about the available hardware. With new support for soft keyboards, + not all devices will have full QWERTY keyboards on the hardware. So if your application + listens for special keypress events that only occur on a keypad, then your application + should degrade gracefully when there is no keyboard available. + </li> + <li>Performs its own layout orientation changes based on the acceletometer (or via other + sensors). Some devices running Android 1.5 will automatically rotate the orientation + (and all devices have the option to turn on auto-rotation), so if your application also + attempts to rotate the orientation, it can result in strange behavior. In addition, if your + application uses the accelerometer to detect shaking and you do not want to rotate the + orientation, then you should lock the current orientation with + <a href="{@docRoot}guide/topics/manifest/activity-element.html#screen">android:screenOrientation</a>. + </li> +</ul> + +<p>Please read our blog post on <a +href="http://android-developers.blogspot.com/2009/04/future-proofing-your-apps.html">Future-Proofing +Your Apps</a> for more information on the issues mentioned above.</p> + +<p>For information +about other changes made to Android 1.5, refer to the following documents:</p> +<ul> + <li><a href="{@docRoot}sdk/api_diff/3/changes.html">Android 1.5 API Differences</a></li> + <li><a href="{@docRoot}sdk/android-1.5.html#api-changes">Android 1.5 Version Notes</a></li> + <li><a +href="http://android-developers.blogspot.com/2009/04/ui-framework-changes-in-android-15.html">UI +framework changes in Android 1.5 »</a></li> +</ul> + +<p>If you have additional trouble updating your code, visit the +<a href="http://groups.google.com/group/android-developers">Android Developers Group</a> +to seek help from other Android developers.</p> diff --git a/docs/html/sdk/RELEASENOTES.jd b/docs/html/sdk/RELEASENOTES.jd index c44cef3a55fb..f3a1951f71fb 100644 --- a/docs/html/sdk/RELEASENOTES.jd +++ b/docs/html/sdk/RELEASENOTES.jd @@ -3,8 +3,16 @@ page.title=SDK Release Notes <p>This document provides version-specific information about Android SDK releases. For the latest known issues, please ensure that you're viewing this -page at: -<a href="http://developer.android.com/sdk/RELEASENOTES.html">http://developer.android.com/sdk/RELEASENOTES.html</a>.</p> +page at <a href="http://developer.android.com/sdk/RELEASENOTES.html">http://developer.android.com/sdk/RELEASENOTES.html</a>.</p> + + +<h2 id="1.5_r2">Android 1.5 SDK, Release 2</h2> + +<p>This SDK release provides the same developer tools as the Android 1.5 SDK, +Release 1, but provides an updated Android 1.5 system image that includes a +security patch for the issue described in the oCert advisory below:</p> + +<p style="margin-left:2em;"><a href="http://www.ocert.org/advisories/ocert-2009-006.html">http://www.ocert.org/advisories/ocert-2009-006.html</a></p> <h2 id="1.5_r1">Android 1.5 SDK, Release 1</h2> diff --git a/docs/html/sdk/android-1.5-highlights.jd b/docs/html/sdk/android-1.5-highlights.jd index e6c4f88e4d32..ff64e8c28cf9 100644 --- a/docs/html/sdk/android-1.5-highlights.jd +++ b/docs/html/sdk/android-1.5-highlights.jd @@ -1,5 +1,4 @@ page.title=Android 1.5 Platform Highlights -sdk.version=1.5_r1 @jd:body <p> diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd index ff57a36ec392..3c2bbd403129 100644 --- a/docs/html/sdk/older_releases.jd +++ b/docs/html/sdk/older_releases.jd @@ -1,37 +1,67 @@ -page.title=Older Releases +page.title=Other SDK Releases @jd:body -<div class="special"> - <strong>NOTICE:</strong> - <p>The SDKs listed on this page are "early-look" versions that were released in +<p>This page provides a full list of older, obsolete SDK releases, including +non-current versions of active releases and "early look" versions that were +released before Android 1.0. The list is provided for informational purposes +only.</p> + +<p>If you are just getting started developing on Android, make sure that you +are using the <a href="{@docRoot}sdk/{@sdkCurrent}/index.html">most current SDK available</a>, +to ensure that your applications will be compatible with the latest +Android-powered devices.</p> + +<h2>Obsolete Releases</h2> + +<p>The table below lists Android SDK releases that have been superceded by an +active release and that are now obsolete. If you are using one of these +releases, please upgrade to the <a href="{@docRoot}sdk/index.html">current SDK +release</a>.</p> + + <table> + <tr> + <th>Release</td> + <th>Platform(s)</th> + <th>Date</td> + <th>Description</td> + </tr> + <tr> + <td><a href="{@docRoot}sdk/1.5_r1/index.html">Android 1.5 SDK, Release 1</a></td> + <td style="text-align:center;">Android 1.5<br>Android 1.1</td> + <td><em>April 2009</em></td> + <td>Replaced by Android 1.5 SDK, Release 2. <em><a href="RELEASENOTES.html#1.5_r1">Release notes</a></em></td> + </tr> + <tr class="alt"> + <td><a href="{@docRoot}sdk/1.0_r1/index.html">Android 1.0 SDK, Release 1</a></td> + <td style="text-align:center;">Android 1.0</td> + <td><em>September 2008</em></td> + <td>Replaced by Android 1.0 SDK, Release 2. <em><a href="RELEASENOTES.html#1.0_r1">Release notes</a></em></td> + </tr> + </table> + + <h2>Non-Compatible Releases</h2> + +<!-- <div class="special"> --> +<p>The SDKs listed below are "early-look" versions that were released in the year preceding the full release of Android 1.0 in September 2008. Because these early-look SDKs were released before the Android 1.0 API specification was finalized, they do not provide a compliant Android execution environment. Consequently, applications that you develop in these SDKs will not be able to run on any Android-powered devices.</p> - <p>If you have an older application that you built in one of the early-look SDKs, - you must migrate it to the Android - 1.0 SDK (or later release) before you will be able to deploy it to - an Android-powered device. To help with this migration, each SDK package below - provides information about API changes from the previous version. You can find - the migration information in the documentation included in each SDK package.</p> - - <p>If you are just getting started developing on Android, do not use one of these early-look - SDKs. Instead, develop using the most <a href="{@docRoot}sdk/index.html">current - SDK release</a> available, to ensure that your applications will be compatible - with Android-powered devices.</p> -</div> - +<p>If you have an older application that you built in one of the early-look +SDKs, you must migrate it to the Android 1.0 SDK (or later release) before you +will be able to deploy it to an Android-powered device. To help with this +migration, each SDK package below provides information about API changes from +the previous version. You can find the migration information in the +documentation included in each SDK package.</p> +<!-- </div> --> - - - <h2>Android 0.9 SDK beta</h2> - <p><em>August 18, 2008 - <a href="OLD_RELEASENOTES.html#0.9_beta">Release Notes</a></em></p> +<h4>Android SDK m5-rc15</h4> +<p><em>August 18, 2008 - <a href="OLD_RELEASENOTES.html#0.9_beta">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> @@ -58,15 +88,11 @@ page.title=Older Releases </tr> </table> - - - -<h2>Version m5-rc15</h2> +<h4>Version m5-rc15</h4> <p><em>March 3, 2008 - <a href="OLD_RELEASENOTES.html#m5-rc15">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> @@ -93,15 +119,11 @@ page.title=Older Releases </tr> </table> - - - - <h2>Version m5-rc14</h2> + <h4>Version m5-rc14</h4> <p><em>February 12, 2008 - <a href="OLD_RELEASENOTES.html#m5-rc14">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> @@ -131,12 +153,11 @@ page.title=Older Releases - <h2>Version m3-rc37a</h2> + <h4>Version m3-rc37a</h4> <p><em>December 14, 2007 - <a href="OLD_RELEASENOTES.html#m3-rc37a">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> @@ -166,12 +187,11 @@ page.title=Older Releases - <h2>Version m3-rc22a</h2> + <h4>Version m3-rc22a</h4> <p><em>November 16, 2007 - <a href="OLD_RELEASENOTES.html#m3-rc22a">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> @@ -201,12 +221,11 @@ page.title=Older Releases - <h2>Version m3-rc20a</h2> + <h4>Version m3-rc20a</h4> <p><em>November 12, 2007 - <a href="OLD_RELEASENOTES.html#m3-rc20a">Release Notes</a></em></p> <table> <tr> - <th>Platform</th> - <th>Package</th> + <th colspan="2">Package</th> <th>Size</th> <th>MD5 Checksum</th> </tr> diff --git a/docs/html/sdk/preview/features.html b/docs/html/sdk/preview/features.html index 392c0895ec25..a2f085cdce34 100644 --- a/docs/html/sdk/preview/features.html +++ b/docs/html/sdk/preview/features.html @@ -133,10 +133,10 @@ <li> <h2>Current SDK Release</h2> <ul> - <li><a href="/sdk/1.5_r1/index.html">Download</a></li> - <li><a href="/sdk/1.5_r1/installing.html">Installing</a></li> - <li><a href="/sdk/1.5_r1/upgrading.html">Upgrading</a></li> - <li><a href="/sdk/1.5_r1/requirements.html">System Requirements</a></li> + <li><a href="/sdk/1.5_r2/index.html">Download</a></li> + <li><a href="/sdk/1.5_r2/installing.html">Installing</a></li> + <li><a href="/sdk/1.5_r2/upgrading.html">Upgrading</a></li> + <li><a href="/sdk/1.5_r2/requirements.html">System Requirements</a></li> </ul> <ul> <li><a href="/sdk/terms.html">SDK Terms and Conditions</a></li> @@ -154,8 +154,7 @@ <ul> <li><a href="/sdk/1.1_r1/index.html">Android 1.1 SDK, r1</a></li> <li><a href="/sdk/1.0_r2/index.html">Android 1.0 SDK, r2</a></li> - <li><a href="/sdk/1.0_r1/index.html">Android 1.0 SDK, r1</a></li> - <li><a href="/sdk/older_releases.html">Older Releases</a></li> + <li><a href="/sdk/older_releases.html">Other Releases</a></li> </ul> </li> </ul> @@ -185,16 +184,6 @@ </div> <!-- end body-content --> -<script type="text/javascript"> -init(); /* initialize android-developer-docs.js */ -var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); -document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); -</script> -<script type="text/javascript"> -var pageTracker = _gat._getTracker("UA-5831155-1"); -pageTracker._trackPageview(); -</script> - </body> </html> diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 4b55b56177ce..2079dd8a62f3 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -27,18 +27,11 @@ </ul> </li> <li> - <h2>Native Development Tools</h2> - <ul> - <li><a href="<?cs var:toroot ?>sdk/ndk/1.5-r1/index.html">Android 1.5 NDK, r1</a></li> - </ul> - </li> - <li> <h2>Previous SDK Releases</h2> <ul> <li><a href="<?cs var:toroot ?>sdk/1.1_r1/index.html">Android 1.1 SDK, r1</a></li> <li><a href="<?cs var:toroot ?>sdk/1.0_r2/index.html">Android 1.0 SDK, r2</a></li> - <li><a href="<?cs var:toroot ?>sdk/1.0_r1/index.html">Android 1.0 SDK, r1</a></li> - <li><a href="<?cs var:toroot ?>sdk/older_releases.html">Older Releases</a></li> + <li><a href="<?cs var:toroot ?>sdk/older_releases.html">Other Releases</a></li> </ul> </li><?cs /if ?> diff --git a/docs/html/search.jd b/docs/html/search.jd index 0a802a6bfcbe..defba3087af3 100644 --- a/docs/html/search.jd +++ b/docs/html/search.jd @@ -2,7 +2,7 @@ page.title=Search Results @jd:body
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/jquery-history.js" type="text/javascript"></script>
+<script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>
<script type="text/javascript">
google.load('search', '1');
diff --git a/docs/html/shareables/icon_templates-v1.0.zip b/docs/html/shareables/icon_templates-v1.0.zip Binary files differnew file mode 100644 index 000000000000..94fbcdcea6cc --- /dev/null +++ b/docs/html/shareables/icon_templates-v1.0.zip diff --git a/docs/html/sitemap.txt b/docs/html/sitemap.txt index 5bb8cae4c1cb..a227d09bf9c9 100644 --- a/docs/html/sitemap.txt +++ b/docs/html/sitemap.txt @@ -75,6 +75,7 @@ http://developer.android.com/guide/publishing/versioning.html http://developer.android.com/guide/publishing/preparing.html http://developer.android.com/guide/publishing/publishing.html http://developer.android.com/guide/practices/ui_guidelines/index.html +http://developer.android.com/guide/practices/ui_guidelines/icon_design.html http://developer.android.com/guide/practices/ui_guidelines/widget_design.html http://developer.android.com/guide/practices/design/performance.html http://developer.android.com/guide/practices/design/responsiveness.html diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index fda584d99b8b..e2e93eb87e67 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -16,14 +16,14 @@ package android.graphics; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; +import java.io.OutputStream; import java.nio.Buffer; import java.nio.ByteBuffer; -import java.nio.ShortBuffer; import java.nio.IntBuffer; -import java.io.OutputStream; +import java.nio.ShortBuffer; public final class Bitmap implements Parcelable { /** @@ -32,14 +32,14 @@ public final class Bitmap implements Parcelable { * @see Bitmap#getDensityScale() * @see Bitmap#setDensityScale(float) * - * @hide pending API council approval + * @hide pending API council approval */ public static final float DENSITY_SCALE_UNKNOWN = -1.0f; // Note: mNativeBitmap is used by FaceDetector_jni.cpp // Don't change/rename without updating FaceDetector_jni.cpp private final int mNativeBitmap; - + private final boolean mIsMutable; private byte[] mNinePatchChunk; // may be null private int mWidth = -1; @@ -63,7 +63,7 @@ public final class Bitmap implements Parcelable { if (nativeBitmap == 0) { throw new RuntimeException("internal error: native bitmap is 0"); } - + // we delete this in our finalizer mNativeBitmap = nativeBitmap; mIsMutable = isMutable; @@ -83,7 +83,7 @@ public final class Bitmap implements Parcelable { * * @see #setDensityScale(float) * @see #isAutoScalingEnabled() - * @see #setAutoScalingEnabled(boolean) + * @see #setAutoScalingEnabled(boolean) * @see android.util.DisplayMetrics#DEFAULT_DENSITY * @see android.util.DisplayMetrics#density * @see #DENSITY_SCALE_UNKNOWN @@ -105,7 +105,7 @@ public final class Bitmap implements Parcelable { * * @see #getDensityScale() * @see #isAutoScalingEnabled() - * @see #setAutoScalingEnabled(boolean) + * @see #setAutoScalingEnabled(boolean) * @see android.util.DisplayMetrics#DEFAULT_DENSITY * @see android.util.DisplayMetrics#density * @see #DENSITY_SCALE_UNKNOWN @@ -126,7 +126,7 @@ public final class Bitmap implements Parcelable { * <p>Auto scaling is turned off by default. If auto scaling is enabled but the * bitmap has an unknown density scale, then the bitmap will never be automatically * scaled at drawing time.</p> - * + * * @return True if the bitmap must be scaled at drawing time, false otherwise. * * @see #setAutoScalingEnabled(boolean) @@ -167,7 +167,7 @@ public final class Bitmap implements Parcelable { public void setNinePatchChunk(byte[] chunk) { mNinePatchChunk = chunk; } - + /** * Free up the memory associated with this bitmap's pixels, and mark the * bitmap as "dead", meaning it will throw an exception if getPixels() or @@ -194,7 +194,7 @@ public final class Bitmap implements Parcelable { public final boolean isRecycled() { return mRecycled; } - + /** * This is called by methods that want to throw an exception if the bitmap * has already been recycled. @@ -204,7 +204,7 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException(errorMessage); } } - + /** * Common code for checking that x and y are >= 0 * @@ -246,16 +246,16 @@ public final class Bitmap implements Parcelable { this.nativeInt = ni; } final int nativeInt; - + /* package */ static Config nativeToConfig(int ni) { return sConfigs[ni]; } - + private static Config sConfigs[] = { null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 }; } - + /** * Copy the bitmap's pixels into the specified buffer (allocated by the * caller). An exception is thrown if the buffer is not large enough to @@ -275,16 +275,16 @@ public final class Bitmap implements Parcelable { } else { throw new RuntimeException("unsupported Buffer subclass"); } - + long bufferSize = (long)elements << shift; long pixelSize = (long)getRowBytes() * getHeight(); - + if (bufferSize < pixelSize) { throw new RuntimeException("Buffer not large enough for pixels"); } - + nativeCopyPixelsToBuffer(mNativeBitmap, dst); - + // now update the buffer's position int position = dst.position(); position += pixelSize >> shift; @@ -299,7 +299,7 @@ public final class Bitmap implements Parcelable { */ public void copyPixelsFromBuffer(Buffer src) { checkRecycled("copyPixelsFromBuffer called on recycled bitmap"); - + int elements = src.remaining(); int shift; if (src instanceof ByteBuffer) { @@ -311,17 +311,17 @@ public final class Bitmap implements Parcelable { } else { throw new RuntimeException("unsupported Buffer subclass"); } - + long bufferBytes = (long)elements << shift; long bitmapBytes = (long)getRowBytes() * getHeight(); - + if (bufferBytes < bitmapBytes) { throw new RuntimeException("Buffer not large enough for pixels"); } - + nativeCopyPixelsFromBuffer(mNativeBitmap, src); } - + /** * Tries to make a new bitmap based on the dimensions of this bitmap, * setting the new bitmap's config to the one specified, and then copying @@ -350,7 +350,7 @@ public final class Bitmap implements Parcelable { if (m == null) { m = new Matrix(); } - + final int width = src.getWidth(); final int height = src.getHeight(); final float sx = dstWidth / (float)width; @@ -365,9 +365,9 @@ public final class Bitmap implements Parcelable { } } - return b; + return b; } - + /** * Returns an immutable bitmap from the source bitmap. The new bitmap may * be the same object as source, or a copy may have been made. @@ -390,7 +390,7 @@ public final class Bitmap implements Parcelable { public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) { return createBitmap(source, x, y, width, height, null, false); } - + /** * Returns an immutable bitmap from subset of the source bitmap, * transformed by the optional matrix. @@ -425,7 +425,7 @@ public final class Bitmap implements Parcelable { height == source.getHeight() && (m == null || m.isIdentity())) { return source; } - + int neww = width; int newh = height; Canvas canvas = new Canvas(); @@ -470,7 +470,7 @@ public final class Bitmap implements Parcelable { return bitmap; } - + /** * Returns a mutable bitmap with the specified width and height. * @@ -484,7 +484,7 @@ public final class Bitmap implements Parcelable { bm.eraseColor(0); // start with black/transparent pixels return bm; } - + /** * Returns a immutable bitmap with the specified width and height, with each * pixel value set to the corresponding value in the colors array. @@ -593,7 +593,7 @@ public final class Bitmap implements Parcelable { return nativeCompress(mNativeBitmap, format.nativeInt, quality, stream, new byte[WORKING_COMPRESS_STORAGE]); } - + /** * Returns true if the bitmap is marked as mutable (i.e. can be drawn into) */ @@ -610,7 +610,7 @@ public final class Bitmap implements Parcelable { public final int getHeight() { return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight; } - + /** * Convenience method that returns the width of this bitmap divided * by the density scale factor. @@ -648,7 +648,7 @@ public final class Bitmap implements Parcelable { public final int getRowBytes() { return nativeRowBytes(mNativeBitmap); } - + /** * If the bitmap's internal config is in one of the public formats, return * that config, otherwise return null. @@ -690,7 +690,7 @@ public final class Bitmap implements Parcelable { checkPixelAccess(x, y); return nativeGetPixel(mNativeBitmap, x, y); } - + /** * Returns in pixels[] a copy of the data in the bitmap. Each value is * a packed int representing a {@link Color}. The stride parameter allows @@ -722,7 +722,7 @@ public final class Bitmap implements Parcelable { nativeGetPixels(mNativeBitmap, pixels, offset, stride, x, y, width, height); } - + /** * Shared code to check for illegal arguments passed to getPixel() * or setPixel() @@ -779,7 +779,7 @@ public final class Bitmap implements Parcelable { throw new ArrayIndexOutOfBoundsException(); } } - + /** * Write the specified {@link Color} into the bitmap (assuming it is * mutable) at the x,y coordinate. @@ -799,10 +799,10 @@ public final class Bitmap implements Parcelable { checkPixelAccess(x, y); nativeSetPixel(mNativeBitmap, x, y, color); } - + /** * Replace pixels in the bitmap with the colors in the array. Each element - * in the array is a packed int prepresenting a {@link Color} + * in the array is a packed int prepresenting a {@link Color} * * @param pixels The colors to write to the bitmap * @param offset The index of the first color to read from pixels[] @@ -834,7 +834,7 @@ public final class Bitmap implements Parcelable { nativeSetPixels(mNativeBitmap, pixels, offset, stride, x, y, width, height); } - + public static final Parcelable.Creator<Bitmap> CREATOR = new Parcelable.Creator<Bitmap>() { /** @@ -884,7 +884,7 @@ public final class Bitmap implements Parcelable { public Bitmap extractAlpha() { return extractAlpha(null, null); } - + /** * Returns a new bitmap that captures the alpha values of the original. * These values may be affected by the optional Paint parameter, which @@ -917,6 +917,22 @@ public final class Bitmap implements Parcelable { return bm; } + /** + * Rebuilds any caches associated with the bitmap that are used for + * drawing it. In the case of purgeable bitmaps, this call will attempt to + * ensure that the pixels have been decoded. + * If this is called on more than one bitmap in sequence, the priority is + * given in LRU order (i.e. the last bitmap called will be given highest + * priority). + * + * For bitmaps with no associated caches, this call is effectively a no-op, + * and therefore is harmless. + */ + public void prepareToDraw() { + nativePrepareToDraw(mNativeBitmap); + } + + @Override protected void finalize() throws Throwable { try { nativeDestructor(mNativeBitmap); @@ -924,7 +940,7 @@ public final class Bitmap implements Parcelable { super.finalize(); } } - + //////////// native methods private static native Bitmap nativeCreate(int[] colors, int offset, @@ -944,12 +960,12 @@ public final class Bitmap implements Parcelable { private static native int nativeRowBytes(int nativeBitmap); private static native int nativeConfig(int nativeBitmap); private static native boolean nativeHasAlpha(int nativeBitmap); - + private static native int nativeGetPixel(int nativeBitmap, int x, int y); private static native void nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height); - + private static native void nativeSetPixel(int nativeBitmap, int x, int y, int color); private static native void nativeSetPixels(int nativeBitmap, int[] colors, @@ -969,6 +985,8 @@ public final class Bitmap implements Parcelable { int nativePaint, int[] offsetXY); + private static native void nativePrepareToDraw(int nativeBitmap); + /* package */ final int ni() { return mNativeBitmap; } diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 9e88d7e11d3e..e5a9aab7ad63 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -18,18 +18,18 @@ package android.graphics; import android.content.res.AssetManager; import android.content.res.Resources; -import android.util.TypedValue; import android.util.DisplayMetrics; +import android.util.TypedValue; import java.io.BufferedInputStream; +import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.InputStream; import java.io.IOException; -import java.io.FileDescriptor; +import java.io.InputStream; /** - * Creates Bitmap objects from various sources, including files, streams, - * and byte-arrays. + * Creates Bitmap objects from various sources, including files, streams, + * and byte-arrays. */ public class BitmapFactory { public static class Options { @@ -62,7 +62,7 @@ public class BitmapFactory { * Also, powers of 2 are often faster/easier for the decoder to honor. */ public int inSampleSize; - + /** * If this is non-null, the decoder will try to decode into this * internal configuration. If it is null, or the request cannot be met, @@ -71,7 +71,7 @@ public class BitmapFactory { * as if it has per-pixel alpha (requiring a config that also does). */ public Bitmap.Config inPreferredConfig; - + /** * If dither is true, the decoder will atttempt to dither the decoded * image. @@ -117,8 +117,6 @@ public class BitmapFactory { * explicitly make a copy of the input data, and keep that. Even if * sharing is allowed, the implementation may still decide to make a * deep copy of the input data. - * - * @hide pending API council approval */ public boolean inPurgeable; @@ -127,8 +125,6 @@ public class BitmapFactory { * false, then this field is ignored. If inPurgeable is true, then this * field determines whether the bitmap can share a reference to the * input data (inputstream, array, etc.) or if it must make a deep copy. - * - * @hide pending API council approval */ public boolean inInputShareable; @@ -151,12 +147,12 @@ public class BitmapFactory { * If not know, or there is an error, it is set to null. */ public String outMimeType; - + /** * Temp storage to use for decoding. Suggest 16K or so. */ public byte[] inTempStorage; - + private native void requestCancel(); /** @@ -167,7 +163,7 @@ public class BitmapFactory { * if the operation is canceled. */ public boolean mCancel; - + /** * This can be called from another thread while this options object is * inside a decode... call. Calling this will notify the decoder that @@ -249,7 +245,7 @@ public class BitmapFactory { if (opts.inDensity == 0) { opts.inDensity = density == TypedValue.DENSITY_DEFAULT ? DisplayMetrics.DEFAULT_DENSITY : density; - } + } float scale = opts.inDensity / (float) DisplayMetrics.DEFAULT_DENSITY; if (opts.inScaled || isNinePatch) { @@ -291,7 +287,7 @@ public class BitmapFactory { */ public static Bitmap decodeResource(Resources res, int id, Options opts) { Bitmap bm = null; - + try { final TypedValue value = new TypedValue(); final InputStream is = res.openRawResource(id, value); @@ -306,7 +302,7 @@ public class BitmapFactory { } return bm; } - + /** * Decode an image referenced by a resource ID. * @@ -337,7 +333,7 @@ public class BitmapFactory { } return nativeDecodeByteArray(data, offset, length, opts); } - + /** * Decode an immutable bitmap from the specified byte array. * @@ -350,13 +346,13 @@ public class BitmapFactory { public static Bitmap decodeByteArray(byte[] data, int offset, int length) { return decodeByteArray(data, offset, length, null); } - + /** * Decode an input stream into a bitmap. If the input stream is null, or * cannot be used to decode a bitmap, the function returns null. * The stream's position will be where ever it was after the encoded data * was read. - * + * * @param is The input stream that holds the raw data to be decoded into a * bitmap. * @param outPadding If not null, return the padding rect for the bitmap if @@ -375,7 +371,7 @@ public class BitmapFactory { if (is == null) { return null; } - + // we need mark/reset to work properly if (!is.markSupported()) { @@ -413,7 +409,7 @@ public class BitmapFactory { * cannot be used to decode a bitmap, the function returns null. * The stream's position will be where ever it was after the encoded data * was read. - * + * * @param is The input stream that holds the raw data to be decoded into a * bitmap. * @return The decoded bitmap, or null if the image data could not be @@ -441,7 +437,7 @@ public class BitmapFactory { public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { return nativeDecodeFileDescriptor(fd, outPadding, opts); } - + /** * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded * return null. The position within the descriptor will not be changed when @@ -453,7 +449,7 @@ public class BitmapFactory { public static Bitmap decodeFileDescriptor(FileDescriptor fd) { return nativeDecodeFileDescriptor(fd, null, null); } - + private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, Rect padding, Options opts); private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 06d53e3a99f9..4498e1a2e117 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1404,7 +1404,11 @@ public class Canvas { protected void finalize() throws Throwable { super.finalize(); - finalizer(mNativeCanvas); + // If the constructor threw an exception before setting mNativeCanvas, the native finalizer + // must not be invoked. + if (mNativeCanvas != 0) { + finalizer(mNativeCanvas); + } } /** diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index 2b24ef227e33..778c90304dcb 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -57,7 +57,9 @@ public class NinePatch { mBitmap = patch.mBitmap; mChunk = patch.mChunk; mSrcName = patch.mSrcName; - mPaint = new Paint(patch.mPaint); + if (patch.mPaint != null) { + mPaint = new Paint(patch.mPaint); + } validateNinePatchChunk(mBitmap.ni(), mChunk); } @@ -120,7 +122,6 @@ public class NinePatch { public native static boolean isNinePatchChunk(byte[] chunk); - private final Rect mRect = new Rect(); private final Bitmap mBitmap; private final byte[] mChunk; private Paint mPaint; diff --git a/location/java/android/location/ILocationCollector.aidl b/graphics/java/android/graphics/drawable/Animatable.java index b2e17969814c..9dc62c36d618 100644 --- a/location/java/android/location/ILocationCollector.aidl +++ b/graphics/java/android/graphics/drawable/Animatable.java @@ -14,23 +14,26 @@ * limitations under the License. */ -package android.location; - -import android.location.Location; +package android.graphics.drawable; /** - * Listens for GPS and cell/wifi changes and anonymously uploads to server - * for improving quality of service of NetworkLocationProvider. - * This service is only enabled when the user has enabled the - * network location provider. - * - * {@hide} + * Interface that drawables suporting animations should implement. */ -oneway interface ILocationCollector { +public interface Animatable { + /** + * Starts the drawable's animation. + */ + void start(); + + /** + * Stops the drawable's animation. + */ + void stop(); + /** - * Updates GPS location if collection is enabled - * - * @param location location object + * Indicates whether the animation is running. + * + * @return True if the animation is running, false otherwise. */ - void updateLocation(in Location location); + boolean isRunning(); } diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java new file mode 100644 index 000000000000..ac96f20bc527 --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -0,0 +1,332 @@ +/* + * 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.graphics.drawable; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.ColorFilter; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.util.Log; +import android.os.SystemClock; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +import com.android.internal.R; + +/** + * @hide + */ +public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable, + Animatable { + + private AnimatedRotateState mState; + private boolean mMutated; + private float mCurrentDegrees; + private float mIncrement; + private boolean mRunning; + + public AnimatedRotateDrawable() { + this(null); + } + + private AnimatedRotateDrawable(AnimatedRotateState rotateState) { + mState = new AnimatedRotateState(rotateState, this); + init(); + } + + private void init() { + final AnimatedRotateState state = mState; + mIncrement = 360.0f / (float) state.mFramesCount; + final Drawable drawable = state.mDrawable; + if (drawable != null) { + drawable.setFilterBitmap(true); + if (drawable instanceof BitmapDrawable) { + ((BitmapDrawable) drawable).setAntiAlias(true); + } + } + } + + public void draw(Canvas canvas) { + int saveCount = canvas.save(); + + final AnimatedRotateState st = mState; + final Drawable drawable = st.mDrawable; + final Rect bounds = drawable.getBounds(); + + int w = bounds.right - bounds.left; + int h = bounds.bottom - bounds.top; + + float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX; + float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY; + + canvas.rotate(mCurrentDegrees, px, py); + + drawable.draw(canvas); + + canvas.restoreToCount(saveCount); + } + + public void start() { + if (!mRunning) { + mRunning = true; + nextFrame(); + } + } + + public void stop() { + mRunning = false; + unscheduleSelf(this); + } + + public boolean isRunning() { + return mRunning; + } + + private void nextFrame() { + unscheduleSelf(this); + scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration); + } + + public void run() { + // TODO: This should be computed in draw(Canvas), based on the amount + // of time since the last frame drawn + mCurrentDegrees += mIncrement; + if (mCurrentDegrees > (360.0f - mIncrement)) { + mCurrentDegrees = 0.0f; + } + invalidateSelf(); + nextFrame(); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + mState.mDrawable.setVisible(visible, restart); + boolean changed = super.setVisible(visible, restart); + if (visible) { + if (changed || restart) { + mCurrentDegrees = 0.0f; + nextFrame(); + } + } else { + unscheduleSelf(this); + } + return changed; + } + + /** + * Returns the drawable rotated by this RotateDrawable. + */ + public Drawable getDrawable() { + return mState.mDrawable; + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | mState.mChangingConfigurations + | mState.mDrawable.getChangingConfigurations(); + } + + public void setAlpha(int alpha) { + mState.mDrawable.setAlpha(alpha); + } + + public void setColorFilter(ColorFilter cf) { + mState.mDrawable.setColorFilter(cf); + } + + public int getOpacity() { + return mState.mDrawable.getOpacity(); + } + + public void invalidateDrawable(Drawable who) { + if (mCallback != null) { + mCallback.invalidateDrawable(this); + } + } + + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (mCallback != null) { + mCallback.scheduleDrawable(this, what, when); + } + } + + public void unscheduleDrawable(Drawable who, Runnable what) { + if (mCallback != null) { + mCallback.unscheduleDrawable(this, what); + } + } + + @Override + public boolean getPadding(Rect padding) { + return mState.mDrawable.getPadding(padding); + } + + @Override + public boolean isStateful() { + return mState.mDrawable.isStateful(); + } + + @Override + protected void onBoundsChange(Rect bounds) { + mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + @Override + public int getIntrinsicWidth() { + return mState.mDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mState.mDrawable.getIntrinsicHeight(); + } + + @Override + public ConstantState getConstantState() { + if (mState.canConstantState()) { + mState.mChangingConfigurations = super.getChangingConfigurations(); + return mState; + } + return null; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + + final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable); + + super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible); + + TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX); + final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; + final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); + final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; + final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + + final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12); + final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150); + + final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0); + Drawable drawable = null; + if (res > 0) { + drawable = r.getDrawable(res); + } + + a.recycle(); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && + (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) { + Log.w("drawable", "Bad element under <animated-rotate>: " + + parser .getName()); + } + } + + if (drawable == null) { + Log.w("drawable", "No drawable specified for <animated-rotate>"); + } + + final AnimatedRotateState rotateState = mState; + rotateState.mDrawable = drawable; + rotateState.mPivotXRel = pivotXRel; + rotateState.mPivotX = pivotX; + rotateState.mPivotYRel = pivotYRel; + rotateState.mPivotY = pivotY; + rotateState.mFramesCount = framesCount; + rotateState.mFrameDuration = frameDuration; + + init(); + + if (drawable != null) { + drawable.setCallback(this); + } + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + + final static class AnimatedRotateState extends Drawable.ConstantState { + Drawable mDrawable; + + int mChangingConfigurations; + + boolean mPivotXRel; + float mPivotX; + boolean mPivotYRel; + float mPivotY; + int mFrameDuration; + int mFramesCount; + + private boolean mCanConstantState; + private boolean mCheckedConstantState; + + public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner) { + if (source != null) { + mDrawable = source.mDrawable.getConstantState().newDrawable(); + mDrawable.setCallback(owner); + mPivotXRel = source.mPivotXRel; + mPivotX = source.mPivotX; + mPivotYRel = source.mPivotYRel; + mPivotY = source.mPivotY; + mFramesCount = source.mFramesCount; + mFrameDuration = source.mFrameDuration; + mCanConstantState = mCheckedConstantState = true; + } + } + + @Override + public Drawable newDrawable() { + return new AnimatedRotateDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + public boolean canConstantState() { + if (!mCheckedConstantState) { + mCanConstantState = mDrawable.getConstantState() != null; + mCheckedConstantState = true; + } + + return mCanConstantState; + } + } +} diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index bab1703fbf24..68718c9763eb 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -71,7 +71,7 @@ import android.util.AttributeSet; * @attr ref android.R.styleable#AnimationDrawableItem_duration * @attr ref android.R.styleable#AnimationDrawableItem_drawable */ -public class AnimationDrawable extends DrawableContainer implements Runnable { +public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable { private final AnimationState mAnimationState; private int mCurFrame = -1; private boolean mMutated; diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f0d49f5b9ac3..910e111e0947 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -745,6 +745,8 @@ public abstract class Drawable { drawable = new ClipDrawable(); } else if (name.equals("rotate")) { drawable = new RotateDrawable(); + } else if (name.equals("animated-rotate")) { + drawable = new AnimatedRotateDrawable(); } else if (name.equals("animation-list")) { drawable = new AnimationDrawable(); } else if (name.equals("inset")) { diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index f8b88d00a8f1..376b1df81b6a 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -234,8 +234,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { - for (Drawable child : mDrawableContainerState.mDrawables) { - child.mutate(); + final int N = mDrawableContainerState.getChildCount(); + final Drawable[] drawables = mDrawableContainerState.getChildren(); + for (int i = 0; i < N; i++) { + drawables[i].mutate(); } mMutated = true; } diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 3db45f0daa08..a7a870881ea5 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/AudioRecord.h b/include/media/AudioRecord.h index 6aa40d008eba..13e51eea1eb4 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -39,10 +39,15 @@ class AudioRecord { public: - enum stream_type { + // input sources values must always be defined in the range + // [AudioRecord::DEFAULT_INPUT, AudioRecord::NUM_INPUT_SOURCES[ + enum input_source { DEFAULT_INPUT =-1, MIC_INPUT = 0, - NUM_STREAM_TYPES + VOICE_UPLINK_INPUT = 1, + VOICE_DOWNLINK_INPUT = 2, + VOICE_CALL_INPUT = 3, + NUM_INPUT_SOURCES }; static const int DEFAULT_SAMPLE_RATE = 8000; @@ -118,7 +123,7 @@ public: * * Parameters: * - * streamType: Select the audio input to record to (e.g. AudioRecord::MIC_INPUT). + * inputSource: Select the audio input to record to (e.g. AudioRecord::MIC_INPUT). * sampleRate: Track sampling rate in Hz. * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed * 16 bits per sample). @@ -140,7 +145,7 @@ public: RECORD_IIR_ENABLE = AudioSystem::TX_IIR_ENABLE }; - AudioRecord(int streamType, + AudioRecord(int inputSource, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, @@ -165,7 +170,7 @@ public: * - NO_INIT: audio server or audio hardware not initialized * - PERMISSION_DENIED: recording is not allowed for the requesting process * */ - status_t set(int streamType = 0, + status_t set(int inputSource = 0, uint32_t sampleRate = 0, int format = 0, int channelCount = 0, @@ -192,11 +197,11 @@ public: /* getters, see constructor */ - uint32_t sampleRate() const; int format() const; int channelCount() const; uint32_t frameCount() const; int frameSize() const; + int inputSource() const; /* After it's created the track is not active. Call start() to @@ -211,7 +216,7 @@ public: status_t stop(); bool stopped() const; - /* get sample rate for this track + /* get sample rate for this record track */ uint32_t getSampleRate(); @@ -317,13 +322,13 @@ private: sp<ClientRecordThread> mClientRecordThread; Mutex mRecordThreadLock; - uint32_t mSampleRate; uint32_t mFrameCount; audio_track_cblk_t* mCblk; uint8_t mFormat; uint8_t mChannelCount; - uint8_t mReserved[2]; + uint8_t mInputSource; + uint8_t mReserved; status_t mStatus; uint32_t mLatency; diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index ba0467c1603c..7c86a6585287 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -201,7 +201,6 @@ public: /* getters, see constructor */ int streamType() const; - uint32_t sampleRate() const; int format() const; int channelCount() const; uint32_t frameCount() const; @@ -246,7 +245,7 @@ public: /* set sample rate for this track, mostly used for games' sound effects */ - void setSampleRate(int sampleRate); + status_t setSampleRate(int sampleRate); uint32_t getSampleRate(); /* Enables looping and sets the start and end points of looping. diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 6f13fe0b52c2..3e59d856085d 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -54,7 +54,7 @@ public: virtual sp<IAudioRecord> openRecord( pid_t pid, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index 8125cc96dfc8..d1d96b141635 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -34,7 +34,7 @@ class IMediaPlayerService: public IInterface public: DECLARE_META_INTERFACE(MediaPlayerService); - virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid) = 0; + virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid) = 0; virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0; virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0; @@ -57,4 +57,3 @@ public: }; // namespace android #endif // ANDROID_IMEDIAPLAYERSERVICE_H - diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 7f0e7b3a473e..7bf555a43320 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -123,4 +123,3 @@ public: #endif // ANDROID_MEDIAPLAYERINTERFACE_H - diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 6b0cc8ab8cce..eafa661eeee1 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/media/mediaplayer.h b/include/media/mediaplayer.h index 255a67b0d84a..513ffe1acabb 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -82,7 +82,7 @@ enum media_error_type { // 0xx: Reserved // 7xx: Android Player info/warning (e.g player lagging behind.) // 8xx: Media info/warning (e.g media badly interleaved.) -// +// enum media_info_type { // 0xx MEDIA_INFO_UNKNOWN = 1, diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index b9ea0c66e301..9b54ca9a3282 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -30,30 +30,55 @@ class ICamera; typedef void (*media_completion_f)(status_t status, void *cookie); /* Do not change these values without updating their counterparts - * in java/android/android/media/MediaRecorder.java! + * in media/java/android/media/MediaRecorder.java! */ enum audio_source { AUDIO_SOURCE_DEFAULT = 0, AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_VOICE_UPLINK = 2, + AUDIO_SOURCE_VOICE_DOWNLINK = 3, + AUDIO_SOURCE_VOICE_CALL = 4, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_CALL, + + AUDIO_SOURCE_LIST_END // must be last - used to validate audio source type }; enum video_source { VIDEO_SOURCE_DEFAULT = 0, VIDEO_SOURCE_CAMERA = 1, + + VIDEO_SOURCE_LIST_END // must be last - used to validate audio source type }; -//Please update java/android/android/media/MediaRecorder.java if the following is updated. +//Please update media/java/android/media/MediaRecorder.java if the following is updated. enum output_format { OUTPUT_FORMAT_DEFAULT = 0, - OUTPUT_FORMAT_THREE_GPP, - OUTPUT_FORMAT_MPEG_4, - OUTPUT_FORMAT_RAW_AMR, + OUTPUT_FORMAT_THREE_GPP = 1, + OUTPUT_FORMAT_MPEG_4 = 2, + + + OUTPUT_FORMAT_AUDIO_ONLY_START = 3, // Used in validating the output format. Should be the + // at the start of the audio only output formats. + + /* These are audio only file formats */ + OUTPUT_FORMAT_RAW_AMR = 3, //to be backward compatible + OUTPUT_FORMAT_AMR_NB = 3, + OUTPUT_FORMAT_AMR_WB = 4, + OUTPUT_FORMAT_AAC_ADIF = 5, + OUTPUT_FORMAT_AAC_ADTS = 6, + OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; enum audio_encoder { AUDIO_ENCODER_DEFAULT = 0, AUDIO_ENCODER_AMR_NB = 1, + AUDIO_ENCODER_AMR_WB = 2, + AUDIO_ENCODER_AAC = 3, + AUDIO_ENCODER_AAC_PLUS = 4, + AUDIO_ENCODER_EAAC_PLUS = 5, + + AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type }; enum video_encoder { @@ -61,8 +86,11 @@ enum video_encoder { VIDEO_ENCODER_H263 = 1, VIDEO_ENCODER_H264 = 2, VIDEO_ENCODER_MPEG_4_SP = 3, + + VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type }; + // Maximum frames per second is 24 #define MEDIA_RECORDER_MAX_FRAME_RATE 24 @@ -101,7 +129,7 @@ enum media_recorder_error_type { // The codes are distributed as follow: // 0xx: Reserved // 8xx: General info/warning -// +// enum media_recorder_info_type { MEDIA_RECORDER_INFO_UNKNOWN = 1, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800, diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index bda969ce8325..496a73987e74 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -26,7 +26,6 @@ namespace android { // ---------------------------------------------------------------------------- -#define MAX_SAMPLE_RATE 65535 #define THREAD_PRIORITY_AUDIO_CLIENT (ANDROID_PRIORITY_AUDIO) // Maximum cumulated timeout milliseconds before restarting audioflinger thread #define MAX_STARTUP_TIMEOUT_MS 3000 // Longer timeout period at startup to cope with A2DP init time @@ -55,9 +54,9 @@ struct audio_track_cblk_t uint16_t volume[2]; uint32_t volumeLR; }; - uint16_t sampleRate; - uint16_t channels; - int16_t flowControlFlag; // underrun (out) or overrrun (in) indication + uint32_t sampleRate; + uint8_t channels; + uint8_t flowControlFlag; // underrun (out) or overrrun (in) indication uint8_t out; // out equals 1 for AudioTrack and 0 for AudioRecord uint8_t forceReady; uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h index 0c7ad462fb2a..a85f275bf9b1 100644 --- a/include/private/opengles/gl_context.h +++ b/include/private/opengles/gl_context.h @@ -456,7 +456,7 @@ struct matrix_stack_t { void validate(); matrixf_t& top() { return stack[depth]; } const matrixf_t& top() const { return stack[depth]; } - const uint32_t top_ops() const { return ops[depth]; } + uint32_t top_ops() const { return ops[depth]; } inline bool isRigidBody() const { return !(ops[depth] & ~(OP_TRANSLATE|OP_UNIFORM_SCALE|OP_ROTATE)); } diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h new file mode 100644 index 000000000000..21cb73bc5527 --- /dev/null +++ b/include/tts/TtsEngine.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <media/AudioSystem.h> + +// This header defines the interface used by the Android platform +// to access Text-To-Speech functionality in shared libraries that implement +// speech synthesis and the management of resources associated with the +// synthesis. +// An example of the implementation of this interface can be found in +// FIXME: add path+name to implementation of default TTS engine +// Libraries implementing this interface are used in: +// frameworks/base/tts/jni/android_tts_SpeechSynthesis.cpp + +namespace android { + +enum tts_synth_status { + TTS_SYNTH_DONE = 0, + TTS_SYNTH_PENDING = 1 +}; + +enum tts_callback_status { + TTS_CALLBACK_HALT = 0, + TTS_CALLBACK_CONTINUE = 1 +}; + +// The callback is used by the implementation of this interface to notify its +// client, the Android TTS service, that the last requested synthesis has been +// completed. // TODO reword +// The callback for synthesis completed takes: +// @param [inout] void *& - The userdata pointer set in the original +// synth call +// @param [in] uint32_t - Track sampling rate in Hz +// @param [in] audio_format - The AudioSystem::audio_format enum +// @param [in] int - The number of channels +// @param [inout] int8_t *& - A buffer of audio data only valid during the +// execution of the callback +// @param [inout] size_t & - The size of the buffer +// @param [in] tts_synth_status - indicate whether the synthesis is done, or +// if more data is to be synthesized. +// @return TTS_CALLBACK_HALT to indicate the synthesis must stop, +// TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if +// there is more data to produce. +typedef tts_callback_status (synthDoneCB_t)(void *&, uint32_t, + AudioSystem::audio_format, int, int8_t *&, size_t&, tts_synth_status); + +class TtsEngine; +extern "C" TtsEngine* getTtsEngine(); + +enum tts_result { + TTS_SUCCESS = 0, + TTS_FAILURE = -1, + TTS_FEATURE_UNSUPPORTED = -2, + TTS_VALUE_INVALID = -3, + TTS_PROPERTY_UNSUPPORTED = -4, + TTS_PROPERTY_SIZE_TOO_SMALL = -5, + 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: + // Initialize the TTS engine and returns whether initialization succeeded. + // @param synthDoneCBPtr synthesis callback function pointer + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result init(synthDoneCB_t synthDoneCBPtr); + + // Shut down the TTS engine and releases all associated resources. + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result shutdown(); + + // Interrupt synthesis and flushes any synthesized data that hasn't been + // output yet. This will block until callbacks underway are completed. + // @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 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 *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 + // 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 setLanguage(const char *lang, const char *country, const char *variant); + + // Retrieve the currently set language, country and variant, or empty strings if none of + // parameters have been set. Language and country are represented by their 3-letter ISO code + // @param[out] pointer to the retrieved 3-letter code language value + // @param[out] pointer to the retrieved 3-letter code country value + // @param[out] pointer to the retrieved variant value + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result getLanguage(char *language, char *country, char *variant); + + // Notifies the engine what audio parameters should be used for the synthesis. + // This is meant to be used as a hint, the engine implementation will set the output values + // to those of the synthesis format, based on a given hint. + // @param[inout] encoding in: the desired audio sample format + // out: the format used by the TTS engine + // @param[inout] rate in: the desired audio sample rate + // out: the sample rate used by the TTS engine + // @param[inout] channels in: the desired number of audio channels + // out: the number of channels used by the TTS engine + // @return TTS_SUCCESS, or TTS_FAILURE + virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate, + int& channels); + + // Set a property for the the TTS engine + // "size" is the maximum size of "value" for properties "property" + // @param property pointer to the property name + // @param value pointer to the property value + // @param size maximum size required to store this type of property + // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE, + // or TTS_VALUE_INVALID + virtual tts_result setProperty(const char *property, const char *value, + const size_t size); + + // Retrieve a property from the TTS engine + // @param property pointer to the property name + // @param[out] value pointer to the retrieved language value + // @param[inout] iosize in: stores the size available to store the + // property value. + // out: stores the size required to hold the language + // value if getLanguage() returned + // TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise + // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, + // or TTS_PROPERTY_SIZE_TOO_SMALL + virtual tts_result getProperty(const char *property, char *value, + size_t *iosize); + + // Synthesize the text. + // As the synthesis is performed, the engine invokes the callback to notify + // the TTS framework that it has filled the given buffer, and indicates how + // many bytes it wrote. The callback is called repeatedly until the engine + // has generated all the audio data corresponding to the text. + // Note about the format of the input: the text parameter may use the + // following elements + // and their respective attributes as defined in the SSML 1.0 specification: + // * lang + // * say-as: + // o interpret-as + // * phoneme + // * voice: + // o gender, + // o age, + // o variant, + // o name + // * emphasis + // * break: + // o strength, + // o time + // * prosody: + // o pitch, + // o contour, + // o range, + // o rate, + // o duration, + // o volume + // * mark + // Differences between this text format and SSML are: + // * full SSML documents are not supported + // * namespaces are not supported + // Text is coded in UTF-8. + // @param text the UTF-8 text to synthesize + // @param userdata pointer to be returned when the call is invoked + // @param buffer the location where the synthesized data must be written + // @param bufferSize the number of bytes that can be written in buffer + // @return TTS_SUCCESS or TTS_FAILURE + virtual tts_result synthesizeText(const char *text, int8_t *buffer, + size_t bufferSize, void *userdata); + + // Synthesize IPA text. + // As the synthesis is performed, the engine invokes the callback to notify + // the TTS framework that it has filled the given buffer, and indicates how + // many bytes it wrote. The callback is called repeatedly until the engine + // has generated all the audio data corresponding to the IPA data. + // @param ipa the IPA data to synthesize + // @param userdata pointer to be returned when the call is invoked + // @param buffer the location where the synthesized data must be written + // @param bufferSize the number of bytes that can be written in buffer + // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported, + // otherwise TTS_SUCCESS or TTS_FAILURE + virtual tts_result synthesizeIpa(const char *ipa, int8_t *buffer, + size_t bufferSize, void *userdata); +}; + +} // namespace android + diff --git a/include/ui/Camera.h b/include/ui/Camera.h index 048bdd560b0b..e3544ab0f155 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, @@ -80,16 +76,25 @@ enum { CAMERA_MSG_COMPRESSED_IMAGE }; +// camera fatal errors +enum { + CAMERA_ERROR_UKNOWN = 1, + CAMERA_ERROR_SERVER_DIED = 100 +}; + class ICameraService; class ICamera; 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 +149,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 +160,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 +181,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/ui/Point.h b/include/ui/Point.h index dbbad1ecc208..1653120a6d13 100644 --- a/include/ui/Point.h +++ b/include/ui/Point.h @@ -31,12 +31,9 @@ public: // because we want the compiler generated versions // Default constructor doesn't initialize the Point - inline Point() - { + inline Point() { } - - inline Point(int _x, int _y) : x(_x), y(_y) - { + inline Point(int x, int y) : x(x), y(y) { } inline bool operator == (const Point& rhs) const { @@ -57,8 +54,8 @@ public: } inline Point& operator - () { - x=-x; - y=-y; + x = -x; + y = -y; return *this; } @@ -73,11 +70,13 @@ public: return *this; } - Point operator + (const Point& rhs) const { - return Point(x+rhs.x, y+rhs.y); + const Point operator + (const Point& rhs) const { + const Point result(x+rhs.x, y+rhs.y); + return result; } - Point operator - (const Point& rhs) const { - return Point(x-rhs.x, y-rhs.y); + const Point operator - (const Point& rhs) const { + const Point result(x-rhs.x, y-rhs.y); + return result; } }; diff --git a/include/ui/Rect.h b/include/ui/Rect.h index d232847113ab..da7294476246 100644 --- a/include/ui/Rect.h +++ b/include/ui/Rect.h @@ -33,23 +33,16 @@ public: // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions - inline Rect() - { + inline Rect() { } - inline Rect(int w, int h) - : left(0), top(0), right(w), bottom(h) - { + : left(0), top(0), right(w), bottom(h) { } - inline Rect(int l, int t, int r, int b) - : left(l), top(t), right(r), bottom(b) - { + : left(l), top(t), right(r), bottom(b) { } - inline Rect(const Point& lt, const Point& rb) - : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) - { + : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) { } void makeInvalid(); @@ -78,21 +71,22 @@ public: return bottom-top; } - // returns left-top Point non-const reference, can be assigned - inline Point& leftTop() { - return reinterpret_cast<Point&>(left); + void setLeftTop(const Point& lt) { + left = lt.x; + top = lt.y; } - // returns right bottom non-const reference, can be assigned - inline Point& rightBottom() { - return reinterpret_cast<Point&>(right); + + void setRightBottom(const Point& rb) { + right = rb.x; + bottom = rb.y; } // the following 4 functions return the 4 corners of the rect as Point - inline const Point& leftTop() const { - return reinterpret_cast<const Point&>(left); + Point leftTop() const { + return Point(left, top); } - inline const Point& rightBottom() const { - return reinterpret_cast<const Point&>(right); + Point rightBottom() const { + return Point(right, bottom); } Point rightTop() const { return Point(right, top); @@ -133,8 +127,8 @@ public: Rect& operator -= (const Point& rhs) { return offsetBy(-rhs.x, -rhs.y); } - Rect operator + (const Point& rhs) const; - Rect operator - (const Point& rhs) const; + const Rect operator + (const Point& rhs) const; + const Rect operator - (const Point& rhs) const; void translate(int dx, int dy) { // legacy, don't use. offsetBy(dx, dy); diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index e94c0e8feeee..d8994e05a027 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -153,6 +153,18 @@ public: AssetDir* openDir(const char* dirName); /* + * Open a directory within a particular path of the asset manager. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openNonAssetDir(void* cookie, const char* dirName); + + /* * Get the type of a file in the asset hierarchy. They will either * be "regular" or "directory". [Currently only works for "regular".] * @@ -239,6 +251,9 @@ private: Asset* getResourceTableAsset(); Asset* setResourceTableAsset(Asset* asset); + ResTable* getResourceTable(); + ResTable* setResourceTable(ResTable* res); + bool isUpToDate(); protected: @@ -253,6 +268,7 @@ private: time_t mModWhen; Asset* mResourceTableAsset; + ResTable* mResourceTable; static Mutex gLock; static DefaultKeyedVector<String8, wp<SharedZip> > gOpen; @@ -276,8 +292,11 @@ private: */ ZipFileRO* getZip(const String8& path); - Asset* getZipResourceTable(const String8& path); - Asset* setZipResourceTable(const String8& path, Asset* asset); + Asset* getZipResourceTableAsset(const String8& path); + Asset* setZipResourceTableAsset(const String8& path, Asset* asset); + + ResTable* getZipResourceTable(const String8& path); + ResTable* setZipResourceTable(const String8& path, ResTable* res); // generate path, e.g. "common/en-US-noogle.zip" static String8 getPathName(const char* path); diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h new file mode 100644 index 000000000000..b1f504512f51 --- /dev/null +++ b/include/utils/BackupHelpers.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_BACKUP_HELPERS_H +#define _UTILS_BACKUP_HELPERS_H + +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/KeyedVector.h> + +namespace android { + +enum { + BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian) +}; + +typedef struct { + int type; // BACKUP_HEADER_ENTITY_V1 + int keyLen; // length of the key name, not including the null terminator + int dataSize; // size of the data, not including the padding, -1 means delete +} entity_header_v1; + +struct SnapshotHeader { + int magic0; + int fileCount; + int magic1; + int totalSize; +}; + +struct FileState { + int modTime_sec; + int modTime_nsec; + int mode; + int size; + int crc32; + int nameLen; +}; + +struct FileRec { + String8 file; + bool deleted; + FileState s; +}; + + +/** + * Writes the data. + * + * If an error occurs, it poisons this object and all write calls will fail + * with the error that occurred. + */ +class BackupDataWriter +{ +public: + BackupDataWriter(int fd); + // does not close fd + ~BackupDataWriter(); + + status_t WriteEntityHeader(const String8& key, size_t dataSize); + status_t WriteEntityData(const void* data, size_t size); + + void SetKeyPrefix(const String8& keyPrefix); + +private: + explicit BackupDataWriter(); + status_t write_padding_for(int n); + + int m_fd; + status_t m_status; + ssize_t m_pos; + int m_entityCount; + String8 m_keyPrefix; +}; + +/** + * Reads the data. + * + * If an error occurs, it poisons this object and all write calls will fail + * with the error that occurred. + */ +class BackupDataReader +{ +public: + BackupDataReader(int fd); + // does not close fd + ~BackupDataReader(); + + status_t Status(); + status_t ReadNextHeader(bool* done, int* type); + + bool HasEntities(); + status_t ReadEntityHeader(String8* key, size_t* dataSize); + status_t SkipEntityData(); // must be called with the pointer at the begining of the data. + ssize_t ReadEntityData(void* data, size_t size); + +private: + explicit BackupDataReader(); + status_t skip_padding(); + + int m_fd; + bool m_done; + status_t m_status; + ssize_t m_pos; + ssize_t m_dataEndPos; + int m_entityCount; + union { + int type; + entity_header_v1 entity; + } m_header; + String8 m_key; +}; + +int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, + char const* const* files, char const* const *keys, int fileCount); + +class RestoreHelperBase +{ +public: + RestoreHelperBase(); + ~RestoreHelperBase(); + + status_t WriteFile(const String8& filename, BackupDataReader* in); + status_t WriteSnapshot(int fd); + +private: + void* m_buf; + bool m_loggedUnknownMetadata; + KeyedVector<String8,FileRec> m_files; +}; + +#define TEST_BACKUP_HELPERS 1 + +#if TEST_BACKUP_HELPERS +int backup_helper_test_empty(); +int backup_helper_test_four(); +int backup_helper_test_files(); +int backup_helper_test_null_base(); +int backup_helper_test_missing_file(); +int backup_helper_test_data_writer(); +int backup_helper_test_data_reader(); +#endif + +} // namespace android + +#endif // _UTILS_BACKUP_HELPERS_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h index 4c0606763d6f..baa3a83dddfc 100644 --- a/include/utils/ByteOrder.h +++ b/include/utils/ByteOrder.h @@ -38,6 +38,16 @@ * intent is to allow us to avoid byte swapping on the device. */ +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} + +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + #define DEVICE_BYTE_ORDER LITTLE_ENDIAN #if BYTE_ORDER == DEVICE_BYTE_ORDER @@ -49,16 +59,6 @@ #else -static inline uint32_t android_swap_long(uint32_t v) -{ - return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); -} - -static inline uint16_t android_swap_short(uint16_t v) -{ - return (v<<8) | (v>>8); -} - #define dtohl(x) (android_swap_long(x)) #define dtohs(x) (android_swap_short(x)) #define htodl(x) (android_swap_long(x)) @@ -66,4 +66,16 @@ static inline uint16_t android_swap_short(uint16_t v) #endif +#if BYTE_ORDER == LITTLE_ENDIAN +#define fromlel(x) (x) +#define fromles(x) (x) +#define tolel(x) (x) +#define toles(x) (x) +#else +#define fromlel(x) (android_swap_long(x)) +#define fromles(x) (android_swap_short(x)) +#define tolel(x) (android_swap_long(x)) +#define toles(x) (android_swap_short(x)) +#endif + #endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h index 9087c4465b96..af1490a02973 100644 --- a/include/utils/Parcel.h +++ b/include/utils/Parcel.h @@ -80,8 +80,11 @@ public: status_t writeStrongBinder(const sp<IBinder>& val); status_t writeWeakBinder(const wp<IBinder>& val); - // doesn't take ownership of the native_handle - status_t writeNativeHandle(const native_handle& handle); + // Place a native_handle into the parcel (the native_handle's file- + // descriptors are dup'ed, so it is safe to delete the native_handle + // when this function returns). + // Doesn't take ownership of the native_handle. + status_t writeNativeHandle(const native_handle* handle); // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. @@ -114,12 +117,11 @@ public: wp<IBinder> readWeakBinder() const; - // if alloc is NULL, native_handle is allocated with malloc(), otherwise - // alloc is used. If the function fails, the effects of alloc() must be - // reverted by the caller. - native_handle* readNativeHandle( - native_handle* (*alloc)(void* cookie, int numFds, int ints), - void* cookie) const; + // Retrieve native_handle from the parcel. This returns a copy of the + // parcel's native_handle (the caller takes ownership). The caller + // must free the native_handle with native_handle_close() and + // native_handle_delete(). + native_handle* readNativeHandle() const; // Retrieve a file descriptor from the parcel. This returns the raw fd diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9b8c3026a088..93bca4aefc08 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -71,7 +71,7 @@ namespace android { * The relative sizes of the stretchy segments indicates the relative * amount of stretchiness of the regions bordered by the segments. For * example, regions 3, 7 and 11 above will take up more horizontal space - * than regions 1, 5 and 9 since the horizonal segment associated with + * than regions 1, 5 and 9 since the horizontal segment associated with * the first set of regions is larger than the other set of regions. The * ratios of the amount of horizontal (or vertical) space taken by any * two stretchable slices is exactly the ratio of their corresponding @@ -87,7 +87,7 @@ namespace android { * the leftmost slices always start at x=0 and the rightmost slices * always end at the end of the image. So, for example, the regions 0, * 4 and 8 (which are fixed along the X axis) start at x value 0 and - * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at * xDiv[2]. * * The array pointed to by the colors field lists contains hints for @@ -626,25 +626,25 @@ public: event_code_t next(); // These are available for all nodes: - const int32_t getCommentID() const; + int32_t getCommentID() const; const uint16_t* getComment(size_t* outLen) const; - const uint32_t getLineNumber() const; + uint32_t getLineNumber() const; // This is available for TEXT: - const int32_t getTextID() const; + int32_t getTextID() const; const uint16_t* getText(size_t* outLen) const; ssize_t getTextValue(Res_value* outValue) const; // These are available for START_NAMESPACE and END_NAMESPACE: - const int32_t getNamespacePrefixID() const; + int32_t getNamespacePrefixID() const; const uint16_t* getNamespacePrefix(size_t* outLen) const; - const int32_t getNamespaceUriID() const; + int32_t getNamespaceUriID() const; const uint16_t* getNamespaceUri(size_t* outLen) const; // These are available for START_TAG and END_TAG: - const int32_t getElementNamespaceID() const; + int32_t getElementNamespaceID() const; const uint16_t* getElementNamespace(size_t* outLen) const; - const int32_t getElementNameID() const; + int32_t getElementNameID() const; const uint16_t* getElementName(size_t* outLen) const; // Remaining methods are for retrieving information about attributes @@ -653,14 +653,14 @@ public: size_t getAttributeCount() const; // Returns -1 if no namespace, -2 if idx out of range. - const int32_t getAttributeNamespaceID(size_t idx) const; + int32_t getAttributeNamespaceID(size_t idx) const; const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; - const int32_t getAttributeNameID(size_t idx) const; + int32_t getAttributeNameID(size_t idx) const; const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; - const uint32_t getAttributeNameResID(size_t idx) const; + uint32_t getAttributeNameResID(size_t idx) const; - const int32_t getAttributeValueStringID(size_t idx) const; + int32_t getAttributeValueStringID(size_t idx) const; const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; int32_t getAttributeDataType(size_t idx) const; @@ -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); } }; @@ -1401,7 +1441,7 @@ struct ResTable_type * This is the beginning of information about an entry in the resource * table. It holds the reference to the name of this entry, and is * immediately followed by one of: - * * A Res_value structures, if FLAG_COMPLEX is -not- set. + * * A Res_value structure, if FLAG_COMPLEX is -not- set. * * An array of ResTable_map structures, if FLAG_COMPLEX is set. * These supply a set of name/value mappings of data. */ @@ -1540,6 +1580,7 @@ public: bool copyData=false); status_t add(Asset* asset, void* cookie, bool copyData=false); + status_t add(ResTable* src); status_t getError() const; @@ -1781,7 +1822,7 @@ public: void getLocales(Vector<String8>* locales) const; #ifndef HAVE_ANDROID_OS - void print() const; + void print(bool inclValues) const; #endif private: @@ -1803,6 +1844,8 @@ private: status_t parsePackage( const ResTable_package* const pkg, const Header* const header); + void print_value(const Package* pkg, const Res_value& value) const; + mutable Mutex mLock; status_t mError; diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h deleted file mode 100644 index 137c5f104172..000000000000 --- a/include/utils/backup_helpers.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _UTILS_BACKUP_HELPERS_H -#define _UTILS_BACKUP_HELPERS_H - -int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, - char const* fileBase, char const* const* files, int fileCount); - -#define TEST_BACKUP_HELPERS 0 - -#if TEST_BACKUP_HELPERS -int backup_helper_test_empty(); -int backup_helper_test_four(); -int backup_helper_test_files(); -#endif - -#endif // _UTILS_BACKUP_HELPERS_H diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java new file mode 100644 index 000000000000..26d22aec57fb --- /dev/null +++ b/keystore/java/android/security/CertTool.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.content.Context; +import android.content.Intent; +import android.security.Keystore; +import android.text.TextUtils; + + +/** + * The CertTool class provides the functions to list the certs/keys, + * generate the certificate request(csr) and store the certificate into + * keystore. + * + * {@hide} + */ +public class CertTool { + static { + System.loadLibrary("certtool_jni"); + } + + public static final String ACTION_ADD_CREDENTIAL = + "android.security.ADD_CREDENTIAL"; + public static final String KEY_TYPE_NAME = "typeName"; + public static final String KEY_ITEM = "item"; + public static final String KEY_NAMESPACE = "namespace"; + public static final String KEY_DESCRIPTION = "description"; + + private static final String TAG = "CertTool"; + + private static final String TITLE_CA_CERT = "CA Certificate"; + private static final String TITLE_USER_CERT = "User Certificate"; + private static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore"; + private static final String TITLE_PRIVATE_KEY = "Private Key"; + private static final String UNKNOWN = "Unknown"; + private static final String ISSUER_NAME = "Issuer Name:"; + private static final String DISTINCT_NAME = "Distinct Name:"; + + private static final String CA_CERTIFICATE = "CACERT"; + private static final String USER_CERTIFICATE = "USRCERT"; + private static final String USER_KEY = "USRKEY"; + + private static final String KEYNAME_DELIMITER = "_"; + private static final Keystore sKeystore = Keystore.getInstance(); + + private native String generateCertificateRequest(int bits, String subject); + private native boolean isPkcs12Keystore(byte[] data); + private native int generateX509Certificate(byte[] data); + private native boolean isCaCertificate(int handle); + private native String getIssuerDN(int handle); + private native String getCertificateDN(int handle); + private native String getPrivateKeyPEM(int handle); + private native void freeX509Certificate(int handle); + + private static CertTool singleton = null; + + private CertTool() { } + + public static final CertTool getInstance() { + if (singleton == null) { + singleton = new CertTool(); + } + return singleton; + } + + public String getUserPrivateKey(String key) { + return USER_KEY + KEYNAME_DELIMITER + key; + } + + public String getUserCertificate(String key) { + return USER_CERTIFICATE + KEYNAME_DELIMITER + key; + } + + public String getCaCertificate(String key) { + return CA_CERTIFICATE + KEYNAME_DELIMITER + key; + } + + public String[] getAllUserCertificateKeys() { + return sKeystore.listKeys(USER_KEY); + } + + public String[] getAllCaCertificateKeys() { + return sKeystore.listKeys(CA_CERTIFICATE); + } + + public String[] getSupportedKeyStrenghs() { + return new String[] {"High Grade", "Medium Grade"}; + } + + private int getKeyLength(int index) { + if (index == 0) return 2048; + return 1024; + } + + public String generateKeyPair(int keyStrengthIndex, String challenge, + String dirName) { + return generateCertificateRequest(getKeyLength(keyStrengthIndex), + dirName); + } + + private Intent prepareIntent(String title, byte[] data, String namespace, + String issuer, String distinctName) { + Intent intent = new Intent(ACTION_ADD_CREDENTIAL); + intent.putExtra(KEY_TYPE_NAME, title); + intent.putExtra(KEY_ITEM + "0", data); + intent.putExtra(KEY_NAMESPACE + "0", namespace); + intent.putExtra(KEY_DESCRIPTION + "0", ISSUER_NAME + issuer); + intent.putExtra(KEY_DESCRIPTION + "1", DISTINCT_NAME + distinctName); + return intent; + } + + private void addExtraIntentInfo(Intent intent, String namespace, + String data) { + intent.putExtra(KEY_ITEM + "1", data); + intent.putExtra(KEY_NAMESPACE + "1", namespace); + } + + public synchronized void addCertificate(byte[] data, Context context) { + int handle; + Intent intent = null; + + if (isPkcs12Keystore(data)) { + intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY, + UNKNOWN, UNKNOWN); + } else if ((handle = generateX509Certificate(data)) != 0) { + String issuer = getIssuerDN(handle); + String distinctName = getCertificateDN(handle); + String privateKeyPEM = getPrivateKeyPEM(handle); + if (isCaCertificate(handle)) { + intent = prepareIntent(TITLE_CA_CERT, data, CA_CERTIFICATE, + issuer, distinctName); + } else { + intent = prepareIntent(TITLE_USER_CERT, data, USER_CERTIFICATE, + issuer, distinctName); + if (!TextUtils.isEmpty(privateKeyPEM)) { + addExtraIntentInfo(intent, USER_KEY, privateKeyPEM); + } + } + freeX509Certificate(handle); + } + if (intent != null) context.startActivity(intent); + } +} diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java new file mode 100644 index 000000000000..1f14da78c2c3 --- /dev/null +++ b/keystore/java/android/security/Keystore.java @@ -0,0 +1,239 @@ +/* + * 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.security; + +/** + * The Keystore class provides the functions to list the certs/keys in keystore. + * {@hide} + */ + +public abstract class Keystore { + private static final String TAG = "Keystore"; + private static final String[] NOTFOUND = new String[0]; + + // Keystore States + public static final int BOOTUP = 0; + public static final int UNINITIALIZED = 1; + public static final int LOCKED = 2; + public static final int UNLOCKED = 3; + + /** + */ + public static Keystore getInstance() { + return new FileKeystore(); + } + + public abstract int lock(); + public abstract int unlock(String password); + public abstract int getState(); + public abstract int changePassword(String oldPassword, String newPassword); + public abstract int setPassword(String firstPassword); + public abstract String[] listKeys(String namespace); + public abstract int put(String namespace, String keyname, String value); + public abstract String get(String namespace, String keyname); + public abstract int remove(String namespace, String keyname); + public abstract int reset(); + + // TODO: for migrating to the mini-keystore, clean up from here + /** + */ + public abstract String getCaCertificate(String key); + + /** + */ + public abstract String getUserCertificate(String key); + + /** + */ + public abstract String getUserPrivateKey(String key); + + /** + * Returns the array of the certificate keynames in keystore if successful. + * Or return an empty array if error. + * + * @return array of the certificate keynames + */ + public abstract String[] getAllUserCertificateKeys(); + + /** + */ + public abstract String[] getAllCaCertificateKeys(); + + /** + */ + public abstract String[] getSupportedKeyStrenghs(); + + /** + * Generates a key pair and returns the certificate request. + * @param keyStrengthIndex index to the array of supported key strengths + * @param challenge the challenge message in the keygen tag + * @param organizations the organization string, e.g., + * "/C=US/ST={state}/L={city}/O={company}/OU={app}/CN={hostname}" + * @return the certificate request + */ + public abstract String generateKeyPair( + int keyStrengthIndex, String challenge, String organizations); + + public abstract void addCertificate(byte[] cert); + // to here + + private static class FileKeystore extends Keystore { + private static final String SERVICE_NAME = "keystore"; + private static final String CA_CERTIFICATE = "CaCertificate"; + private static final String USER_CERTIFICATE = "UserCertificate"; + private static final String USER_KEY = "UserPrivateKey"; + private static final String COMMAND_DELIMITER = " "; + private static final ServiceCommand mServiceCommand = + new ServiceCommand(SERVICE_NAME); + + // TODO: for migrating to the mini-keystore, start from here + @Override + public String getUserPrivateKey(String key) { + return ""; + } + + @Override + public String getUserCertificate(String key) { + return ""; + } + + @Override + public String getCaCertificate(String key) { + return ""; + } + + @Override + public String[] getAllUserCertificateKeys() { + return new String[0]; + } + + @Override + public String[] getAllCaCertificateKeys() { + return new String[0]; + } + + @Override + public String[] getSupportedKeyStrenghs() { + // TODO: real implementation + return new String[] {"High Grade", "Medium Grade"}; + } + + @Override + public String generateKeyPair(int keyStrengthIndex, String challenge, + String organizations) { + // TODO: real implementation + return "-----BEGIN CERTIFICATE REQUEST-----" + + "\nMIICzjCCAbYCAQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh" + + "\nMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRYw" + + "\nFAYDVQQLEw1SZW1vdGUgQWNjZXNzMRAwDgYDVQQLEwdHbGFwdG9wMQ0wCwYDVQQD" + + "\nEwR0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAznwy7a16O35u" + + "\nODLQOw6yHAxozrrX1J+c0reiIh8GYohwKrBedFnQ/FnTls6bxY4fNHD+SZvFFgvU" + + "\nECBFOfRmRm7AFo51qT0t2a8qgvDLM6L1qGkmy94W28Q3OlcpF2QianHYdjyGT+Ac" + + "\nYDek1Zi/E/mdPzuVM/K8tkB7n8ktC0PTm1ZtdMRauE5R0WrEhWuF6In/2gy1Q/Zh" + + "\noy7/zQqpbPl2ouulvkx1Y3OXHM6XPNFLoHS1gH0HyAuBUokO0QmetRn6ngJSvz7e" + + "\nVD7QYRppGp+g4BxqaV9XSxhaaKrMs4PAld9enV51X9qjvjCRBve2QxtuJgMfGJdU" + + "\njGr/JweZoQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBADtxOtEseoLOVYh6sh4b" + + "\nWCdngK87uHn2bdGipFwKdNTxQDdxNQLAKdoGYIfbVsC1cDgFiufeNwVukxxymdnm" + + "\nk0GGK+0O0tZKENv8ysgfbgEsHpJH9FoR5Y5XEq1etejkcgCp59dyhrSk0DLyVm0D" + + "\nIfTC/nsK95H7AAGOkbbDFo2otyLNNrthYncQ9diAG0UzzLacA+86JXZmD3HyC48u" + + "\nI9hsivVnTTfl9afcfVAhfxbQ6HgkhZZjbjFjfABSd4v8wKlAAqK58VxCajNVOVcV" + + "\ncCzOWf6NpE7xEHCf32i8bWDP6hi0WgQcdpQwnZNKhhTLGNb23Uty6HYlJhbxexC7" + + "\nUoM=" + + "\n-----END CERTIFICATE REQUEST-----"; + } + + @Override + public void addCertificate(byte[] cert) { + // TODO: real implementation + } + + // to here + + @Override + public int lock() { + Reply result = mServiceCommand.execute(ServiceCommand.LOCK, null); + return (result != null) ? result.returnCode : -1; + } + + @Override + public int unlock(String password) { + Reply result = mServiceCommand.execute(ServiceCommand.UNLOCK, + password); + return (result != null) ? result.returnCode : -1; + } + + @Override + public int getState() { + Reply result = mServiceCommand.execute(ServiceCommand.GET_STATE, + null); + return (result != null) ? result.returnCode : -1; + } + + @Override + public int changePassword(String oldPassword, String newPassword) { + Reply result = mServiceCommand.execute(ServiceCommand.PASSWD, + oldPassword + " " + newPassword); + return (result != null) ? result.returnCode : -1; + } + + @Override + public int setPassword(String firstPassword) { + Reply result = mServiceCommand.execute(ServiceCommand.PASSWD, + firstPassword); + return (result != null) ? result.returnCode : -1; + } + + @Override + public String[] listKeys(String namespace) { + Reply result = mServiceCommand.execute(ServiceCommand.LIST_KEYS, + namespace); + if ((result == null) || (result.returnCode != 0) || + (result.len == 0)) { + return NOTFOUND; + } + return new String(result.data, 0, result.len).split("\\s+"); + } + + @Override + public int put(String namespace, String keyname, String value) { + Reply result = mServiceCommand.execute(ServiceCommand.PUT_KEY, + namespace + " " + keyname + " " + value); + return (result != null) ? result.returnCode : -1; + } + + @Override + public String get(String namespace, String keyname) { + Reply result = mServiceCommand.execute(ServiceCommand.GET_KEY, + namespace + " " + keyname); + return (result != null) ? ((result.returnCode != 0) ? null : + new String(result.data, 0, result.len)) : null; + } + + @Override + public int remove(String namespace, String keyname) { + Reply result = mServiceCommand.execute(ServiceCommand.REMOVE_KEY, + namespace + " " + keyname); + return (result != null) ? result.returnCode : -1; + } + + @Override + public int reset() { + Reply result = mServiceCommand.execute(ServiceCommand.RESET, null); + return (result != null) ? result.returnCode : -1; + } + } +} diff --git a/keystore/java/android/security/Reply.java b/keystore/java/android/security/Reply.java new file mode 100644 index 000000000000..15a0dde61e83 --- /dev/null +++ b/keystore/java/android/security/Reply.java @@ -0,0 +1,26 @@ +/* + * 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.security; + +/* + * {@hide} + */ +public class Reply { + public int len; + public int returnCode; + public byte[] data = new byte[ServiceCommand.BUFFER_LENGTH]; +} diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java new file mode 100644 index 000000000000..6178d59ae16a --- /dev/null +++ b/keystore/java/android/security/ServiceCommand.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.net.LocalSocketAddress; +import android.net.LocalSocket; +import android.util.Config; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/* + * ServiceCommand is used to connect to a service throught the local socket, + * and send out the command, return the result to the caller. + * {@hide} + */ +public class ServiceCommand { + public static final String SUCCESS = "0"; + public static final String FAILED = "-1"; + + // Opcodes for keystore commands. + public static final int LOCK = 0; + public static final int UNLOCK = 1; + public static final int PASSWD = 2; + public static final int GET_STATE = 3; + public static final int LIST_KEYS = 4; + public static final int GET_KEY = 5; + public static final int PUT_KEY = 6; + public static final int REMOVE_KEY = 7; + public static final int RESET = 8; + public static final int MAX_CMD_INDEX = 9; + + public static final int BUFFER_LENGTH = 4096; + + private String mServiceName; + private String mTag; + private InputStream mIn; + private OutputStream mOut; + private LocalSocket mSocket; + + private boolean connect() { + if (mSocket != null) { + return true; + } + Log.i(mTag, "connecting..."); + try { + mSocket = new LocalSocket(); + + LocalSocketAddress address = new LocalSocketAddress( + mServiceName, LocalSocketAddress.Namespace.RESERVED); + + mSocket.connect(address); + + mIn = mSocket.getInputStream(); + mOut = mSocket.getOutputStream(); + } catch (IOException ex) { + disconnect(); + return false; + } + return true; + } + + private void disconnect() { + Log.i(mTag,"disconnecting..."); + try { + if (mSocket != null) mSocket.close(); + } catch (IOException ex) { } + try { + if (mIn != null) mIn.close(); + } catch (IOException ex) { } + try { + if (mOut != null) mOut.close(); + } catch (IOException ex) { } + mSocket = null; + mIn = null; + mOut = null; + } + + private boolean readBytes(byte buffer[], int len) { + int off = 0, count; + if (len < 0) return false; + while (off != len) { + try { + count = mIn.read(buffer, off, len - off); + if (count <= 0) { + Log.e(mTag, "read error " + count); + break; + } + off += count; + } catch (IOException ex) { + Log.e(mTag,"read exception"); + break; + } + } + if (off == len) return true; + disconnect(); + return false; + } + + private Reply readReply() { + byte buf[] = new byte[4]; + Reply reply = new Reply(); + + if (!readBytes(buf, 4)) return null; + reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) | + ((((int) buf[2]) & 0xff) << 16) | + ((((int) buf[3]) & 0xff) << 24); + + if (!readBytes(buf, 4)) return null; + reply.returnCode = (((int) buf[0]) & 0xff) | + ((((int) buf[1]) & 0xff) << 8) | + ((((int) buf[2]) & 0xff) << 16) | + ((((int) buf[3]) & 0xff) << 24); + + if (reply.len > BUFFER_LENGTH) { + Log.e(mTag,"invalid reply length (" + reply.len + ")"); + disconnect(); + return null; + } + if (!readBytes(reply.data, reply.len)) return null; + return reply; + } + + private boolean writeCommand(int cmd, String _data) { + byte buf[] = new byte[8]; + byte[] data = (_data == null) ? new byte[0] : _data.getBytes(); + int len = data.length; + // the length of data + buf[0] = (byte) (len & 0xff); + buf[1] = (byte) ((len >> 8) & 0xff); + buf[2] = (byte) ((len >> 16) & 0xff); + buf[3] = (byte) ((len >> 24) & 0xff); + // the opcode of the command + buf[4] = (byte) (cmd & 0xff); + buf[5] = (byte) ((cmd >> 8) & 0xff); + buf[6] = (byte) ((cmd >> 16) & 0xff); + buf[7] = (byte) ((cmd >> 24) & 0xff); + try { + mOut.write(buf, 0, 8); + mOut.write(data, 0, len); + } catch (IOException ex) { + Log.e(mTag,"write error"); + disconnect(); + return false; + } + return true; + } + + private Reply executeCommand(int cmd, String data) { + if (!writeCommand(cmd, data)) { + /* If service died and restarted in the background + * (unlikely but possible) we'll fail on the next + * write (this one). Try to reconnect and write + * the command one more time before giving up. + */ + Log.e(mTag, "write command failed? reconnect!"); + if (!connect() || !writeCommand(cmd, data)) { + return null; + } + } + return readReply(); + } + + public synchronized Reply execute(int cmd, String data) { + Reply result; + if (!connect()) { + Log.e(mTag, "connection failed"); + return null; + } + result = executeCommand(cmd, data); + disconnect(); + return result; + } + + public ServiceCommand(String service) { + mServiceName = service; + mTag = service; + } +} diff --git a/keystore/jni/Android.mk b/keystore/jni/Android.mk new file mode 100644 index 000000000000..92c2d6d292ec --- /dev/null +++ b/keystore/jni/Android.mk @@ -0,0 +1,31 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + cert.c certtool.c + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libnativehelper \ + libutils \ + libcrypto + +ifeq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS),linux) +ifeq ($(TARGET_ARCH),x86) +LOCAL_LDLIBS += -lpthread -ldl -lrt -lssl +endif +endif +endif + +ifeq ($(WITH_MALLOC_LEAK_CHECK),true) + LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK +endif + +LOCAL_MODULE:= libcerttool_jni + +include $(BUILD_SHARED_LIBRARY) diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c new file mode 100644 index 000000000000..cc36b84e99a3 --- /dev/null +++ b/keystore/jni/cert.c @@ -0,0 +1,252 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "CertTool" + +#include <stdio.h> +#include <openssl/engine.h> +#include <openssl/pem.h> +#include <openssl/pkcs12.h> +#include <openssl/rsa.h> +#include <openssl/x509v3.h> +#include <cutils/log.h> + +#include "cert.h" + +static PKEY_STORE pkey_store[KEYGEN_STORE_SIZE]; +static int store_index = 0; + +static char emsg[][30] = { + "", + STR(ERR_INVALID_KEY_LENGTH), + STR(ERR_CONSTRUCT_NEW_DATA), + STR(ERR_RSA_KEYGEN), + STR(ERR_X509_PROCESS), + STR(ERR_BIO_READ), +}; + +static void save_in_store(X509_REQ *req, EVP_PKEY *pkey) +{ + EVP_PKEY *newpkey = EVP_PKEY_new(); + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_set1_RSA(newpkey, rsa); + PKEY_STORE_free(pkey_store[store_index]); + pkey_store[store_index].key_len = + i2d_X509_PUBKEY(req->req_info->pubkey, &pkey_store[store_index].public_key); + pkey_store[store_index++].pkey = newpkey; + store_index %= KEYGEN_STORE_SIZE; + RSA_free(rsa); +} + +static EVP_PKEY *get_pkey_from_store(X509 *cert) +{ + int i, key_len; + unsigned char *buf = NULL; + if ((key_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf)) == 0) { + return NULL; + } + for (i = 0 ; i < KEYGEN_STORE_SIZE ; ++i) { + if ((key_len == pkey_store[i].key_len) && + memcmp(buf, pkey_store[i].public_key, key_len) == 0) { + break; + } + } + free(buf); + return (i == KEYGEN_STORE_SIZE) ? NULL : pkey_store[i].pkey; +} + +int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]) +{ + int len, ret_code = 0; + BIGNUM *bn = NULL; + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + X509_REQ *req = NULL; + X509_NAME *name = NULL; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err; + + if ((bits != KEYLENGTH_MEDIUM) && (bits != KEYLENGTH_MAXIMUM)) { + ret_code = ERR_INVALID_KEY_LENGTH; + goto err; + } + + if (((pkey = EVP_PKEY_new()) == NULL) || + ((req = X509_REQ_new()) == NULL) || + ((rsa = RSA_new()) == NULL) || ((bn = BN_new()) == NULL)) { + ret_code = ERR_CONSTRUCT_NEW_DATA; + goto err; + } + + if (!BN_set_word(bn, RSA_F4) || + !RSA_generate_key_ex(rsa, bits, bn, NULL) || + !EVP_PKEY_assign_RSA(pkey, rsa)) { + ret_code = ERR_RSA_KEYGEN; + goto err; + } + + // rsa will be part of the req, it will be freed in X509_REQ_free(req) + rsa = NULL; + + X509_REQ_set_pubkey(req, pkey); + name = X509_REQ_get_subject_name(req); + + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, + (const unsigned char *)"US", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const unsigned char *) ANDROID_KEYSTORE, + -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + (const unsigned char *)organizations, -1, -1, 0); + + if (!X509_REQ_sign(req, pkey, EVP_md5()) || + (PEM_write_bio_X509_REQ(bio, req) <= 0)) { + ret_code = ERR_X509_PROCESS; + goto err; + } + if ((len = BIO_read(bio, reply, REPLY_MAX - 1)) > 0) { + reply[len] = 0; + save_in_store(req, pkey); + } else { + ret_code = ERR_BIO_READ; + } + +err: + if (rsa) RSA_free(rsa); + if (bn) BN_free(bn); + if (req) X509_REQ_free(req); + if (pkey) EVP_PKEY_free(pkey); + if (bio) BIO_free(bio); + if ((ret_code > 0) && (ret_code < ERR_MAXIMUM)) LOGE(emsg[ret_code]); + return ret_code; +} + +int is_pkcs12(const char *buf, int bufLen) +{ + int ret = 0; + BIO *bp = NULL; + PKCS12 *p12 = NULL; + + if (!buf || bufLen < 1) goto err; + + bp = BIO_new(BIO_s_mem()); + if (!bp) goto err; + + if (buf[0] != 48) goto err; // it is not DER. + + if (!BIO_write(bp, buf, bufLen)) goto err; + + if ((p12 = d2i_PKCS12_bio(bp, NULL)) != NULL) { + PKCS12_free(p12); + ret = 1; + } +err: + if (bp) BIO_free(bp); + return ret; +} + +X509* parse_cert(const char *buf, int bufLen) +{ + X509 *cert = NULL; + BIO *bp = NULL; + + if(!buf || bufLen < 1) + return NULL; + + bp = BIO_new(BIO_s_mem()); + if (!bp) goto err; + + if (!BIO_write(bp, buf, bufLen)) goto err; + + cert = PEM_read_bio_X509(bp, NULL, NULL, NULL); + if (!cert) { + BIO_free(bp); + if((bp = BIO_new(BIO_s_mem())) == NULL) goto err; + + if(!BIO_write(bp, (char *) buf, bufLen)) goto err; + cert = d2i_X509_bio(bp, NULL); + } + +err: + if (bp) BIO_free(bp); + return cert; +} + +static int get_distinct_name(X509_NAME *dname, char *buf, int size) +{ + int i, len; + char *p, *name; + + if (X509_NAME_oneline(dname, buf, size) == NULL) { + return -1; + } + name = strstr(buf, "/CN="); + p = name = name ? (name + 4) : buf; + while (*p != 0) { + if (*p == ' ') *p = '_'; + if (*p == '/') { + *p = 0; + break; + } + ++p; + } + return 0; +} + +int get_cert_name(X509 *cert, char *buf, int size) +{ + if (!cert) return -1; + return get_distinct_name(X509_get_subject_name(cert), buf, size); +} + +int get_issuer_name(X509 *cert, char *buf, int size) +{ + if (!cert) return -1; + return get_distinct_name(X509_get_issuer_name(cert), buf, size); +} + +int is_ca_cert(X509 *cert) +{ + int ret = 0; + BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *) + X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); + if (bs != NULL) ret = bs->ca; + if (bs) BASIC_CONSTRAINTS_free(bs); + return ret; +} + +int get_private_key_pem(X509 *cert, char *buf, int size) +{ + int len = 0; + BIO *bio = NULL; + EVP_PKEY *pkey = get_pkey_from_store(cert); + + if (pkey == NULL) return -1; + + bio = BIO_new(BIO_s_mem()); + if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err; + if (!PEM_write_bio_PrivateKey(bio, pkey, NULL,NULL,0,NULL, NULL)) { + goto err; + } + if ((len = BIO_read(bio, buf, size - 1)) > 0) { + buf[len] = 0; + } +err: + if (bio) BIO_free(bio); + return (len == 0) ? -1 : 0; +} diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h new file mode 100644 index 000000000000..a9807b1b20f0 --- /dev/null +++ b/keystore/jni/cert.h @@ -0,0 +1,59 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __CERT_H__ +#define __CERT_H__ + +#define ANDROID_KEYSTORE "Android Keystore" +#define KEYGEN_STORE_SIZE 5 +#define KEYLENGTH_MEDIUM 1024 +#define KEYLENGTH_MAXIMUM 2048 +#define MAX_CERT_NAME_LEN 128 +#define MAX_PEM_LENGTH 4096 +#define REPLY_MAX MAX_PEM_LENGTH + + +#define STR(token) #token +#define ERR_INVALID_KEY_LENGTH 1 +#define ERR_CONSTRUCT_NEW_DATA 2 +#define ERR_RSA_KEYGEN 3 +#define ERR_X509_PROCESS 4 +#define ERR_BIO_READ 5 +#define ERR_MAXIMUM 6 + +typedef struct { + EVP_PKEY *pkey; + unsigned char *public_key; + int key_len; +} PKEY_STORE; + +#define PKEY_STORE_free(x) { \ + if(x.pkey) EVP_PKEY_free(x.pkey); \ + if(x.public_key) free(x.public_key); \ +} + +#define nelem(x) (sizeof (x) / sizeof *(x)) + +int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]); +int is_pkcs12(const char *buf, int bufLen); +X509* parse_cert(const char *buf, int bufLen); +int get_cert_name(X509 *cert, char *buf, int size); +int get_issuer_name(X509 *cert, char *buf, int size); +int is_ca_cert(X509 *cert); +int get_private_key_pem(X509 *cert, char *buf, int size); + +#endif diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c new file mode 100644 index 000000000000..fabf5cdf3fb7 --- /dev/null +++ b/keystore/jni/certtool.c @@ -0,0 +1,176 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#define LOG_TAG "CertTool" + +#include <string.h> +#include <jni.h> +#include <cutils/log.h> +#include <openssl/x509v3.h> + +#include "cert.h" + +jstring +android_security_CertTool_generateCertificateRequest(JNIEnv* env, + jobject thiz, + jint bits, + jstring subject) + +{ + char csr[REPLY_MAX]; + if (gen_csr(bits, subject, csr) == 0) { + return (*env)->NewStringUTF(env, csr); + } + return NULL; +} + +jboolean +android_security_CertTool_isPkcs12Keystore(JNIEnv* env, + jobject thiz, + jbyteArray data) +{ + char buf[REPLY_MAX]; + int len = (*env)->GetArrayLength(env, data); + + if (len > REPLY_MAX) return 0; + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf); + return (jboolean) is_pkcs12(buf, len); +} + +jint +android_security_CertTool_generateX509Certificate(JNIEnv* env, + jobject thiz, + jbyteArray data) +{ + char buf[REPLY_MAX]; + int len = (*env)->GetArrayLength(env, data); + + if (len > REPLY_MAX) return 0; + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf); + return (jint) parse_cert(buf, len); +} + +jboolean android_security_CertTool_isCaCertificate(JNIEnv* env, + jobject thiz, + jint handle) +{ + return (handle == 0) ? (jboolean)0 : (jboolean) is_ca_cert((X509*)handle); +} + +jstring android_security_CertTool_getIssuerDN(JNIEnv* env, + jobject thiz, + jint handle) +{ + char issuer[MAX_CERT_NAME_LEN]; + + if (handle == 0) return NULL; + if (get_issuer_name((X509*)handle, issuer, MAX_CERT_NAME_LEN)) return NULL; + return (*env)->NewStringUTF(env, issuer); +} + +jstring android_security_CertTool_getCertificateDN(JNIEnv* env, + jobject thiz, + jint handle) +{ + char name[MAX_CERT_NAME_LEN]; + if (handle == 0) return NULL; + if (get_cert_name((X509*)handle, name, MAX_CERT_NAME_LEN)) return NULL; + return (*env)->NewStringUTF(env, name); +} + +jstring android_security_CertTool_getPrivateKeyPEM(JNIEnv* env, + jobject thiz, + jint handle) +{ + char pem[MAX_PEM_LENGTH]; + if (handle == 0) return NULL; + if (get_private_key_pem((X509*)handle, pem, MAX_PEM_LENGTH)) return NULL; + return (*env)->NewStringUTF(env, pem); +} + +void android_security_CertTool_freeX509Certificate(JNIEnv* env, + jobject thiz, + jint handle) +{ + if (handle != 0) X509_free((X509*)handle); +} + +/* + * Table of methods associated with the CertTool class. + */ +static JNINativeMethod gCertToolMethods[] = { + /* name, signature, funcPtr */ + {"generateCertificateRequest", "(ILjava/lang/String;)Ljava/lang/String;", + (void*)android_security_CertTool_generateCertificateRequest}, + {"isPkcs12Keystore", "([B)Z", + (void*)android_security_CertTool_isPkcs12Keystore}, + {"generateX509Certificate", "([B)I", + (void*)android_security_CertTool_generateX509Certificate}, + {"isCaCertificate", "(I)Z", + (void*)android_security_CertTool_isCaCertificate}, + {"getIssuerDN", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_getIssuerDN}, + {"getCertificateDN", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_getCertificateDN}, + {"getPrivateKeyPEM", "(I)Ljava/lang/String;", + (void*)android_security_CertTool_getPrivateKeyPEM}, + {"freeX509Certificate", "(I)V", + (void*)android_security_CertTool_freeX509Certificate}, +}; + +/* + * Register several native methods for one class. + */ +static int registerNatives(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = (*env)->FindClass(env, className); + if (clazz == NULL) { + LOGE("Can not find class %s\n", className); + return JNI_FALSE; + } + + if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { + LOGE("Can not RegisterNatives\n"); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { + goto bail; + } + + if (!registerNatives(env, "android/security/CertTool", + gCertToolMethods, nelem(gCertToolMethods))) { + goto bail; + } + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + +bail: + return result; +} diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp index b6d5078c44f5..16a4f2d4c143 100644 --- a/libs/audioflinger/A2dpAudioInterface.cpp +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -71,8 +71,8 @@ AudioStreamOut* A2dpAudioInterface::openOutputStream( } AudioStreamIn* A2dpAudioInterface::openInputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status, - AudioSystem::audio_in_acoustics acoustics) + int inputSource, int format, int channelCount, uint32_t sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) { if (status) *status = -1; diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h index 7901a8cca72f..091e775a1f7f 100644 --- a/libs/audioflinger/A2dpAudioInterface.h +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -55,6 +55,7 @@ public: status_t *status=0); virtual AudioStreamIn* openInputStream( + int inputSource, int format, int channelCount, uint32_t sampleRate, diff --git a/libs/audioflinger/AudioBufferProvider.h b/libs/audioflinger/AudioBufferProvider.h index 1a467c74429d..81c5c3959930 100644 --- a/libs/audioflinger/AudioBufferProvider.h +++ b/libs/audioflinger/AudioBufferProvider.h @@ -36,6 +36,8 @@ public: }; size_t frameCount; }; + + virtual ~AudioBufferProvider() {} virtual status_t getNextBuffer(Buffer* buffer) = 0; virtual void releaseBuffer(Buffer* buffer) = 0; diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index 9a941021e651..b72c94e0ac8b 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -78,9 +78,9 @@ public: virtual status_t setParameter(const char* key, const char* value) {return mFinalInterface->setParameter(key, value);} - virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status, - AudioSystem::audio_in_acoustics acoustics) - {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status, acoustics);} + virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount, + uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) + { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); } virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index b56221fcae4b..8a19fbd559e1 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -499,7 +499,8 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) } #ifdef WITH_A2DP - LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid()); + LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), + IPCThreadState::self()->getCallingPid()); if (mode == AudioSystem::MODE_NORMAL && (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { AutoMutex lock(&mLock); @@ -817,19 +818,22 @@ void AudioFlinger::handleForcedSpeakerRoute(int command) { AutoMutex lock(mHardwareLock); if (mForcedSpeakerCount++ == 0) { - mRouteRestoreTime = 0; - mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); - if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); - usleep(mHardwareMixerThread->latency()*1000); - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareStatus = AUDIO_HW_IDLE; - // delay track start so that audio hardware has time to siwtch routes - usleep(kStartSleepTime); + if (mForcedRoute == 0) { + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime); + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + } } mForcedRoute = AudioSystem::ROUTE_SPEAKER; + mRouteRestoreTime = 0; } LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); } @@ -890,7 +894,7 @@ void AudioFlinger::handleRouteDisablesA2dp_l(int routes) } LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount); } else { - LOGE("mA2dpDisableCount is already zero"); + LOGV("mA2dpDisableCount is already zero"); } } } @@ -1277,7 +1281,7 @@ sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack_l( status_t lStatus; // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { + if (sampleRate > mSampleRate*2) { LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); lStatus = BAD_VALUE; goto Exit; @@ -1553,7 +1557,6 @@ size_t AudioFlinger::MixerThread::getOutputFrameCount() AudioFlinger::MixerThread::TrackBase::TrackBase( const sp<MixerThread>& mixerThread, const sp<Client>& client, - int streamType, uint32_t sampleRate, int format, int channelCount, @@ -1563,7 +1566,6 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( : RefBase(), mMixerThread(mixerThread), mClient(client), - mStreamType(streamType), mFrameCount(0), mState(IDLE), mClientTid(-1), @@ -1594,8 +1596,8 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( new(mCblk) audio_track_cblk_t(); // clear all buffers mCblk->frameCount = frameCount; - mCblk->sampleRate = (uint16_t)sampleRate; - mCblk->channels = (uint16_t)channelCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = (uint8_t)channelCount; if (sharedBuffer == 0) { mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); @@ -1618,8 +1620,8 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( new(mCblk) audio_track_cblk_t(); // clear all buffers mCblk->frameCount = frameCount; - mCblk->sampleRate = (uint16_t)sampleRate; - mCblk->channels = (uint16_t)channelCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = (uint8_t)channelCount; mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); // Force underrun condition to avoid false underrun callback until first data is @@ -1680,7 +1682,7 @@ int AudioFlinger::MixerThread::TrackBase::sampleRate() const { } int AudioFlinger::MixerThread::TrackBase::channelCount() const { - return mCblk->channels; + return (int)mCblk->channels; } void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { @@ -1713,12 +1715,13 @@ AudioFlinger::MixerThread::Track::Track( int channelCount, int frameCount, const sp<IMemory>& sharedBuffer) - : TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) + : TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) { mVolume[0] = 1.0f; mVolume[1] = 1.0f; mMute = false; mSharedBuffer = sharedBuffer; + mStreamType = streamType; } AudioFlinger::MixerThread::Track::~Track() @@ -1902,15 +1905,15 @@ void AudioFlinger::MixerThread::Track::setVolume(float left, float right) AudioFlinger::MixerThread::RecordTrack::RecordTrack( const sp<MixerThread>& mixerThread, const sp<Client>& client, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, int frameCount, uint32_t flags) - : TrackBase(mixerThread, client, streamType, sampleRate, format, + : TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, flags, 0), - mOverflow(false) + mOverflow(false), mInputSource(inputSource) { } @@ -2235,7 +2238,7 @@ status_t AudioFlinger::TrackHandle::onTransact( sp<IAudioRecord> AudioFlinger::openRecord( pid_t pid, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -2258,18 +2261,12 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) { + if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) { LOGE("invalid stream type"); lStatus = BAD_VALUE; goto Exit; } - if (sampleRate > MAX_SAMPLE_RATE) { - LOGE("Sample rate out of range"); - lStatus = BAD_VALUE; - goto Exit; - } - if (mAudioRecordThread == 0) { LOGE("Audio record thread not started"); lStatus = NO_INIT; @@ -2301,7 +2298,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; // create new record track. The record track uses one track in mHardwareMixerThread by convention. - recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, + recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate, format, channelCount, frameCount, flags); } if (recordTrack->getCblk() == NULL) { @@ -2407,7 +2404,9 @@ bool AudioFlinger::AudioRecordThread::threadLoop() LOGV("AudioRecordThread: loop starting"); if (mRecordTrack != 0) { - input = mAudioHardware->openInputStream(mRecordTrack->format(), + input = mAudioHardware->openInputStream( + mRecordTrack->inputSource(), + mRecordTrack->format(), mRecordTrack->channelCount(), mRecordTrack->sampleRate(), &mStartStatus, diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index c7ca9ecc1012..8e47b29be68a 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -139,7 +139,7 @@ public: // record interface virtual sp<IAudioRecord> openRecord( pid_t pid, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -232,7 +232,6 @@ private: TrackBase(const sp<MixerThread>& mixerThread, const sp<Client>& client, - int streamType, uint32_t sampleRate, int format, int channelCount, @@ -260,10 +259,6 @@ private: return mCblk; } - int type() const { - return mStreamType; - } - int format() const { return mFormat; } @@ -293,7 +288,6 @@ private: sp<Client> mClient; sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; - int mStreamType; void* mBuffer; void* mBufferEnd; uint32_t mFrameCount; @@ -328,6 +322,11 @@ private: void mute(bool); void setVolume(float left, float right); + int type() const { + return mStreamType; + } + + protected: friend class MixerThread; friend class AudioFlinger; @@ -364,6 +363,7 @@ private: int8_t mRetryCount; sp<IMemory> mSharedBuffer; bool mResetDone; + int mStreamType; }; // end of Track // record track @@ -371,7 +371,7 @@ private: public: RecordTrack(const sp<MixerThread>& mixerThread, const sp<Client>& client, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -385,6 +385,8 @@ private: bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + int inputSource() const { return mInputSource; } + private: friend class AudioFlinger; friend class AudioFlinger::RecordHandle; @@ -397,6 +399,7 @@ private: virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); bool mOverflow; + int mInputSource; }; // playback track diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp index 62beadabddcb..1e159b814fe6 100644 --- a/libs/audioflinger/AudioHardwareGeneric.cpp +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -30,6 +30,7 @@ #include <utils/String8.h> #include "AudioHardwareGeneric.h" +#include <media/AudioRecord.h> namespace android { @@ -93,9 +94,15 @@ void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { } AudioStreamIn* AudioHardwareGeneric::openInputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status, - AudioSystem::audio_in_acoustics acoustics) + int inputSource, int format, int channelCount, uint32_t sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) { + // check for valid input source + if ((inputSource < AudioRecord::DEFAULT_INPUT) || + (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + return 0; + } + AutoMutex lock(mLock); // only one input stream allowed diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h index c949aa12a49f..c89df87f3cdd 100644 --- a/libs/audioflinger/AudioHardwareGeneric.h +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -112,6 +112,7 @@ public: status_t *status=0); virtual AudioStreamIn* openInputStream( + int inputSource, int format, int channelCount, uint32_t sampleRate, diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp index b13cb1c072a8..0ab4c60e5361 100644 --- a/libs/audioflinger/AudioHardwareStub.cpp +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -23,6 +23,7 @@ #include <utils/String8.h> #include "AudioHardwareStub.h" +#include <media/AudioRecord.h> namespace android { @@ -56,9 +57,15 @@ AudioStreamOut* AudioHardwareStub::openOutputStream( } AudioStreamIn* AudioHardwareStub::openInputStream( - int format, int channelCount, uint32_t sampleRate, + int inputSource, int format, int channelCount, uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) { + // check for valid input source + if ((inputSource < AudioRecord::DEFAULT_INPUT) || + (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + return 0; + } + AudioStreamInStub* in = new AudioStreamInStub(); status_t lStatus = in->set(format, channelCount, sampleRate, acoustics); if (status) { diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h index d40642465b8a..bf63cc5e2e47 100644 --- a/libs/audioflinger/AudioHardwareStub.h +++ b/libs/audioflinger/AudioHardwareStub.h @@ -78,6 +78,7 @@ public: status_t *status=0); virtual AudioStreamIn* openInputStream( + int inputSource, int format, int channelCount, uint32_t sampleRate, diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 22124361ab3f..9272983636db 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -6,7 +6,6 @@ LOCAL_SRC_FILES:= \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ GPUHardware/GPUHardware.cpp \ - BootAnimation.cpp \ BlurFilter.cpp.arm \ CPUGauge.cpp \ Layer.cpp \ diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index f14d7e99c2d3..eec645edc907 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -189,9 +189,14 @@ void DisplayHardware::init(uint32_t dpy) char property[PROPERTY_VALUE_MAX]; - if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { - LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); - strcpy(property, "160"); + /* Read density from build-specific ro.sf.lcd_density property + * except if it is overriden by qemu.sf.lcd_density. + */ + if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) { + if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { + LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); + strcpy(property, "160"); + } } mDensity = atoi(property) * (1.0f/160.0f); diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp index e84435088a7c..397ddc82e12a 100644 --- a/libs/surfaceflinger/LayerBitmap.cpp +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -114,7 +114,9 @@ status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, } if (mBitsMemory==0 || mSurface.data==0) { - LOGE("not enough memory for layer bitmap size=%u", size); + LOGE("not enough memory for layer bitmap " + "size=%u (w=%d, h=%d, stride=%d, format=%d)", + size, int(w), int(h), int(stride), int(format)); allocator->dump("LayerBitmap"); mSurface.data = 0; mSize = -1U; diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 167a59b18977..ef4a8ea17a2e 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -61,6 +61,13 @@ #include "GPUHardware/GPUHardware.h" +/* ideally AID_GRAPHICS would be in a semi-public header + * or there would be a way to map a user/group name to its id + */ +#ifndef AID_GRAPHICS +#define AID_GRAPHICS 1003 +#endif + #define DISPLAY_COUNT 1 namespace android { @@ -184,7 +191,6 @@ SurfaceFlinger::SurfaceFlinger() mDebugCpu(0), mDebugFps(0), mDebugBackground(0), - mDebugNoBootAnimation(0), mSyncObject(), mDeplayedTransactionPending(0), mConsoleSignals(0), @@ -207,14 +213,11 @@ void SurfaceFlinger::init() mDebugBackground = atoi(value); property_get("debug.sf.showfps", value, "0"); mDebugFps = atoi(value); - property_get("debug.sf.nobootanimation", value, "0"); - mDebugNoBootAnimation = atoi(value); LOGI_IF(mDebugRegion, "showupdates enabled"); LOGI_IF(mDebugCpu, "showcpu enabled"); LOGI_IF(mDebugBackground, "showbackground enabled"); LOGI_IF(mDebugFps, "showfps enabled"); - LOGI_IF(mDebugNoBootAnimation, "boot animation disabled"); } SurfaceFlinger::~SurfaceFlinger() @@ -324,11 +327,8 @@ void SurfaceFlinger::bootFinished() { const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; - LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); - if (mBootAnimation != 0) { - mBootAnimation->requestExit(); - mBootAnimation.clear(); - } + LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + property_set("ctl.stop", "bootanim"); } void SurfaceFlinger::onFirstRef() @@ -456,10 +456,10 @@ status_t SurfaceFlinger::readyToRun() if (mDebugCpu) mCpuGauge = new CPUGauge(this, ms2ns(500)); - // the boot animation! - if (mDebugNoBootAnimation == false) - mBootAnimation = new BootAnimation(this); - + + // start boot animation + property_set("ctl.start", "bootanim"); + return NO_ERROR; } @@ -771,10 +771,11 @@ void SurfaceFlinger::computeVisibleRegions( dirty.orSelf(layer->visibleRegionScreen); layer->contentDirty = false; } else { - // compute the exposed region - // dirty = what's visible now - what's wasn't covered before - // = what's visible now & what's was covered before - dirty = visibleRegion.intersect(layer->coveredRegionScreen); + /* compute the exposed region: + * exposed = what's VISIBLE and NOT COVERED now + * but was COVERED before + */ + dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen; } dirty.subtractSelf(aboveOpaqueLayers); @@ -783,7 +784,7 @@ void SurfaceFlinger::computeVisibleRegions( // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer aboveOpaqueLayers.orSelf(opaqueRegion); - aboveCoveredLayers.orSelf(bounds); + aboveCoveredLayers.orSelf(visibleRegion); // Store the visible region is screen space layer->setVisibleRegion(visibleRegion); @@ -1543,13 +1544,13 @@ status_t SurfaceFlinger::onTransact( // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid)) { + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { // we're called from a different process, do the real check if (!checkCallingPermission( String16("android.permission.ACCESS_SURFACE_FLINGER"))) { - const int uid = ipc->getCallingUid(); LOGE("Permission Denial: " "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index e02318259108..15913f2318f7 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -36,7 +36,6 @@ #include <private/ui/SurfaceFlingerSynchro.h> #include "Barrier.h" -#include "BootAnimation.h" #include "CPUGauge.h" #include "Layer.h" #include "Tokenizer.h" @@ -347,7 +346,6 @@ private: sp<SurfaceHeapManager> mSurfaceHeapManager; sp<GPUHardwareInterface> mGPU; GLuint mWormholeTexName; - sp<BootAnimation> mBootAnimation; nsecs_t mBootTime; // Can only accessed from the main thread, these members @@ -374,7 +372,6 @@ private: int mDebugCpu; int mDebugFps; int mDebugBackground; - int mDebugNoBootAnimation; // these are thread safe mutable Barrier mReadyToRunBarrier; diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp index 238c602e8684..5f633bda5a32 100644 --- a/libs/surfaceflinger/VRamHeap.cpp +++ b/libs/surfaceflinger/VRamHeap.cpp @@ -35,6 +35,8 @@ #include <utils/MemoryHeapPmem.h> #include <utils/MemoryHeapBase.h> +#include <EGL/eglnatives.h> + #include "GPUHardware/GPUHardware.h" #include "SurfaceFlinger.h" #include "VRamHeap.h" diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index f9443579af00..7bbe38bcbc4b 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -20,13 +20,11 @@ LOCAL_SRC_FILES:= \ LayerState.cpp \ Overlay.cpp \ PixelFormat.cpp \ - Point.cpp \ Rect.cpp \ 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 661370040e44..975594f39e11 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; } @@ -164,21 +149,21 @@ status_t Camera::unlock() status_t Camera::setPreviewDisplay(const sp<Surface>& surface) { LOGV("setPreviewDisplay"); - if (surface == 0) { - LOGE("app passed NULL surface"); - return NO_INIT; - } sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - return c->setPreviewDisplay(surface->getISurface()); + if (surface != 0) { + return c->setPreviewDisplay(surface->getISurface()); + } else { + LOGD("app passed NULL surface"); + return c->setPreviewDisplay(0); + } } status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) { LOGV("setPreviewDisplay"); if (surface == 0) { - LOGE("app passed NULL surface"); - return NO_INIT; + LOGD("app passed NULL surface"); } sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; @@ -186,7 +171,7 @@ status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) } -// start preview mode, must call setPreviewDisplay first +// start preview mode status_t Camera::startPreview() { LOGV("startPreview"); @@ -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, CAMERA_ERROR_SERVER_DIED, 0); } void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp index dd6a798b9899..dab5f718475d 100644 --- a/libs/ui/ISurfaceFlingerClient.cpp +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -35,6 +35,13 @@ // --------------------------------------------------------------------------- +/* ideally AID_GRAPHICS would be in a semi-public header + * or there would be a way to map a user/group name to its id + */ +#ifndef AID_GRAPHICS +#define AID_GRAPHICS 1003 +#endif + #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -136,13 +143,13 @@ status_t BnSurfaceFlingerClient::onTransact( IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); - const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid)) { + const int uid = ipc->getCallingUid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { // we're called from a different process, do the real check if (!checkCallingPermission( String16("android.permission.ACCESS_SURFACE_FLINGER"))) { - const int uid = ipc->getCallingUid(); LOGE("Permission Denial: " "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index b236edc290f5..59c65146e5d3 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -129,12 +129,8 @@ OverlayRef::OverlayRef(overlay_handle_t handle, const sp<IOverlay>& channel, OverlayRef::~OverlayRef() { if (mOwnHandle) { - /* FIXME: handles should be promoted to "real" API and be handled by - * the framework */ - for (int i=0 ; i<mOverlayHandle->numFds ; i++) { - close(mOverlayHandle->data[i]); - } - free((void*)mOverlayHandle); + native_handle_close(mOverlayHandle); + native_handle_delete(const_cast<native_handle*>(mOverlayHandle)); } } @@ -147,7 +143,7 @@ sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) { uint32_t f = data.readInt32(); uint32_t ws = data.readInt32(); uint32_t hs = data.readInt32(); - native_handle* handle = data.readNativeHandle(NULL, NULL); + native_handle* handle = data.readNativeHandle(); result = new OverlayRef(); result->mOverlayHandle = handle; @@ -169,7 +165,7 @@ status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) { reply->writeInt32(o->mFormat); reply->writeInt32(o->mWidthStride); reply->writeInt32(o->mHeightStride); - reply->writeNativeHandle(*(o->mOverlayHandle)); + reply->writeNativeHandle(o->mOverlayHandle); } else { reply->writeStrongBinder(NULL); } diff --git a/libs/ui/Point.cpp b/libs/ui/Point.cpp deleted file mode 100644 index 438d49fa4f68..000000000000 --- a/libs/ui/Point.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Point.cpp - * Android - * - * Created on 11/16/2006. - * Copyright 2005 The Android Open Source Project - * - */ - -#include <ui/Point.h> - diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index 99e68bb1712a..66b95762d050 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -1,21 +1,28 @@ /* - * Rect.cpp - * Android + * Copyright (C) 2009 The Android Open Source Project * - * Created on 10/14/05. - * Copyright 2005 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #include <ui/Rect.h> namespace android { -inline int min(int a, int b) { +static inline int min(int a, int b) { return (a<b) ? a : b; } -inline int max(int a, int b) { +static inline int max(int a, int b) { return (a>b) ? a : b; } @@ -64,14 +71,16 @@ Rect& Rect::offsetBy(int x, int y) return *this; } -Rect Rect::operator + (const Point& rhs) const +const Rect Rect::operator + (const Point& rhs) const { - return Rect(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); + const Rect result(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); + return result; } -Rect Rect::operator - (const Point& rhs) const +const Rect Rect::operator - (const Point& rhs) const { - return Rect(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); + const Rect result(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); + return result; } bool Rect::intersect(const Rect& with, Rect* result) const diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index f9fb780388bc..9bdd64adeeb0 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -117,7 +117,8 @@ LOCAL_SRC_FILES:= \ IPermissionController.cpp \ IServiceManager.cpp \ Unicode.cpp \ - file_backup_helper.cpp + BackupData.cpp \ + BackupHelpers.cpp ifeq ($(TARGET_SIMULATOR),true) LOCAL_SRC_FILES += $(hostSources) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 91203ddb45b6..23cb72d4efca 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -582,11 +582,14 @@ const void* _FileAsset::ensureAlignment(FileMap* map) if ((((size_t)data)&0x3) == 0) { // We can return this directly if it is aligned on a word // boundary. + LOGV("Returning aligned FileAsset %p (%s).", this, + getAssetSource()); return data; } // If not aligned on a word boundary, then we need to copy it into // our own buffer. - LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, + getAssetSource(), (int)mLength); unsigned char* buf = new unsigned char[mLength]; if (buf == NULL) { LOGE("alloc of %ld bytes failed\n", (long) mLength); diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 447b801937be..5a05e6a61f17 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -395,21 +395,41 @@ const ResTable* AssetManager::getResTable(bool required) const const size_t N = mAssetPaths.size(); for (size_t i=0; i<N; i++) { Asset* ass = NULL; + ResTable* sharedRes = NULL; bool shared = true; const asset_path& ap = mAssetPaths.itemAt(i); LOGV("Looking for resource asset in '%s'\n", ap.path.string()); if (ap.type != kFileTypeDirectory) { - ass = const_cast<AssetManager*>(this)-> - mZipSet.getZipResourceTable(ap.path); - if (ass == NULL) { - LOGV("loading resource table %s\n", ap.path.string()); + if (i == 0) { + // The first item is typically the framework resources, + // which we want to avoid parsing every time. + sharedRes = const_cast<AssetManager*>(this)-> + mZipSet.getZipResourceTable(ap.path); + } + if (sharedRes == NULL) { ass = const_cast<AssetManager*>(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - if (ass != NULL && ass != kExcludedAsset) { + mZipSet.getZipResourceTableAsset(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); ass = const_cast<AssetManager*>(this)-> - mZipSet.setZipResourceTable(ap.path, ass); + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast<AssetManager*>(this)-> + mZipSet.setZipResourceTableAsset(ap.path, ass); + } + } + + if (i == 0 && ass != NULL) { + // If this is the first resource table in the asset + // manager, then we are going to cache it so that we + // can quickly copy it out for others. + LOGV("Creating shared resources for %s", ap.path.string()); + sharedRes = new ResTable(); + sharedRes->add(ass, (void*)(i+1), false); + sharedRes = const_cast<AssetManager*>(this)-> + mZipSet.setZipResourceTable(ap.path, sharedRes); } } } else { @@ -420,13 +440,19 @@ const ResTable* AssetManager::getResTable(bool required) const ap); shared = false; } - if (ass != NULL && ass != kExcludedAsset) { + if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { if (rt == NULL) { mResources = rt = new ResTable(); updateResourceParamsLocked(); } LOGV("Installing resource asset %p in to table %p\n", ass, mResources); - rt->add(ass, (void*)(i+1), !shared); + if (sharedRes != NULL) { + LOGV("Copying existing resources for %s", ap.path.string()); + rt->add(sharedRes); + } else { + LOGV("Parsing resources for %s", ap.path.string()); + rt->add(ass, (void*)(i+1), !shared); + } if (!shared) { delete ass; @@ -901,6 +927,60 @@ AssetDir* AssetManager::openDir(const char* dirName) } /* + * Open a directory in the non-asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + pMergedInfo = new SortedVector<AssetDir::FileInfo>; + + const size_t which = ((size_t)cookie)-1; + + if (which < mAssetPaths.size()) { + const asset_path& ap = mAssetPaths.itemAt(which); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + +/* * Scan the contents of the specified directory and merge them into the * "pMergedInfo" vector, removing previous entries if we find "exclude" * directives. @@ -1143,6 +1223,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg LOGE("ARGH: name too long?\n"); continue; } + //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); if (dirNameLen == 0 || (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && nameBuf[dirNameLen] == '/')) @@ -1165,7 +1246,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg createZipSourceNameLocked(zipName, dirName, info.getFileName())); contents.add(info); - //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + //printf("FOUND: file '%s'\n", info.getFileName().string()); } else { /* this is a subdir; add it if we don't already have it*/ String8 subdirName(cp, nextSlash - cp); @@ -1181,7 +1262,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg dirs.add(subdirName); } - //printf("FOUND: dir '%s'\n", (const char*) subdirName); + //printf("FOUND: dir '%s'\n", subdirName.string()); } } } @@ -1455,7 +1536,8 @@ Mutex AssetManager::SharedZip::gLock; DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen; AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), + mResourceTableAsset(NULL), mResourceTable(NULL) { //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); mZipFile = new ZipFileRO; @@ -1508,6 +1590,25 @@ Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) return mResourceTableAsset; } +ResTable* AssetManager::SharedZip::getResourceTable() +{ + LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); + return mResourceTable; +} + +ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res) +{ + { + AutoMutex _l(gLock); + if (mResourceTable == NULL) { + mResourceTable = res; + return res; + } + } + delete res; + return mResourceTable; +} + bool AssetManager::SharedZip::isUpToDate() { time_t modWhen = getFileModDate(mPath.string()); @@ -1517,6 +1618,9 @@ bool AssetManager::SharedZip::isUpToDate() AssetManager::SharedZip::~SharedZip() { //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTable != NULL) { + delete mResourceTable; + } if (mResourceTableAsset != NULL) { delete mResourceTableAsset; } @@ -1572,7 +1676,7 @@ ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) return zip->getZip(); } -Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) { int idx = getIndex(path); sp<SharedZip> zip = mZipFile[idx]; @@ -1583,7 +1687,7 @@ Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) return zip->getResourceTableAsset(); } -Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, +Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path, Asset* asset) { int idx = getIndex(path); @@ -1592,6 +1696,26 @@ Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, return zip->setResourceTableAsset(asset); } +ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp<SharedZip> zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTable(); +} + +ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path, + ResTable* res) +{ + int idx = getIndex(path); + sp<SharedZip> zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTable(res); +} + /* * Generate the partial pathname for the specified archive. The caller * gets to prepend the asset root directory. diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp new file mode 100644 index 000000000000..cce754a08213 --- /dev/null +++ b/libs/utils/BackupData.cpp @@ -0,0 +1,349 @@ +/* + * 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 "backup_data" + +#include <utils/BackupHelpers.h> +#include <utils/ByteOrder.h> + +#include <stdio.h> +#include <unistd.h> + +#include <cutils/log.h> + +namespace android { + +/* + * File Format (v1): + * + * All ints are stored little-endian. + * + * - An app_header_v1 struct. + * - The name of the package, utf-8, null terminated, padded to 4-byte boundary. + * - A sequence of zero or more key/value paires (entities), each with + * - A entity_header_v1 struct + * - The key, utf-8, null terminated, padded to 4-byte boundary. + * - The value, padded to 4 byte boundary + */ + +const static int ROUND_UP[4] = { 0, 3, 2, 1 }; + +static inline size_t +round_up(size_t n) +{ + return n + ROUND_UP[n % 4]; +} + +static inline size_t +padding_extra(size_t n) +{ + return ROUND_UP[n % 4]; +} + +BackupDataWriter::BackupDataWriter(int fd) + :m_fd(fd), + m_status(NO_ERROR), + m_pos(0), + m_entityCount(0) +{ +} + +BackupDataWriter::~BackupDataWriter() +{ +} + +// Pad out anything they've previously written to the next 4 byte boundary. +status_t +BackupDataWriter::write_padding_for(int n) +{ + ssize_t amt; + ssize_t paddingSize; + + paddingSize = padding_extra(n); + if (paddingSize > 0) { + uint32_t padding = 0xbcbcbcbc; + amt = write(m_fd, &padding, paddingSize); + if (amt != paddingSize) { + m_status = errno; + return m_status; + } + m_pos += amt; + } + return NO_ERROR; +} + +status_t +BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t amt; + + amt = write_padding_for(m_pos); + if (amt != 0) { + return amt; + } + + String8 k; + if (m_keyPrefix.length() > 0) { + k = m_keyPrefix; + k += ":"; + k += key; + } else { + k = key; + } + if (true) { + LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), + dataSize); + } + + entity_header_v1 header; + ssize_t keyLen; + + keyLen = k.length(); + + header.type = tolel(BACKUP_HEADER_ENTITY_V1); + header.keyLen = tolel(keyLen); + header.dataSize = tolel(dataSize); + + amt = write(m_fd, &header, sizeof(entity_header_v1)); + if (amt != sizeof(entity_header_v1)) { + m_status = errno; + return m_status; + } + m_pos += amt; + + amt = write(m_fd, k.string(), keyLen+1); + if (amt != keyLen+1) { + m_status = errno; + return m_status; + } + m_pos += amt; + + amt = write_padding_for(keyLen+1); + + m_entityCount++; + + return amt; +} + +status_t +BackupDataWriter::WriteEntityData(const void* data, size_t size) +{ + if (m_status != NO_ERROR) { + return m_status; + } + + // We don't write padding here, because they're allowed to call this several + // times with smaller buffers. We write it at the end of WriteEntityHeader + // instead. + ssize_t amt = write(m_fd, data, size); + if (amt != (ssize_t)size) { + m_status = errno; + return m_status; + } + m_pos += amt; + return NO_ERROR; +} + +void +BackupDataWriter::SetKeyPrefix(const String8& keyPrefix) +{ + m_keyPrefix = keyPrefix; +} + + +BackupDataReader::BackupDataReader(int fd) + :m_fd(fd), + m_done(false), + m_status(NO_ERROR), + m_pos(0), + m_entityCount(0) +{ + memset(&m_header, 0, sizeof(m_header)); +} + +BackupDataReader::~BackupDataReader() +{ +} + +status_t +BackupDataReader::Status() +{ + return m_status; +} + +#define CHECK_SIZE(actual, expected) \ + do { \ + if ((actual) != (expected)) { \ + if ((actual) == 0) { \ + m_status = EIO; \ + } else { \ + m_status = errno; \ + } \ + return m_status; \ + } \ + } while(0) +#define SKIP_PADDING() \ + do { \ + status_t err = skip_padding(); \ + if (err != NO_ERROR) { \ + m_status = err; \ + return err; \ + } \ + } while(0) + +status_t +BackupDataReader::ReadNextHeader(bool* done, int* type) +{ + *done = m_done; + if (m_status != NO_ERROR) { + return m_status; + } + + int amt; + + // No error checking here, in case we're at the end of the stream. Just let read() fail. + skip_padding(); + amt = read(m_fd, &m_header, sizeof(m_header)); + *done = m_done = (amt == 0); + CHECK_SIZE(amt, sizeof(m_header)); + m_pos += sizeof(m_header); + if (type) { + *type = m_header.type; + } + + // validate and fix up the fields. + m_header.type = fromlel(m_header.type); + switch (m_header.type) + { + case BACKUP_HEADER_ENTITY_V1: + { + m_header.entity.keyLen = fromlel(m_header.entity.keyLen); + if (m_header.entity.keyLen <= 0) { + LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, + (int)m_header.entity.keyLen); + m_status = EINVAL; + } + m_header.entity.dataSize = fromlel(m_header.entity.dataSize); + m_entityCount++; + + // read the rest of the header (filename) + size_t size = m_header.entity.keyLen; + char* buf = m_key.lockBuffer(size); + if (buf == NULL) { + m_status = ENOMEM; + return m_status; + } + int amt = read(m_fd, buf, size+1); + CHECK_SIZE(amt, (int)size+1); + m_key.unlockBuffer(size); + m_pos += size+1; + SKIP_PADDING(); + m_dataEndPos = m_pos + m_header.entity.dataSize; + + break; + } + default: + LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); + m_status = EINVAL; + } + + return m_status; +} + +bool +BackupDataReader::HasEntities() +{ + return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1; +} + +status_t +BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != BACKUP_HEADER_ENTITY_V1) { + return EINVAL; + } + *key = m_key; + *dataSize = m_header.entity.dataSize; + return NO_ERROR; +} + +status_t +BackupDataReader::SkipEntityData() +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != BACKUP_HEADER_ENTITY_V1) { + return EINVAL; + } + if (m_header.entity.dataSize > 0) { + int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); + return pos == -1 ? (int)errno : (int)NO_ERROR; + } else { + return NO_ERROR; + } +} + +ssize_t +BackupDataReader::ReadEntityData(void* data, size_t size) +{ + if (m_status != NO_ERROR) { + return -1; + } + int remaining = m_dataEndPos - m_pos; + //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", + // size, m_pos, m_dataEndPos, remaining); + if (remaining <= 0) { + return 0; + } + if (((int)size) > remaining) { + size = remaining; + } + //LOGD(" reading %d bytes", size); + int amt = read(m_fd, data, size); + if (amt < 0) { + m_status = errno; + return -1; + } + m_pos += amt; + return amt; +} + +status_t +BackupDataReader::skip_padding() +{ + ssize_t amt; + ssize_t paddingSize; + + paddingSize = padding_extra(m_pos); + if (paddingSize > 0) { + uint32_t padding; + amt = read(m_fd, &padding, paddingSize); + CHECK_SIZE(amt, paddingSize); + m_pos += amt; + } + return NO_ERROR; +} + + +} // namespace android diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp new file mode 100644 index 000000000000..4ad9b5179532 --- /dev/null +++ b/libs/utils/BackupHelpers.cpp @@ -0,0 +1,1314 @@ +/* + * 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 "file_backup_helper" + +#include <utils/BackupHelpers.h> + +#include <utils/KeyedVector.h> +#include <utils/ByteOrder.h> +#include <utils/String8.h> + +#include <errno.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/time.h> // for utimes +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <fcntl.h> +#include <zlib.h> + +#include <cutils/log.h> + +namespace android { + +#define MAGIC0 0x70616e53 // Snap +#define MAGIC1 0x656c6946 // File + +/* + * 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 }; + +static inline int +round_up(int n) +{ + return n + ROUND_UP[n % 4]; +} + +static int +read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot) +{ + int bytesRead = 0; + int amt; + SnapshotHeader header; + + amt = read(fd, &header, sizeof(header)); + if (amt != sizeof(header)) { + return errno; + } + bytesRead += amt; + + if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { + LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); + return 1; + } + + for (int i=0; i<header.fileCount; i++) { + FileState file; + char filenameBuf[128]; + + amt = read(fd, &file, sizeof(FileState)); + if (amt != sizeof(FileState)) { + LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); + return 1; + } + bytesRead += amt; + + // filename is not NULL terminated, but it is padded + int nameBufSize = round_up(file.nameLen); + char* filename = nameBufSize <= (int)sizeof(filenameBuf) + ? filenameBuf + : (char*)malloc(nameBufSize); + amt = read(fd, filename, nameBufSize); + if (amt == nameBufSize) { + snapshot->add(String8(filename, file.nameLen), file); + } + bytesRead += amt; + if (filename != filenameBuf) { + free(filename); + } + if (amt != nameBufSize) { + LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); + return 1; + } + } + + if (header.totalSize != bytesRead) { + LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", + header.totalSize, bytesRead); + return 1; + } + + return 0; +} + +static int +write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot) +{ + int fileCount = 0; + int bytesWritten = sizeof(SnapshotHeader); + // preflight size + const int N = snapshot.size(); + for (int i=0; i<N; i++) { + const FileRec& g = snapshot.valueAt(i); + if (!g.deleted) { + const String8& name = snapshot.keyAt(i); + bytesWritten += sizeof(FileState) + round_up(name.length()); + fileCount++; + } + } + + LOGP("write_snapshot_file fd=%d\n", fd); + + int amt; + SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten }; + + amt = write(fd, &header, sizeof(header)); + if (amt != sizeof(header)) { + LOGW("write_snapshot_file error writing header %s", strerror(errno)); + return errno; + } + + for (int i=0; i<N; i++) { + FileRec r = snapshot.valueAt(i); + if (!r.deleted) { + const String8& name = snapshot.keyAt(i); + int nameLen = r.s.nameLen = name.length(); + + amt = write(fd, &r.s, sizeof(FileState)); + if (amt != sizeof(FileState)) { + LOGW("write_snapshot_file error writing header %s", strerror(errno)); + return 1; + } + + // filename is not NULL terminated, but it is padded + amt = write(fd, name.string(), nameLen); + if (amt != nameLen) { + LOGW("write_snapshot_file error writing filename %s", strerror(errno)); + return 1; + } + int paddingLen = ROUND_UP[nameLen % 4]; + if (paddingLen != 0) { + int padding = 0xabababab; + amt = write(fd, &padding, paddingLen); + if (amt != paddingLen) { + LOGW("write_snapshot_file error writing %d bytes of filename padding %s", + paddingLen, strerror(errno)); + return 1; + } + } + } + } + + return 0; +} + +static int +write_delete_file(BackupDataWriter* dataStream, const String8& key) +{ + LOGP("write_delete_file %s\n", key.string()); + return dataStream->WriteEntityHeader(key, -1); +} + +static int +write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key, + char const* realFilename) +{ + 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); + + + 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) { + amt += bytesLeft; // Plus a negative is minus. Don't write more than we promised. + } + err = dataStream->WriteEntityData(buf, amt); + if (err != 0) { + free(buf); + return err; + } + } + if (bytesLeft != 0) { + if (bytesLeft > 0) { + // Pad out the space we promised in the buffer. We can't corrupt the buffer, + // even though the data we're sending is probably bad. + memset(buf, 0, bufsize); + while (bytesLeft > 0) { + amt = bytesLeft < bufsize ? bytesLeft : bufsize; + bytesLeft -= amt; + err = dataStream->WriteEntityData(buf, amt); + if (err != 0) { + free(buf); + return err; + } + } + } + LOGE("write_update_file size mismatch for %s. expected=%d actual=%d." + " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); + } + + free(buf); + return NO_ERROR; +} + +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, st.st_mode, key, realFilename); + close(fd); + return err; +} + +static int +compute_crc32(int fd) +{ + const int bufsize = 4*1024; + int amt; + + char* buf = (char*)malloc(bufsize); + int crc = crc32(0L, Z_NULL, 0); + + lseek(fd, 0, SEEK_SET); + + while ((amt = read(fd, buf, bufsize)) != 0) { + crc = crc32(crc, (Bytef*)buf, amt); + } + + free(buf); + return crc; +} + +int +back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, + char const* const* files, char const* const* keys, int fileCount) +{ + int err; + KeyedVector<String8,FileState> oldSnapshot; + KeyedVector<String8,FileRec> newSnapshot; + + if (oldSnapshotFD != -1) { + err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); + if (err != 0) { + // On an error, treat this as a full backup. + oldSnapshot.clear(); + } + } + + for (int i=0; i<fileCount; i++) { + String8 key(keys[i]); + FileRec r; + char const* file = files[i]; + r.file = file; + struct stat st; + + err = stat(file, &st); + if (err != 0) { + 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. + + if (newSnapshot.indexOfKey(key) >= 0) { + LOGP("back_up_files key already in use '%s'", key.string()); + return -1; + } + } + newSnapshot.add(key, r); + } + + int n = 0; + int N = oldSnapshot.size(); + int m = 0; + + while (n<N && m<fileCount) { + const String8& p = oldSnapshot.keyAt(n); + const String8& q = newSnapshot.keyAt(m); + FileRec& g = newSnapshot.editValueAt(m); + int cmp = p.compare(q); + if (g.deleted || cmp < 0) { + // file removed + LOGP("file removed: %s", p.string()); + g.deleted = true; // They didn't mention the file, but we noticed that it's gone. + dataStream->WriteEntityHeader(p, -1); + n++; + } + else if (cmp > 0) { + // file added + LOGP("file added: %s", g.file.string()); + write_update_file(dataStream, q, g.file.string()); + m++; + } + else { + // both files exist, check them + const FileState& f = oldSnapshot.valueAt(n); + + int fd = open(g.file.string(), O_RDONLY); + if (fd < 0) { + // We can't open the file. Don't report it as a delete either. Let the + // server keep the old version. Maybe they'll be able to deal with it + // on restore. + LOGP("Unable to open file %s - skipping", g.file.string()); + } else { + g.s.crc32 = compute_crc32(fd); + + LOGP("%s", q.string()); + 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.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); + } + n++; + m++; + } + } + + // these were deleted + while (n<N) { + dataStream->WriteEntityHeader(oldSnapshot.keyAt(n), -1); + n++; + } + + // these were added + while (m<fileCount) { + const String8& q = newSnapshot.keyAt(m); + FileRec& g = newSnapshot.editValueAt(m); + write_update_file(dataStream, q, g.file.string()); + m++; + } + + err = write_snapshot_file(newSnapshotFD, newSnapshot); + + return 0; +} + +#define RESTORE_BUF_SIZE (8*1024) + +RestoreHelperBase::RestoreHelperBase() +{ + m_buf = malloc(RESTORE_BUF_SIZE); + m_loggedUnknownMetadata = false; +} + +RestoreHelperBase::~RestoreHelperBase() +{ + free(m_buf); +} + +status_t +RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) +{ + ssize_t err; + size_t dataSize; + String8 key; + int fd; + void* buf = m_buf; + ssize_t amt; + int mode; + int crc; + struct stat st; + FileRec r; + + err = in->ReadEntityHeader(&key, &dataSize); + if (err != NO_ERROR) { + return err; + } + + // 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); + fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); + if (fd == -1) { + LOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); + return errno; + } + + while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) { + err = write(fd, buf, amt); + if (err != amt) { + close(fd); + LOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); + return errno; + } + crc = crc32(crc, (Bytef*)buf, amt); + } + + close(fd); + + // Record for the snapshot + err = stat(filename.string(), &st); + if (err != 0) { + LOGW("Error stating file that we just created %s", filename.string()); + return errno; + } + + r.file = filename; + 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; + r.s.crc32 = crc; + + m_files.add(key, r); + + return NO_ERROR; +} + +status_t +RestoreHelperBase::WriteSnapshot(int fd) +{ + return write_snapshot_file(fd, m_files);; +} + +#if TEST_BACKUP_HELPERS + +#define SCRATCH_DIR "/data/backup_helper_test/" + +static int +write_text_file(const char* path, const char* data) +{ + int amt; + int fd; + int len; + + fd = creat(path, 0666); + if (fd == -1) { + fprintf(stderr, "creat %s failed\n", path); + return errno; + } + + len = strlen(data); + amt = write(fd, data, len); + if (amt != len) { + fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path); + return errno; + } + + close(fd); + + return 0; +} + +static int +compare_file(const char* path, const unsigned char* data, int len) +{ + int fd; + int amt; + + fd = open(path, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path); + return errno; + } + + unsigned char* contents = (unsigned char*)malloc(len); + if (contents == NULL) { + fprintf(stderr, "malloc(%d) failed\n", len); + return ENOMEM; + } + + bool sizesMatch = true; + amt = lseek(fd, 0, SEEK_END); + if (amt != len) { + fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt); + sizesMatch = false; + } + lseek(fd, 0, SEEK_SET); + + int readLen = amt < len ? amt : len; + amt = read(fd, contents, readLen); + if (amt != readLen) { + fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt); + } + + bool contentsMatch = true; + for (int i=0; i<readLen; i++) { + if (data[i] != contents[i]) { + if (contentsMatch) { + fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n"); + contentsMatch = false; + } + fprintf(stderr, " [%-2d] %02x %02x\n", i, data[i], contents[i]); + } + } + + free(contents); + return contentsMatch && sizesMatch ? 0 : 1; +} + +int +backup_helper_test_empty() +{ + int err; + int fd; + KeyedVector<String8,FileRec> snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating %s\n", filename); + return 1; + } + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 + }; + + err = compare_file(filename, correct_data, sizeof(correct_data)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + KeyedVector<String8,FileState> readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 0) { + fprintf(stderr, "readSnapshot should be length 0\n"); + return 1; + } + + return 0; +} + +int +backup_helper_test_four() +{ + int err; + int fd; + KeyedVector<String8,FileRec> snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error opening %s\n", filename); + return 1; + } + + String8 filenames[4]; + FileState states[4]; + FileRec r; + r.deleted = false; + + 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; + r.s = states[0]; + filenames[0] = String8("bytes_of_padding"); + snapshot.add(filenames[0], r); + + 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; + r.s = states[1]; + filenames[1] = String8("bytes_of_padding3"); + snapshot.add(filenames[1], r); + + 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; + r.s = states[2]; + filenames[2] = String8("bytes_of_padding_2"); + snapshot.add(filenames[2], r); + + 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; + r.s = states[3]; + filenames[3] = String8("bytes_of_padding__1"); + snapshot.add(filenames[3], r); + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + // header + 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00, + + // bytes_of_padding + 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, + 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, + 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, + 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, + 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)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + + KeyedVector<String8,FileState> readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 4) { + fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); + return 1; + } + + bool matched = true; + for (size_t i=0; i<readSnapshot.size(); i++) { + const String8& name = readSnapshot.keyAt(i); + 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].mode != state.mode + || states[i].size != state.size || states[i].crc32 != states[i].crc32) { + 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; + } + } + + return matched ? 0 : 1; +} + +// hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data +const unsigned char DATA_GOLDEN_FILE[] = { + 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, + 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, + 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, + 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, + 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, + 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00 + +}; +const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); + +static int +test_write_header_and_entity(BackupDataWriter& writer, const char* str) +{ + int err; + String8 text(str); + + err = writer.WriteEntityHeader(text, text.length()+1); + if (err != 0) { + fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err)); + return err; + } + + err = writer.WriteEntityData(text.string(), text.length()+1); + if (err != 0) { + fprintf(stderr, "write failed for data '%s'\n", text.string()); + return errno; + } + + return err; +} + +int +backup_helper_test_data_writer() +{ + int err; + int fd; + const char* filename = SCRATCH_DIR "data_writer.data"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + BackupDataWriter writer(fd); + + err = 0; + err |= test_write_header_and_entity(writer, "no_padding_"); + err |= test_write_header_and_entity(writer, "padded_to__3"); + err |= test_write_header_and_entity(writer, "padded_to_2__"); + err |= test_write_header_and_entity(writer, "padded_to1"); + + close(fd); + + err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); + if (err != 0) { + return err; + } + + return err; +} + +int +test_read_header_and_entity(BackupDataReader& reader, const char* str) +{ + int err; + int bufSize = strlen(str)+1; + char* buf = (char*)malloc(bufSize); + String8 string; + int cookie = 0x11111111; + size_t actualSize; + bool done; + int type; + ssize_t nRead; + + // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); + + err = reader.ReadNextHeader(&done, &type); + if (done) { + fprintf(stderr, "should not be done yet\n"); + goto finished; + } + if (err != 0) { + fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); + goto finished; + } + if (type != BACKUP_HEADER_ENTITY_V1) { + err = EINVAL; + fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1); + } + + err = reader.ReadEntityHeader(&string, &actualSize); + if (err != 0) { + fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err)); + goto finished; + } + if (string != str) { + fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string()); + err = EINVAL; + goto finished; + } + if ((int)actualSize != bufSize) { + fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize, + actualSize); + err = EINVAL; + goto finished; + } + + nRead = reader.ReadEntityData(buf, bufSize); + if (nRead < 0) { + err = reader.Status(); + fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); + goto finished; + } + + if (0 != memcmp(buf, str, bufSize)) { + fprintf(stderr, "ReadEntityData expected '%s' but got something starting with " + "%02x %02x %02x %02x '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3], + buf[0], buf[1], buf[2], buf[3]); + err = EINVAL; + goto finished; + } + + // The next read will confirm whether it got the right amount of data. + +finished: + if (err != NO_ERROR) { + fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); + } + free(buf); + return err; +} + +int +backup_helper_test_data_reader() +{ + int err; + int fd; + const char* filename = SCRATCH_DIR "data_reader.data"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); + if (err != DATA_GOLDEN_FILE_SIZE) { + fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename); + return errno; + } + + close(fd); + + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno), + filename); + return errno; + } + + { + BackupDataReader reader(fd); + + err = 0; + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "no_padding_"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to__3"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to_2__"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to1"); + } + } + + close(fd); + + return err; +} + +static int +get_mod_time(const char* filename, struct timeval times[2]) +{ + int err; + struct stat64 st; + err = stat64(filename, &st); + if (err != 0) { + fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno)); + return errno; + } + times[0].tv_sec = st.st_atime; + times[1].tv_sec = st.st_mtime; + + // If st_atime is a macro then struct stat64 uses struct timespec + // to store the access and modif time values and typically + // st_*time_nsec is not defined. In glibc, this is controlled by + // __USE_MISC. +#ifdef __USE_MISC +#if !defined(st_atime) || defined(st_atime_nsec) +#error "Check if this __USE_MISC conditional is still needed." +#endif + times[0].tv_usec = st.st_atim.tv_nsec / 1000; + times[1].tv_usec = st.st_mtim.tv_nsec / 1000; +#else + times[0].tv_usec = st.st_atime_nsec / 1000; + times[1].tv_usec = st.st_mtime_nsec / 1000; +#endif + + return 0; +} + +int +backup_helper_test_files() +{ + int err; + int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); + write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); + write_text_file(SCRATCH_DIR "data/d", "d\ndd\n"); + write_text_file(SCRATCH_DIR "data/e", "e\nee\n"); + write_text_file(SCRATCH_DIR "data/f", "f\nff\n"); + write_text_file(SCRATCH_DIR "data/h", "h\nhh\n"); + + char const* files_before[] = { + SCRATCH_DIR "data/b", + SCRATCH_DIR "data/c", + SCRATCH_DIR "data/d", + SCRATCH_DIR "data/e", + SCRATCH_DIR "data/f" + }; + + char const* keys_before[] = { + "data/b", + "data/c", + "data/d", + "data/e", + "data/f" + }; + + dataStreamFD = creat(SCRATCH_DIR "1.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5); + if (err != 0) { + return err; + } + } + + close(dataStreamFD); + close(newSnapshotFD); + + sleep(3); + + struct timeval d_times[2]; + struct timeval e_times[2]; + + err = get_mod_time(SCRATCH_DIR "data/d", d_times); + err |= get_mod_time(SCRATCH_DIR "data/e", e_times); + if (err != 0) { + return err; + } + + write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); + unlink(SCRATCH_DIR "data/c"); + write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); + write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n"); + utimes(SCRATCH_DIR "data/d", d_times); + write_text_file(SCRATCH_DIR "data/e", "z\nzz\n"); + utimes(SCRATCH_DIR "data/e", e_times); + write_text_file(SCRATCH_DIR "data/g", "g\ngg\n"); + unlink(SCRATCH_DIR "data/f"); + + char const* files_after[] = { + SCRATCH_DIR "data/a", // added + SCRATCH_DIR "data/b", // same + SCRATCH_DIR "data/c", // different mod time + SCRATCH_DIR "data/d", // different size (same mod time) + SCRATCH_DIR "data/e", // different contents (same mod time, same size) + SCRATCH_DIR "data/g" // added + }; + + char const* keys_after[] = { + "data/a", // added + "data/b", // same + "data/c", // different mod time + "data/d", // different size (same mod time) + "data/e", // different contents (same mod time, same size) + "data/g" // added + }; + + oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY); + if (oldSnapshotFD == -1) { + fprintf(stderr, "error opening: %s\n", strerror(errno)); + return errno; + } + + dataStreamFD = creat(SCRATCH_DIR "2.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6); + if (err != 0) { + return err; + } +} + + close(oldSnapshotFD); + close(dataStreamFD); + close(newSnapshotFD); + + return 0; +} + +int +backup_helper_test_null_base() +{ + int err; + int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); + + char const* files[] = { + SCRATCH_DIR "data/a", + }; + + char const* keys[] = { + "a", + }; + + dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); + if (err != 0) { + return err; + } + } + + close(dataStreamFD); + close(newSnapshotFD); + + return 0; +} + +int +backup_helper_test_missing_file() +{ + int err; + int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); + + char const* files[] = { + SCRATCH_DIR "data/a", + SCRATCH_DIR "data/b", + SCRATCH_DIR "data/c", + }; + + char const* keys[] = { + "a", + "b", + "c", + }; + + dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); + if (err != 0) { + return err; + } + } + + close(dataStreamFD); + close(newSnapshotFD); + + return 0; +} + + +#endif // TEST_BACKUP_HELPERS + +} diff --git a/libs/utils/characterData.h b/libs/utils/CharacterData.h index e931d995e50d..e931d995e50d 100644 --- a/libs/utils/characterData.h +++ b/libs/utils/CharacterData.h diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 0f4b6473008d..b0e37509783e 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -409,12 +409,16 @@ status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) mObjects[idx++] = off; mObjectsSize++; - const flat_binder_object* flat + flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off); acquire_object(proc, *flat, this); - // take note if the object is a file descriptor if (flat->type == BINDER_TYPE_FD) { + // If this is a file descriptor, we need to dup it so the + // new Parcel now owns its own fd, and can declare that we + // officially know we have fds. + flat->handle = dup(flat->handle); + flat->cookie = (void*)1; mHasFds = mFdsKnown = true; } } @@ -650,28 +654,26 @@ status_t Parcel::writeWeakBinder(const wp<IBinder>& val) return flatten_binder(ProcessState::self(), val, this); } -status_t Parcel::writeNativeHandle(const native_handle& handle) +status_t Parcel::writeNativeHandle(const native_handle* handle) { - if (handle.version != sizeof(native_handle)) + if (handle->version != sizeof(native_handle)) return BAD_TYPE; status_t err; - err = writeInt32(handle.numFds); + err = writeInt32(handle->numFds); if (err != NO_ERROR) return err; - err = writeInt32(handle.numInts); + err = writeInt32(handle->numInts); if (err != NO_ERROR) return err; - for (int i=0 ; err==NO_ERROR && i<handle.numFds ; i++) - err = writeDupFileDescriptor(handle.data[i]); + for (int i=0 ; err==NO_ERROR && i<handle->numFds ; i++) + err = writeDupFileDescriptor(handle->data[i]); if (err != NO_ERROR) { LOGD("write native handle, write dup fd failed"); return err; } - - err = write(handle.data + handle.numFds, sizeof(int)*handle.numInts); - + err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts); return err; } @@ -928,7 +930,7 @@ wp<IBinder> Parcel::readWeakBinder() const } -native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +native_handle* Parcel::readNativeHandle() const { int numFds, numInts; status_t err; @@ -937,31 +939,15 @@ native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int) err = readInt32(&numInts); if (err != NO_ERROR) return 0; - native_handle* h; - if (alloc == 0) { - size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); - h = (native_handle*)malloc(size); - h->version = sizeof(native_handle); - h->numFds = numFds; - h->numInts = numInts; - } else { - h = alloc(cookie, numFds, numInts); - if (h->version != sizeof(native_handle)) { - return 0; - } - } - + native_handle* h = native_handle_create(numFds, numInts); for (int i=0 ; err==NO_ERROR && i<numFds ; i++) { h->data[i] = dup(readFileDescriptor()); if (h->data[i] < 0) err = BAD_VALUE; } - err = read(h->data + numFds, sizeof(int)*numInts); - if (err != NO_ERROR) { - if (alloc == 0) { - free(h); - } + native_handle_close(h); + native_handle_delete(h); h = 0; } return h; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3d12dca7a32c..109f28d305ee 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -544,7 +544,7 @@ ResXMLParser::event_code_t ResXMLParser::next() return mEventCode; } -const int32_t ResXMLParser::getCommentID() const +int32_t ResXMLParser::getCommentID() const { return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; } @@ -555,12 +555,12 @@ const uint16_t* ResXMLParser::getComment(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const uint32_t ResXMLParser::getLineNumber() const +uint32_t ResXMLParser::getLineNumber() const { return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; } -const int32_t ResXMLParser::getTextID() const +int32_t ResXMLParser::getTextID() const { if (mEventCode == TEXT) { return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); @@ -583,7 +583,7 @@ ssize_t ResXMLParser::getTextValue(Res_value* outValue) const return BAD_TYPE; } -const int32_t ResXMLParser::getNamespacePrefixID() const +int32_t ResXMLParser::getNamespacePrefixID() const { if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); @@ -598,7 +598,7 @@ const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getNamespaceUriID() const +int32_t ResXMLParser::getNamespaceUriID() const { if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); @@ -613,7 +613,7 @@ const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getElementNamespaceID() const +int32_t ResXMLParser::getElementNamespaceID() const { if (mEventCode == START_TAG) { return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); @@ -630,7 +630,7 @@ const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getElementNameID() const +int32_t ResXMLParser::getElementNameID() const { if (mEventCode == START_TAG) { return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); @@ -655,7 +655,7 @@ size_t ResXMLParser::getAttributeCount() const return 0; } -const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; @@ -678,7 +678,7 @@ const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +int32_t ResXMLParser::getAttributeNameID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; @@ -701,7 +701,7 @@ const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const { int32_t id = getAttributeNameID(idx); if (id >= 0 && (size_t)id < mTree.mNumResIds) { @@ -710,7 +710,7 @@ const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const return 0; } -const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; @@ -1136,8 +1136,9 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const struct ResTable::Header { - Header() : ownedData(NULL), header(NULL) { } + Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { } + ResTable* const owner; void* ownedData; const ResTable_header* header; size_t size; @@ -1163,8 +1164,8 @@ struct ResTable::Type struct ResTable::Package { - Package(const Header* _header, const ResTable_package* _package) - : header(_header), package(_package) { } + Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) + : owner(_owner), header(_header), package(_package) { } ~Package() { size_t i = types.size(); @@ -1174,10 +1175,14 @@ struct ResTable::Package } } + ResTable* const owner; const Header* const header; const ResTable_package* const package; Vector<Type*> types; + ResStringPool typeStrings; + ResStringPool keyStrings; + const Type* getType(size_t idx) const { return idx < types.size() ? types[idx] : NULL; } @@ -1188,13 +1193,16 @@ struct ResTable::Package // table that defined the package); the ones after are skins on top of it. struct ResTable::PackageGroup { - PackageGroup(const String16& _name, uint32_t _id) - : name(_name), id(_id), typeCount(0), bags(NULL) { } + PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id) + : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { } ~PackageGroup() { clearBagCache(); const size_t N = packages.size(); for (size_t i=0; i<N; i++) { - delete packages[i]; + Package* pkg = packages[i]; + if (pkg->owner == owner) { + delete pkg; + } } } @@ -1225,15 +1233,17 @@ struct ResTable::PackageGroup } } + ResTable* const owner; String16 const name; uint32_t const id; Vector<Package*> packages; + + // This is for finding typeStrings and other common package stuff. + Package* basePackage; - // Taken from the root package. - ResStringPool typeStrings; - ResStringPool keyStrings; + // For quick access. size_t typeCount; - + // Computed attribute bags, first indexed by the type and second // by the entry in that type. bag_set*** bags; @@ -1560,11 +1570,36 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData) return add(data, size, cookie, asset, copyData); } +status_t ResTable::add(ResTable* src) +{ + mError = src->mError; + mParams = src->mParams; + + for (size_t i=0; i<src->mHeaders.size(); i++) { + mHeaders.add(src->mHeaders[i]); + } + + for (size_t i=0; i<src->mPackageGroups.size(); i++) { + PackageGroup* srcPg = src->mPackageGroups[i]; + PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id); + for (size_t j=0; j<srcPg->packages.size(); j++) { + pg->packages.add(srcPg->packages[j]); + } + pg->basePackage = srcPg->basePackage; + pg->typeCount = srcPg->typeCount; + mPackageGroups.add(pg); + } + + memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); + + return mError; +} + status_t ResTable::add(const void* data, size_t size, void* cookie, Asset* asset, bool copyData) { if (!data) return NO_ERROR; - Header* header = new Header; + Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; mHeaders.add(header); @@ -1682,10 +1717,12 @@ void ResTable::uninit() N = mHeaders.size(); for (size_t i=0; i<N; i++) { Header* header = mHeaders[i]; - if (header->ownedData) { - free(header->ownedData); + if (header->owner == this) { + if (header->ownedData) { + free(header->ownedData); + } + delete header; } - delete header; } mPackageGroups.clear(); @@ -1728,8 +1765,8 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->package = grp->name.string(); outName->packageLen = grp->name.size(); - outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); - outName->name = grp->keyStrings.stringAt( + outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->basePackage->keyStrings.stringAt( dtohl(entry->key.index), &outName->nameLen); return true; } @@ -2331,13 +2368,13 @@ nope: continue; } - const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); if (ti < 0) { TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); continue; } - const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); if (ei < 0) { TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); continue; @@ -3630,25 +3667,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, PackageGroup* group = NULL; uint32_t id = dtohl(pkg->id); if (id != 0 && id < 256) { + + package = new Package(this, header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + size_t idx = mPackageMap[id]; if (idx == 0) { idx = mPackageGroups.size()+1; char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(String16(tmpName), id); + group = new PackageGroup(this, String16(tmpName), id); if (group == NULL) { + delete package; return (mError=NO_MEMORY); } - err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), header->dataEnd-(base+dtohl(pkg->typeStrings))); if (err != NO_ERROR) { + delete group; + delete package; return (mError=err); } - err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), header->dataEnd-(base+dtohl(pkg->keyStrings))); if (err != NO_ERROR) { + delete group; + delete package; return (mError=err); } @@ -3657,6 +3705,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (err < NO_ERROR) { return (mError=err); } + group->basePackage = package; + mPackageMap[id] = (uint8_t)idx; } else { group = mPackageGroups.itemAt(idx-1); @@ -3664,10 +3714,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=UNKNOWN_ERROR); } } - package = new Package(header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } err = group->packages.add(package); if (err < NO_ERROR) { return (mError=err); @@ -3830,9 +3876,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, #define CHAR16_ARRAY_EQ(constant, var, len) \ ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) -void ResTable::print() const +void print_complex(uint32_t complex, bool isFraction) +{ + const float MANTISSA_MULT = + 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT); + const float RADIX_MULTS[] = { + 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, + 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT + }; + + float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK + <<Res_value::COMPLEX_MANTISSA_SHIFT)) + * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT) + & Res_value::COMPLEX_RADIX_MASK]; + printf("%f", value); + + if (!isFraction) { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_PX: printf("px"); break; + case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break; + case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; + case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; + case Res_value::COMPLEX_UNIT_IN: printf("in"); break; + case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; + default: printf(" (unknown unit)"); break; + } + } else { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; + case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; + default: printf(" (unknown unit)"); break; + } + } +} + +void ResTable::print_value(const Package* pkg, const Res_value& value) const +{ + if (value.dataType == Res_value::TYPE_NULL) { + printf("(null)\n"); + } else if (value.dataType == Res_value::TYPE_REFERENCE) { + printf("(reference) 0x%08x\n", value.data); + } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) { + printf("(attribute) 0x%08x\n", value.data); + } else if (value.dataType == Res_value::TYPE_STRING) { + size_t len; + const char16_t* str = pkg->header->values.stringAt( + value.data, &len); + if (str == NULL) { + printf("(string) null\n"); + } else { + printf("(string) \"%s\"\n", + String8(str, len).string()); + } + } else if (value.dataType == Res_value::TYPE_FLOAT) { + printf("(float) %g\n", *(const float*)&value.data); + } else if (value.dataType == Res_value::TYPE_DIMENSION) { + printf("(dimension) "); + print_complex(value.data, false); + printf("\n"); + } else if (value.dataType == Res_value::TYPE_FRACTION) { + printf("(fraction) "); + print_complex(value.data, true); + printf("\n"); + } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT + || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) { + printf("(color) #%08x\n", value.data); + } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) { + printf("(boolean) %s\n", value.data ? "true" : "false"); + } else if (value.dataType >= Res_value::TYPE_FIRST_INT + || value.dataType <= Res_value::TYPE_LAST_INT) { + printf("(int) 0x%08x or %d\n", value.data, value.data); + } else { + printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n", + (int)value.dataType, (int)value.data, + (int)value.size, (int)value.res0); + } +} + +void ResTable::print(bool inclValues) const { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); + if (mError != 0) { + printf("mError=0x%x (%s)\n", mError, strerror(mError)); + } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], @@ -3883,7 +4008,7 @@ void ResTable::print() 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] : '-', @@ -3896,7 +4021,8 @@ void ResTable::print() 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) { @@ -3947,32 +4073,60 @@ void ResTable::print() const (void*)(entriesStart + thisOffset)); continue; } + + uint16_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)esize, (void*)typeSize); + continue; + } + + const Res_value* valuePtr = NULL; + const ResTable_map_entry* bagPtr = NULL; + Res_value value; if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { printf("<bag>"); + bagPtr = (const ResTable_map_entry*)ent; } else { - uint16_t esize = dtohs(ent->size); - if ((esize&0x3) != 0) { - printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); - continue; - } - if ((thisOffset+esize) > typeSize) { - printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n", - (void*)entriesStart, (void*)thisOffset, - (void*)esize, (void*)typeSize); - continue; - } - - const Res_value* value = (const Res_value*) + valuePtr = (const Res_value*) (((const uint8_t*)ent) + esize); + value.copyFrom_dtoh(*valuePtr); printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", - (int)value->dataType, (int)dtohl(value->data), - (int)dtohs(value->size), (int)value->res0); + (int)value.dataType, (int)value.data, + (int)value.size, (int)value.res0); } if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { printf(" (PUBLIC)"); } printf("\n"); + + if (inclValues) { + if (valuePtr != NULL) { + printf(" "); + print_value(pkg, value); + } else if (bagPtr != NULL) { + const int N = dtohl(bagPtr->count); + const ResTable_map* mapPtr = (const ResTable_map*) + (((const uint8_t*)ent) + esize); + printf(" Parent=0x%08x, Count=%d\n", + dtohl(bagPtr->parent.ident), N); + for (int i=0; i<N; i++) { + printf(" #%i (Key=0x%08x): ", + i, dtohl(mapPtr->name.ident)); + value.copyFrom_dtoh(mapPtr->value); + print_value(pkg, value); + const size_t size = dtohs(mapPtr->value.size); + mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr) + + size + sizeof(*mapPtr)-sizeof(mapPtr->value)); + } + } + } } } } diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index 33f535fd1a7c..f92703e7f0e8 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "utils/AndroidUnicode.h" -#include "characterData.h" +#include <utils/AndroidUnicode.h> +#include "CharacterData.h" #define LOG_TAG "Unicode" -#include "utils/Log.h" +#include <utils/Log.h> // ICU headers for using macros #include <unicode/utf16.h> diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp index fbc9e67843f1..96f9fc4d692f 100644 --- a/libs/utils/ZipEntry.cpp +++ b/libs/utils/ZipEntry.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "zip" -#include "utils/ZipEntry.h" -#include "utils/Log.h" +#include <utils/ZipEntry.h> +#include <utils/Log.h> #include <stdio.h> #include <string.h> diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp index 89aa874b4941..6f27d17f07f1 100644 --- a/libs/utils/ZipFile.cpp +++ b/libs/utils/ZipFile.cpp @@ -20,9 +20,9 @@ #define LOG_TAG "zip" -#include "utils/ZipFile.h" -#include "utils/ZipUtils.h" -#include "utils/Log.h" +#include <utils/ZipFile.h> +#include <utils/ZipUtils.h> +#include <utils/Log.h> #include <zlib.h> #define DEF_MEM_LEVEL 8 // normally in zutil.h? diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index d312dafada72..45f6c8baa707 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "utils/ZipFileCRO.h" -#include "utils/ZipFileRO.h" +#include <utils/ZipFileCRO.h> +#include <utils/ZipFileRO.h> using namespace android; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index ae8c719726d2..6c701dd0b673 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -19,9 +19,9 @@ // #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 -#include "utils/ZipFileRO.h" -#include "utils/Log.h" -#include "utils/misc.h" +#include <utils/ZipFileRO.h> +#include <utils/Log.h> +#include <utils/misc.h> #include <zlib.h> diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index bfbacfecd992..5df94cbbd9a0 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -20,9 +20,9 @@ #define LOG_TAG "ziputil" -#include "utils/ZipUtils.h" -#include "utils/ZipFileRO.h" -#include "utils/Log.h" +#include <utils/ZipUtils.h> +#include <utils/ZipFileRO.h> +#include <utils/Log.h> #include <stdlib.h> #include <string.h> diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp deleted file mode 100644 index 453084ace452..000000000000 --- a/libs/utils/file_backup_helper.cpp +++ /dev/null @@ -1,685 +0,0 @@ -#define LOG_TAG "file_backup_helper" - -#include <utils/backup_helpers.h> - -#include <utils/KeyedVector.h> -#include <utils/ByteOrder.h> -#include <utils/String8.h> - -#include <errno.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/stat.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <utime.h> -#include <fcntl.h> -#include <zlib.h> - -#include <cutils/log.h> - -using namespace android; - -#define MAGIC0 0x70616e53 // Snap -#define MAGIC1 0x656c6946 // File - -#define LOGP(x...) LOGD(x) -//#define LOGP(x...) printf(x) - -struct SnapshotHeader { - int magic0; - int fileCount; - int magic1; - int totalSize; -}; - -struct FileState { - int modTime_sec; - int modTime_nsec; - int size; - int crc32; - int nameLen; -}; - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline int -round_up(int n) -{ - return n + ROUND_UP[n % 4]; -} - -static int -read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot) -{ - int bytesRead = 0; - int amt; - SnapshotHeader header; - - amt = read(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - return errno; - } - bytesRead += amt; - - if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { - LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); - return 1; - } - - for (int i=0; i<header.fileCount; i++) { - FileState file; - char filenameBuf[128]; - - amt = read(fd, &file, sizeof(file)); - if (amt != sizeof(file)) { - LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - bytesRead += amt; - - // filename is not NULL terminated, but it is padded - int nameBufSize = round_up(file.nameLen); - char* filename = nameBufSize <= (int)sizeof(filenameBuf) - ? filenameBuf - : (char*)malloc(nameBufSize); - amt = read(fd, filename, nameBufSize); - if (amt == nameBufSize) { - snapshot->add(String8(filename, file.nameLen), file); - } - bytesRead += amt; - if (filename != filenameBuf) { - free(filename); - } - if (amt != nameBufSize) { - LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - } - - if (header.totalSize != bytesRead) { - LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", - header.totalSize, bytesRead); - return 1; - } - - return 0; -} - -static int -write_snapshot_file(int fd, const KeyedVector<String8,FileState>& snapshot) -{ - int bytesWritten = sizeof(SnapshotHeader); - // preflight size - const int N = snapshot.size(); - for (int i=0; i<N; i++) { - const String8& name = snapshot.keyAt(i); - bytesWritten += sizeof(FileState) + round_up(name.length()); - } - - int amt; - SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten }; - - amt = write(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - LOGW("write_snapshot_file error writing header %s", strerror(errno)); - return errno; - } - - for (int i=0; i<header.fileCount; i++) { - const String8& name = snapshot.keyAt(i); - FileState file = snapshot.valueAt(i); - int nameLen = file.nameLen = name.length(); - - amt = write(fd, &file, sizeof(file)); - if (amt != sizeof(file)) { - LOGW("write_snapshot_file error writing header %s", strerror(errno)); - return 1; - } - - // filename is not NULL terminated, but it is padded - amt = write(fd, name.string(), nameLen); - if (amt != nameLen) { - LOGW("write_snapshot_file error writing filename %s", strerror(errno)); - return 1; - } - int paddingLen = ROUND_UP[nameLen % 4]; - if (paddingLen != 0) { - int padding = 0xabababab; - amt = write(fd, &padding, paddingLen); - if (amt != paddingLen) { - LOGW("write_snapshot_file error writing %d bytes of filename padding %s", - paddingLen, strerror(errno)); - return 1; - } - } - } - - return 0; -} - -static int -write_delete_file(const String8& key) -{ - LOGP("write_delete_file %s\n", key.string()); - return 0; -} - -static int -write_update_file(const String8& realFilename, const String8& key) -{ - LOGP("write_update_file %s (%s)\n", realFilename.string(), key.string()); - return 0; -} - -static int -compute_crc32(const String8& filename) -{ - const int bufsize = 4*1024; - int amt; - - int fd = open(filename.string(), O_RDONLY); - if (fd == -1) { - return -1; - } - - char* buf = (char*)malloc(bufsize); - int crc = crc32(0L, Z_NULL, 0); - - while ((amt = read(fd, buf, bufsize)) != 0) { - crc = crc32(crc, (Bytef*)buf, amt); - } - - close(fd); - free(buf); - - return crc; -} - -int -back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, - char const* fileBase, char const* const* files, int fileCount) -{ - int err; - const String8 base(fileBase); - KeyedVector<String8,FileState> oldSnapshot; - KeyedVector<String8,FileState> newSnapshot; - - if (oldSnapshotFD != -1) { - err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); - if (err != 0) { - // On an error, treat this as a full backup. - oldSnapshot.clear(); - } - } - - for (int i=0; i<fileCount; i++) { - String8 name(files[i]); - FileState s; - struct stat st; - String8 realFilename(base); - realFilename.appendPath(name); - - err = stat(realFilename.string(), &st); - if (err != 0) { - LOGW("Error stating file %s", realFilename.string()); - continue; - } - - s.modTime_sec = st.st_mtime; - s.modTime_nsec = 0; // workaround sim breakage - //s.modTime_nsec = st.st_mtime_nsec; - s.size = st.st_size; - s.crc32 = compute_crc32(realFilename); - - newSnapshot.add(name, s); - } - - int n = 0; - int N = oldSnapshot.size(); - int m = 0; - - while (n<N && m<fileCount) { - const String8& p = oldSnapshot.keyAt(n); - const String8& q = newSnapshot.keyAt(m); - int cmp = p.compare(q); - if (cmp > 0) { - // file added - String8 realFilename(base); - realFilename.appendPath(q); - write_update_file(realFilename, q); - m++; - } - else if (cmp < 0) { - // file removed - write_delete_file(p); - n++; - } - else { - // both files exist, check them - String8 realFilename(base); - realFilename.appendPath(q); - const FileState& f = oldSnapshot.valueAt(n); - const FileState& g = newSnapshot.valueAt(m); - - LOGP("%s\n", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", - f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", - g.modTime_sec, g.modTime_nsec, g.size, g.crc32); - if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec - || f.size != g.size || f.crc32 != g.crc32) { - write_update_file(realFilename, p); - } - n++; - m++; - } - } - - // these were deleted - while (n<N) { - write_delete_file(oldSnapshot.keyAt(n)); - n++; - } - - // these were added - while (m<fileCount) { - const String8& q = newSnapshot.keyAt(m); - String8 realFilename(base); - realFilename.appendPath(q); - write_update_file(realFilename, q); - m++; - } - - err = write_snapshot_file(newSnapshotFD, newSnapshot); - - return 0; -} - -#if TEST_BACKUP_HELPERS - -#define SCRATCH_DIR "/data/backup_helper_test/" - -static int -write_text_file(const char* path, const char* data) -{ - int amt; - int fd; - int len; - - fd = creat(path, 0666); - if (fd == -1) { - fprintf(stderr, "creat %s failed\n", path); - return errno; - } - - len = strlen(data); - amt = write(fd, data, len); - if (amt != len) { - fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path); - return errno; - } - - close(fd); - - return 0; -} - -static int -compare_file(const char* path, const unsigned char* data, int len) -{ - int fd; - int amt; - - fd = open(path, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path); - return errno; - } - - unsigned char* contents = (unsigned char*)malloc(len); - if (contents == NULL) { - fprintf(stderr, "malloc(%d) failed\n", len); - return ENOMEM; - } - - bool sizesMatch = true; - amt = lseek(fd, 0, SEEK_END); - if (amt != len) { - fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt); - sizesMatch = false; - } - lseek(fd, 0, SEEK_SET); - - int readLen = amt < len ? amt : len; - amt = read(fd, contents, readLen); - if (amt != readLen) { - fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt); - } - - bool contentsMatch = true; - for (int i=0; i<readLen; i++) { - if (data[i] != contents[i]) { - if (contentsMatch) { - fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n"); - contentsMatch = false; - } - fprintf(stderr, " [%-2d] %02x %02x\n", i, data[i], contents[i]); - } - } - - return contentsMatch && sizesMatch ? 0 : 1; -} - -int -backup_helper_test_empty() -{ - int err; - int fd; - KeyedVector<String8,FileState> snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating %s\n", filename); - return 1; - } - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - KeyedVector<String8,FileState> readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 0) { - fprintf(stderr, "readSnapshot should be length 0\n"); - return 1; - } - - return 0; -} - -int -backup_helper_test_four() -{ - int err; - int fd; - KeyedVector<String8,FileState> snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error opening %s\n", filename); - return 1; - } - - String8 filenames[4]; - FileState states[4]; - - states[0].modTime_sec = 0xfedcba98; - states[0].modTime_nsec = 0xdeadbeef; - states[0].size = 0xababbcbc; - states[0].crc32 = 0x12345678; - states[0].nameLen = -12; - filenames[0] = String8("bytes_of_padding"); - snapshot.add(filenames[0], states[0]); - - states[1].modTime_sec = 0x93400031; - states[1].modTime_nsec = 0xdeadbeef; - states[1].size = 0x88557766; - states[1].crc32 = 0x22334422; - states[1].nameLen = -1; - filenames[1] = String8("bytes_of_padding3"); - snapshot.add(filenames[1], states[1]); - - states[2].modTime_sec = 0x33221144; - states[2].modTime_nsec = 0xdeadbeef; - states[2].size = 0x11223344; - states[2].crc32 = 0x01122334; - states[2].nameLen = 0; - filenames[2] = String8("bytes_of_padding_2"); - snapshot.add(filenames[2], states[2]); - - states[3].modTime_sec = 0x33221144; - states[3].modTime_nsec = 0xdeadbeef; - states[3].size = 0x11223344; - states[3].crc32 = 0x01122334; - states[3].nameLen = 0; - filenames[3] = String8("bytes_of_padding__1"); - snapshot.add(filenames[3], states[3]); - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - // header - 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xac, 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, - - // 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, - - // 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, - - // 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 - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - - KeyedVector<String8,FileState> readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 4) { - fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); - return 1; - } - - bool matched = true; - for (size_t i=0; i<readSnapshot.size(); i++) { - const String8& name = readSnapshot.keyAt(i); - 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].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()); - matched = false; - } - } - - return matched ? 0 : 1; -} - -static int -get_mod_time(const char* filename, struct timeval times[2]) -{ - int err; - struct stat64 st; - err = stat64(filename, &st); - if (err != 0) { - fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno)); - return errno; - } - times[0].tv_sec = st.st_atime; - times[0].tv_usec = st.st_atime_nsec / 1000; - times[1].tv_sec = st.st_mtime; - times[1].tv_usec = st.st_mtime_nsec / 1000; - return 0; -} - -int -backup_helper_test_files() -{ - int err; - int newSnapshotFD; - int oldSnapshotFD; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); - write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); - write_text_file(SCRATCH_DIR "data/d", "d\ndd\n"); - write_text_file(SCRATCH_DIR "data/e", "e\nee\n"); - write_text_file(SCRATCH_DIR "data/f", "f\nff\n"); - write_text_file(SCRATCH_DIR "data/h", "h\nhh\n"); - - char const* files_before[] = { - "data/b", - "data/c", - "data/d", - "data/e", - "data/f" - }; - - newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - err = back_up_files(-1, newSnapshotFD, 0, SCRATCH_DIR, files_before, 5); - if (err != 0) { - return err; - } - - close(newSnapshotFD); - - sleep(3); - - struct timeval d_times[2]; - struct timeval e_times[2]; - - err = get_mod_time(SCRATCH_DIR "data/d", d_times); - err |= get_mod_time(SCRATCH_DIR "data/e", e_times); - if (err != 0) { - return err; - } - - write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); - unlink(SCRATCH_DIR "data/c"); - write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); - write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n"); - utimes(SCRATCH_DIR "data/d", d_times); - write_text_file(SCRATCH_DIR "data/e", "z\nzz\n"); - utimes(SCRATCH_DIR "data/e", e_times); - write_text_file(SCRATCH_DIR "data/g", "g\ngg\n"); - unlink(SCRATCH_DIR "data/f"); - - char const* files_after[] = { - "data/a", // added - "data/b", // same - "data/c", // different mod time - "data/d", // different size (same mod time) - "data/e", // different contents (same mod time, same size) - "data/g" // added - }; - - oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY); - if (oldSnapshotFD == -1) { - fprintf(stderr, "error opening: %s\n", strerror(errno)); - return errno; - } - - newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - err = back_up_files(oldSnapshotFD, newSnapshotFD, 0, SCRATCH_DIR, files_after, 6); - if (err != 0) { - return err; - } - - close(oldSnapshotFD); - close(newSnapshotFD); - - return 0; -} - -#endif // TEST_BACKUP_HELPERS diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c index ba19520339ed..ab48c69218fd 100644 --- a/libs/utils/futex_synchro.c +++ b/libs/utils/futex_synchro.c @@ -28,6 +28,7 @@ // This futex glue code is need on desktop linux, but is already part of bionic. #if !defined(HAVE_FUTEX_WRAPPERS) +#include <unistd.h> #include <sys/syscall.h> typedef unsigned int u32; #define asmlinkage diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 2c214c9ef3b6..caf9516f4183 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -20,7 +20,6 @@ import android.app.PendingIntent; import android.location.Address; import android.location.IGeocodeProvider; import android.location.IGpsStatusListener; -import android.location.ILocationCollector; import android.location.ILocationListener; import android.location.ILocationProvider; import android.location.Location; @@ -83,6 +82,5 @@ interface ILocationManager /* for installing external Location Providers */ void installLocationProvider(String name, ILocationProvider provider); - void installLocationCollector(ILocationCollector collector); void installGeocodeProvider(IGeocodeProvider provider); } diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl index 6c23f838a955..4fe049462bdd 100644 --- a/location/java/android/location/ILocationProvider.aidl +++ b/location/java/android/location/ILocationProvider.aidl @@ -16,6 +16,7 @@ package android.location; +import android.location.Location; import android.os.Bundle; /** @@ -41,6 +42,7 @@ interface ILocationProvider { void enableLocationTracking(boolean enable); void setMinTime(long minTime); void updateNetworkState(int state); + void updateLocation(in Location location); boolean sendExtraCommand(String command, inout Bundle extras); void addListener(int uid); void removeListener(int uid); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 872838c732bd..86ea66f103cc 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1279,27 +1279,6 @@ public class LocationManager { } /** - * Installs a location collector. - * - * @param provider Binder interface for the location collector - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_COLLECTOR permission. - * - * {@hide} - */ - public boolean installLocationCollector(ILocationCollector collector) { - try { - mService.installLocationCollector(collector); - return true; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in setLocationCollector: ", e); - return false; - } - } - - /** * Installs a geocoder server. * * @param provider Binder interface for the geocoder provider diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 565859cca622..4a51e313b2c6 100644..100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -208,12 +208,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private GpsNetworkThread mNetworkThread; private Object mNetworkThreadLock = new Object(); - private String mSuplHost; - private int mSuplPort; - private String mC2KHost; - private int mC2KPort; - private boolean mSetSuplServer; - private boolean mSetC2KServer; private String mAGpsApn; private int mAGpsDataConnectionState; private final ConnectivityManager mConnMgr; @@ -355,23 +349,27 @@ public class GpsLocationProvider extends ILocationProvider.Stub { stream.close(); mNtpServer = mProperties.getProperty("NTP_SERVER", null); - mSuplHost = mProperties.getProperty("SUPL_HOST"); + String host = mProperties.getProperty("SUPL_HOST"); String portString = mProperties.getProperty("SUPL_PORT"); - if (mSuplHost != null && portString != null) { + if (host != null && portString != null) { try { - mSuplPort = Integer.parseInt(portString); - mSetSuplServer = true; + int port = Integer.parseInt(portString); + native_set_agps_server(AGPS_TYPE_SUPL, host, port); + // use MS-Based position mode if SUPL support is enabled + mPositionMode = GPS_POSITION_MODE_MS_BASED; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse SUPL_PORT: " + portString); } } - mC2KHost = mProperties.getProperty("C2K_HOST"); + host = mProperties.getProperty("C2K_HOST"); portString = mProperties.getProperty("C2K_PORT"); - if (mC2KHost != null && portString != null) { + if (host != null && portString != null) { try { - mC2KPort = Integer.parseInt(portString); - mSetC2KServer = true; + int port = Integer.parseInt(portString); + native_set_agps_server(AGPS_TYPE_C2K, host, port); + // use MS-Based position mode if SUPL support is enabled + mPositionMode = GPS_POSITION_MODE_MS_BASED; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse C2K_PORT: " + portString); } @@ -386,10 +384,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * data network (e.g., the Internet), false otherwise. */ public boolean requiresNetwork() { - // We want updateNetworkState() to get called when the network state changes - // for XTRA and NTP time injection support. - return (mNtpServer != null || native_supports_xtra() || - mSuplHost != null || mC2KHost != null); + return true; } public void updateNetworkState(int state) { @@ -406,6 +401,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } /** + * This is called to inform us when another location provider returns a location. + * Someday we might use this for network location injection to aid the GPS + */ + public void updateLocation(Location location) { + if (location.hasAccuracy()) { + native_inject_location(location.getLatitude(), location.getLongitude(), + location.getAccuracy()); + } + } + + /** * Returns true if the provider requires access to a * satellite-based positioning system (e.g., GPS), false * otherwise. @@ -611,27 +617,44 @@ public class GpsLocationProvider extends ILocationProvider.Stub { synchronized(mListeners) { mListeners.remove(this); } + if (mListener != null) { + mListener.asBinder().unlinkToDeath(this, 0); + } } } 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"); + } } } } @@ -641,6 +664,16 @@ public class GpsLocationProvider extends ILocationProvider.Stub { if ("delete_aiding_data".equals(command)) { return deleteAidingData(extras); } + if ("force_time_injection".equals(command)) { + return forceTimeInjection(); + } + if ("force_xtra_injection".equals(command)) { + if (native_supports_xtra() && mNetworkThread != null) { + xtraDownloadRequest(); + return true; + } + return false; + } Log.w(TAG, "sendExtraCommand: unknown command " + command); return false; @@ -676,6 +709,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub { return false; } + private boolean forceTimeInjection() { + if (Config.LOGD) Log.d(TAG, "forceTimeInjection"); + if (mNetworkThread != null) { + mNetworkThread.timeInjectRequest(); + return true; + } + return false; + } + public void startNavigating() { if (!mStarted) { if (DEBUG) Log.d(TAG, "startNavigating"); @@ -811,30 +853,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--; } } @@ -924,8 +969,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub { int result = mConnMgr.startUsingNetworkFeature( ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL); if (result == Phone.APN_ALREADY_ACTIVE) { - native_agps_data_conn_open(mAGpsApn); - mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; + if (mAGpsApn != null) { + native_agps_data_conn_open(mAGpsApn); + mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; + } else { + Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE"); + native_agps_data_conn_failed(); + } } else if (result == Phone.APN_REQUEST_STARTED) { mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING; } else { @@ -959,29 +1009,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - private boolean setAGpsServer(int type, String host, int port) { - try { - InetAddress inetAddress = InetAddress.getByName(host); - if (inetAddress != null) { - byte[] addrBytes = inetAddress.getAddress(); - long addr = 0; - for (int i = 0; i < addrBytes.length; i++) { - int temp = addrBytes[i]; - // signed -> unsigned - if (temp < 0) temp = 256 + temp; - addr = addr * 256 + temp; - } - // use MS-Based position mode if SUPL support is enabled - mPositionMode = GPS_POSITION_MODE_MS_BASED; - native_set_agps_server(type, (int)addr, port); - } - } catch (UnknownHostException e) { - Log.e(TAG, "unknown host for server " + host); - return false; - } - return true; - } - private class GpsEventThread extends Thread { public GpsEventThread() { @@ -1004,6 +1031,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private long mNextNtpTime = 0; private long mNextXtraTime = 0; + private boolean mTimeInjectRequested = false; private boolean mXtraDownloadRequested = false; private boolean mDone = false; @@ -1054,16 +1082,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } waitTime = getWaitTime(); } while (!mDone && ((!mXtraDownloadRequested && - !mSetSuplServer && !mSetC2KServer && waitTime > 0) + !mTimeInjectRequested && waitTime > 0) || !mNetworkAvailable)); if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop"); if (!mDone) { if (mNtpServer != null && - mNextNtpTime <= System.currentTimeMillis()) { + (mTimeInjectRequested || mNextNtpTime <= System.currentTimeMillis())) { if (Config.LOGD) { Log.d(TAG, "Requesting time from NTP server " + mNtpServer); } + mTimeInjectRequested = false; if (client.requestTime(mNtpServer, 10000)) { long time = client.getNtpTime(); long timeReference = client.getNtpTimeReference(); @@ -1081,21 +1110,10 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - // Set the AGPS server addresses if we have not yet - if (mSetSuplServer) { - if (setAGpsServer(AGPS_TYPE_SUPL, mSuplHost, mSuplPort)) { - mSetSuplServer = false; - } - } - if (mSetC2KServer) { - if (setAGpsServer(AGPS_TYPE_C2K, mC2KHost, mC2KPort)) { - mSetC2KServer = false; - } - } - if ((mXtraDownloadRequested || (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) && xtraDownloader != null) { + mXtraDownloadRequested = false; byte[] data = xtraDownloader.downloadXtraData(); if (data != null) { if (Config.LOGD) { @@ -1103,7 +1121,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } native_inject_xtra_data(data, data.length); mNextXtraTime = 0; - mXtraDownloadRequested = false; } else { mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL; } @@ -1118,6 +1135,11 @@ public class GpsLocationProvider extends ILocationProvider.Stub { notify(); } + synchronized void timeInjectRequest() { + mTimeInjectRequested = true; + notify(); + } + synchronized void signal() { notify(); } @@ -1177,7 +1199,8 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // mask[0] is ephemeris mask and mask[1] is almanac mask private native int native_read_sv_status(int[] svs, float[] snrs, float[] elevations, float[] azimuths, int[] masks); - + private native void native_inject_location(double latitude, double longitude, float accuracy); + // XTRA Support private native void native_inject_time(long time, long timeReference, int uncertainty); private native boolean native_supports_xtra(); @@ -1187,5 +1210,5 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private native void native_agps_data_conn_open(String apn); private native void native_agps_data_conn_closed(); private native void native_agps_data_conn_failed(); - private native void native_set_agps_server(int type, int addr, int port); + private native void native_set_agps_server(int type, String hostname, int port); } diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java index b40cdcaf212f..4ae424a9b486 100644 --- a/location/java/com/android/internal/location/LocationProviderProxy.java +++ b/location/java/com/android/internal/location/LocationProviderProxy.java @@ -53,6 +53,12 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { } } + public void unlinkProvider() { + if (mProvider != null) { + mProvider.asBinder().unlinkToDeath(this, 0); + } + } + public String getName() { return mName; } @@ -219,6 +225,14 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { } } + public void updateLocation(Location location) { + try { + mProvider.updateLocation(location); + } catch (RemoteException e) { + Log.e(TAG, "updateLocation failed", e); + } + } + public boolean sendExtraCommand(String command, Bundle extras) { try { return mProvider.sendExtraCommand(command, extras); @@ -247,5 +261,6 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { public void binderDied() { Log.w(TAG, "Location Provider " + mName + " died"); mDead = true; + mProvider.asBinder().unlinkToDeath(this, 0); } } diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java index f167a44f2ce8..e2e056243e0b 100644 --- a/location/java/com/android/internal/location/MockProvider.java +++ b/location/java/com/android/internal/location/MockProvider.java @@ -172,6 +172,9 @@ public class MockProvider extends ILocationProvider.Stub { public void updateNetworkState(int state) { } + public void updateLocation(Location location) { + } + public boolean sendExtraCommand(String command, Bundle extras) { return false; } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 3346bed93e30..4d1535f9342b 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -88,7 +88,7 @@ public class AudioRecord private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -17; private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -19; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE = -19; private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; // Events: @@ -113,13 +113,7 @@ public class AudioRecord */ @SuppressWarnings("unused") private int mNativeRecorderInJavaObj; - /** - * Accessed by native methods: provides access to record source constants - */ - @SuppressWarnings("unused") - private final static int SOURCE_DEFAULT = MediaRecorder.AudioSource.DEFAULT; - @SuppressWarnings("unused") - private final static int SOURCE_MIC = MediaRecorder.AudioSource.MIC; + /** * Accessed by native methods: provides access to the callback data. */ @@ -252,8 +246,8 @@ public class AudioRecord //-------------- // audio source - if ( (audioSource != MediaRecorder.AudioSource.DEFAULT) - && (audioSource != MediaRecorder.AudioSource.MIC) ) { + if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) || + (audioSource > MediaRecorder.getAudioSourceMax()) ) { throw (new IllegalArgumentException("Invalid audio source.")); } else { mRecordSource = audioSource; diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 881de4d2a468..937baad5be94 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -163,6 +163,9 @@ public class AudioService extends IAudioService.Stub { */ private int mRingerMode; + /** @see System#MODE_RINGER_STREAMS_AFFECTED */ + private int mRingerModeAffectedStreams; + /** @see System#MUTE_STREAMS_AFFECTED */ private int mMuteAffectedStreams; @@ -286,6 +289,10 @@ public class AudioService extends IAudioService.Stub { mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); + mRingerModeAffectedStreams = Settings.System.getInt(cr, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + ((1 << AudioManager.STREAM_RING)|(1 << AudioManager.STREAM_NOTIFICATION)|(1 << AudioManager.STREAM_SYSTEM))); + mMuteAffectedStreams = System.getInt(cr, System.MUTE_STREAMS_AFFECTED, ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); @@ -399,7 +406,7 @@ public class AudioService extends IAudioService.Stub { ensureValidStreamType(streamType); syncRingerAndNotificationStreamVolume(streamType, index, false); - setStreamVolumeInt(streamType, index, false); + setStreamVolumeInt(streamType, index, false, true); // UI, etc. mVolumePanel.postVolumeChanged(streamType, flags); @@ -437,7 +444,7 @@ public class AudioService extends IAudioService.Stub { } if (streamType == AudioManager.STREAM_RING) { // One-off to sync notification volume to ringer volume - setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force); + setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force, true); } } } @@ -451,10 +458,11 @@ public class AudioService extends IAudioService.Stub { * @param index Desired volume index of the stream * @param force If true, set the volume even if the desired volume is same * as the current volume. + * @param lastAudible If true, stores new index as last audible one */ - private void setStreamVolumeInt(int streamType, int index, boolean force) { + private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) { VolumeStreamState streamState = mStreamStates[streamType]; - if (streamState.setIndex(index) || force) { + if (streamState.setIndex(index, lastAudible) || force) { // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. if (streamState.muteCount() == 0) { @@ -517,13 +525,20 @@ public class AudioService extends IAudioService.Stub { if (!isStreamAffectedByRingerMode(streamType)) continue; // Bring back last audible volume setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex, - false); + false, false); } } else { for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - if (!isStreamAffectedByRingerMode(streamType)) continue; - // Either silent or vibrate, either way volume is 0 - setStreamVolumeInt(streamType, 0, false); + if (isStreamAffectedByRingerMode(streamType)) { + // Either silent or vibrate, either way volume is 0 + setStreamVolumeInt(streamType, 0, false, false); + } else { + // restore stream volume in the case the stream changed from affected + // to non affected by ringer mode. Does not arm to do it for streams that + // are not affected as well. + setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex, + false, false); + } } } @@ -621,7 +636,7 @@ public class AudioService extends IAudioService.Stub { int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int index = mStreamStates[streamType].mIndex; syncRingerAndNotificationStreamVolume(streamType, index, true); - setStreamVolumeInt(streamType, index, true); + setStreamVolumeInt(streamType, index, true, true); } } @@ -653,7 +668,7 @@ public class AudioService extends IAudioService.Stub { mSpeakerIsOn = true; mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; incallMask = AudioSystem.ROUTE_ALL; - } else if (mSpeakerIsOn) { + } else if (routes == 0 && mSpeakerIsOn) { mSpeakerIsOn = false; if (mBluetoothScoIsConnected) { mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO; @@ -680,7 +695,7 @@ public class AudioService extends IAudioService.Stub { // should not affect A2DP routing ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; - } else if (mBluetoothScoIsConnected) { + } else if (routes == 0 && mBluetoothScoIsConnected) { mBluetoothScoIsConnected = false; if (mHeadsetIsConnected) { mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; @@ -724,7 +739,7 @@ public class AudioService extends IAudioService.Stub { ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; } - } else if (mHeadsetIsConnected) { + } else if (routes == 0 && mHeadsetIsConnected) { mHeadsetIsConnected = false; // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior if (!mBluetoothScoIsConnected) { @@ -757,7 +772,7 @@ public class AudioService extends IAudioService.Stub { // so there is no need to disable other routes. ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; - } else if (mBluetoothA2dpIsConnected) { + } else if (routes == 0 && mBluetoothA2dpIsConnected) { mBluetoothA2dpIsConnected = false; mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; @@ -791,7 +806,7 @@ public class AudioService extends IAudioService.Stub { int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int index = mStreamStates[streamType].mIndex; syncRingerAndNotificationStreamVolume(streamType, index, true); - setStreamVolumeInt(streamType, index, true); + setStreamVolumeInt(streamType, index, true, true); } } } @@ -941,9 +956,7 @@ public class AudioService extends IAudioService.Stub { } public boolean isStreamAffectedByRingerMode(int streamType) { - int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, - Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); - return (ringerModeAffectedStreams & (1 << streamType)) != 0; + return (mRingerModeAffectedStreams & (1 << streamType)) != 0; } public boolean isStreamAffectedByMute(int streamType) { @@ -1095,15 +1108,15 @@ public class AudioService extends IAudioService.Stub { } public boolean adjustIndex(int deltaIndex) { - return setIndex(mIndex + deltaIndex); + return setIndex(mIndex + deltaIndex, true); } - public boolean setIndex(int index) { + public boolean setIndex(int index, boolean lastAudible) { int oldIndex = mIndex; mIndex = getValidIndex(index); if (oldIndex != mIndex) { - if (mIndex > 0) { + if (lastAudible) { mLastAudibleIndex = mIndex; } return true; @@ -1153,7 +1166,7 @@ public class AudioService extends IAudioService.Stub { mDeathHandlers.add(this); // If the stream is not yet muted by any client, set lvel to 0 if (muteCount() == 0) { - setIndex(0); + setIndex(0, false); sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, VolumeStreamState.this, 0); } @@ -1180,7 +1193,7 @@ public class AudioService extends IAudioService.Stub { // If the stream is not mut any more, restore it's volume if // ringer mode allows it if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { - setIndex(mLastAudibleIndex); + setIndex(mLastAudibleIndex, false); sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, VolumeStreamState.this, 0); } @@ -1405,6 +1418,10 @@ public class AudioService extends IAudioService.Stub { public void onChange(boolean selfChange) { super.onChange(selfChange); + mRingerModeAffectedStreams = Settings.System.getInt(mContentResolver, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + 0); + /* * Ensure all stream types that should be affected by ringer mode * are in the proper state. diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 3cd841de4145..5f1be9d5e118 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -425,8 +425,7 @@ public class AudioTrack } /** - * Returns the current playback rate in Hz. Note that this rate may differ from the one set - * with {@link #setPlaybackRate(int)} as the value effectively used is implementation-dependent. + * Returns the current playback rate in Hz. */ public int getPlaybackRate() { return native_get_playback_rate(); @@ -651,18 +650,13 @@ public class AudioTrack * Sets the playback sample rate for this track. This sets the sampling rate at which * the audio data will be consumed and played back, not the original sampling rate of the * content. Setting it to half the sample rate of the content will cause the playback to - * last twice as long, but will also result result in a negative pitch shift. - * The current implementation supports a maximum sample rate of 64kHz. - * Use {@link #getSampleRate()} to check the rate actually used in hardware after - * potential clamping. + * last twice as long, but will also result in a negative pitch shift. + * The valid sample rate range if from 1Hz to twice the value returned by + * {@link #getNativeOutputSampleRate(int)}. * @param sampleRateInHz the sample rate expressed in Hz * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, * {@link #ERROR_INVALID_OPERATION} */ - // FIXME: the implementation should support twice the hardware output sample rate - // (see {@link #getNativeOutputSampleRate(int)}), but currently - // due to the representation of the sample rate in the native layer, the sample rate - // is limited to 65535Hz public int setPlaybackRate(int sampleRateInHz) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; @@ -670,8 +664,7 @@ public class AudioTrack if (sampleRateInHz <= 0) { return ERROR_BAD_VALUE; } - native_set_playback_rate(sampleRateInHz); - return SUCCESS; + return native_set_playback_rate(sampleRateInHz); } @@ -1031,8 +1024,8 @@ public class AudioTrack private native final void native_setVolume(float leftVolume, float rightVolume); - private native final void native_set_playback_rate(int sampleRateInHz); - private native final int native_get_playback_rate(); + private native final int native_set_playback_rate(int sampleRateInHz); + private native final int native_get_playback_rate(); private native final int native_set_marker_pos(int marker); private native final int native_get_marker_pos(); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 19ab0add2b91..3b46d690c40b 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -58,7 +58,7 @@ import java.lang.ref.WeakReference; * MediaPlayer object driven by the supported playback control operations. * The ovals represent the states a MediaPlayer object may reside * in. The arcs represent the playback control operations that drive the object - * state transition. There are two types of arcs. The arcs with a single arrow + * state transition. There are two types of arcs. The arcs with a single arrow * head represent synchronous method calls, while those with * a double arrow head represent asynchronous method calls.</p> * @@ -69,42 +69,42 @@ import java.lang.ref.WeakReference; * <p>From this state diagram, one can see that a MediaPlayer object has the * following states:</p> * <ul> - * <li>When a MediaPlayer object is just created using <code>new</code> or - * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after - * {@link #release()} is called, it is in the <em>End</em> state. Between these - * two states is the life cycle of the MediaPlayer object. + * <li>When a MediaPlayer object is just created using <code>new</code> or + * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after + * {@link #release()} is called, it is in the <em>End</em> state. Between these + * two states is the life cycle of the MediaPlayer object. * <ul> - * <li>There is a subtle but important difference between a newly constructed - * MediaPlayer object and the MediaPlayer object after {@link #reset()} - * is called. It is a programming error to invoke methods such - * as {@link #getCurrentPosition()}, - * {@link #getDuration()}, {@link #getVideoHeight()}, + * <li>There is a subtle but important difference between a newly constructed + * MediaPlayer object and the MediaPlayer object after {@link #reset()} + * is called. It is a programming error to invoke methods such + * as {@link #getCurrentPosition()}, + * {@link #getDuration()}, {@link #getVideoHeight()}, * {@link #getVideoWidth()}, {@link #setAudioStreamType(int)}, * {@link #setLooping(boolean)}, - * {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()}, - * {@link #stop()}, {@link #seekTo(int)}, {@link #prepare()} or - * {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these - * methods is called right after a MediaPlayer object is constructed, - * the user supplied callback method OnErrorListener.onError() won't be + * {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()}, + * {@link #stop()}, {@link #seekTo(int)}, {@link #prepare()} or + * {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these + * methods is called right after a MediaPlayer object is constructed, + * the user supplied callback method OnErrorListener.onError() won't be * called by the internal player engine and the object state remains - * unchanged; but if these methods are called right after {@link #reset()}, + * unchanged; but if these methods are called right after {@link #reset()}, * the user supplied callback method OnErrorListener.onError() will be - * invoked by the internal player engine and the object will be + * invoked by the internal player engine and the object will be * transfered to the <em>Error</em> state. </li> - * <li>It is also recommended that once - * a MediaPlayer object is no longer being used, call {@link #release()} immediately - * so that resources used by the internal player engine associated with the + * <li>It is also recommended that once + * a MediaPlayer object is no longer being used, call {@link #release()} immediately + * so that resources used by the internal player engine associated with the * MediaPlayer object can be released immediately. Resource may include - * singleton resources such as hardware acceleration components and + * singleton resources such as hardware acceleration components and * failure to call {@link #release()} may cause subsequent instances of * MediaPlayer objects to fallback to software implementations or fail * altogether. Once the MediaPlayer - * object is in the <em>End</em> state, it can no longer be used and + * object is in the <em>End</em> state, it can no longer be used and * there is no way to bring it back to any other state. </li> - * <li>Furthermore, - * the MediaPlayer objects created using <code>new</code> is in the - * <em>Idle</em> state, while those created with one - * of the overloaded convenient <code>create</code> methods are <em>NOT</em> + * <li>Furthermore, + * the MediaPlayer objects created using <code>new</code> is in the + * <em>Idle</em> state, while those created with one + * of the overloaded convenient <code>create</code> methods are <em>NOT</em> * in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em> * state if the creation using <code>create</code> method is successful. * </li> @@ -114,23 +114,23 @@ import java.lang.ref.WeakReference; * reasons, such as unsupported audio/video format, poorly interleaved * audio/video, resolution too high, streaming timeout, and the like. * Thus, error reporting and recovery is an important concern under - * these circumstances. Sometimes, due to programming errors, invoking a playback + * these circumstances. Sometimes, due to programming errors, invoking a playback * control operation in an invalid state may also occur. Under all these * error conditions, the internal player engine invokes a user supplied * OnErrorListener.onError() method if an OnErrorListener has been * registered beforehand via * {@link #setOnErrorListener(android.media.MediaPlayer.OnErrorListener)}. * <ul> - * <li>It is important to note that once an error occurs, the - * MediaPlayer object enters the <em>Error</em> state (except as noted + * <li>It is important to note that once an error occurs, the + * MediaPlayer object enters the <em>Error</em> state (except as noted * above), even if an error listener has not been registered by the application.</li> * <li>In order to reuse a MediaPlayer object that is in the <em> - * Error</em> state and recover from the error, + * Error</em> state and recover from the error, * {@link #reset()} can be called to restore the object to its <em>Idle</em> * state.</li> - * <li>It is good programming practice to have your application - * register a OnErrorListener to look out for error notifications from - * the internal player engine.</li> + * <li>It is good programming practice to have your application + * register a OnErrorListener to look out for error notifications from + * the internal player engine.</li> * <li>IlleglStateException is * thrown to prevent programming errors such as calling {@link #prepare()}, * {@link #prepareAsync()}, or one of the overloaded <code>setDataSource @@ -141,28 +141,28 @@ import java.lang.ref.WeakReference; * {@link #setDataSource(FileDescriptor)}, or * {@link #setDataSource(String)}, or * {@link #setDataSource(Context, Uri)}, or - * {@link #setDataSource(FileDescriptor, long, long)} transfers a + * {@link #setDataSource(FileDescriptor, long, long)} transfers a * MediaPlayer object in the <em>Idle</em> state to the * <em>Initialized</em> state. * <ul> - * <li>An IllegalStateException is thrown if + * <li>An IllegalStateException is thrown if * setDataSource() is called in any other state.</li> - * <li>It is good programming - * practice to always look out for <code>IllegalArgumentException</code> + * <li>It is good programming + * practice to always look out for <code>IllegalArgumentException</code> * and <code>IOException</code> that may be thrown from the overloaded * <code>setDataSource</code> methods.</li> * </ul> * </li> * <li>A MediaPlayer object must first enter the <em>Prepared</em> state - * before playback can be started. + * before playback can be started. * <ul> * <li>There are two ways (synchronous vs. * asynchronous) that the <em>Prepared</em> state can be reached: * either a call to {@link #prepare()} (synchronous) which * transfers the object to the <em>Prepared</em> state once the method call * returns, or a call to {@link #prepareAsync()} (asynchronous) which - * first transfers the object to the <em>Preparing</em> state after the - * call returns (which occurs almost right way) while the internal + * first transfers the object to the <em>Preparing</em> state after the + * call returns (which occurs almost right way) while the internal * player engine continues working on the rest of preparation work * until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns, * the internal player engine then calls a user supplied callback method, @@ -173,22 +173,22 @@ import java.lang.ref.WeakReference; * the <em>Preparing</em> state is a transient state, and the behavior * of calling any method with side effect while a MediaPlayer object is * in the <em>Preparing</em> state is undefined.</li> - * <li>An IllegalStateException is - * thrown if {@link #prepare()} or {@link #prepareAsync()} is called in - * any other state.</li> - * <li>While in the <em>Prepared</em> state, properties - * such as audio/sound volume, screenOnWhilePlaying, looping can be + * <li>An IllegalStateException is + * thrown if {@link #prepare()} or {@link #prepareAsync()} is called in + * any other state.</li> + * <li>While in the <em>Prepared</em> state, properties + * such as audio/sound volume, screenOnWhilePlaying, looping can be * adjusted by invoking the corresponding set methods.</li> * </ul> * </li> - * <li>To start the playback, {@link #start()} must be called. After + * <li>To start the playback, {@link #start()} must be called. After * {@link #start()} returns successfully, the MediaPlayer object is in the - * <em>Started</em> state. {@link #isPlaying()} can be called to test + * <em>Started</em> state. {@link #isPlaying()} can be called to test * whether the MediaPlayer object is in the <em>Started</em> state. * <ul> * <li>While in the <em>Started</em> state, the internal player engine calls * a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback - * method if a OnBufferingUpdateListener has been registered beforehand + * method if a OnBufferingUpdateListener has been registered beforehand * via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}. * This callback allows applications to keep track of the buffering status * while streaming audio/video.</li> @@ -197,44 +197,44 @@ import java.lang.ref.WeakReference; * </ul> * </li> * <li>Playback can be paused and stopped, and the current playback position - * can be adjusted. Playback can be paused via {@link #pause()}. When the call to + * can be adjusted. Playback can be paused via {@link #pause()}. When the call to * {@link #pause()} returns, the MediaPlayer object enters the - * <em>Paused</em> state. Note that the transition from the <em>Started</em> - * state to the <em>Paused</em> state and vice versa happens - * asynchronously in the player engine. It may take some time before - * the state is updated in calls to {@link #isPlaying()}, and it can be + * <em>Paused</em> state. Note that the transition from the <em>Started</em> + * state to the <em>Paused</em> state and vice versa happens + * asynchronously in the player engine. It may take some time before + * the state is updated in calls to {@link #isPlaying()}, and it can be * a number of seconds in the case of streamed content. * <ul> - * <li>Calling {@link #start()} to resume playback for a paused + * <li>Calling {@link #start()} to resume playback for a paused * MediaPlayer object, and the resumed playback - * position is the same as where it was paused. When the call to + * position is the same as where it was paused. When the call to * {@link #start()} returns, the paused MediaPlayer object goes back to * the <em>Started</em> state.</li> * <li>Calling {@link #pause()} has no effect on * a MediaPlayer object that is already in the <em>Paused</em> state.</li> * </ul> * </li> - * <li>Calling {@link #stop()} stops playback and causes a + * <li>Calling {@link #stop()} stops playback and causes a * MediaPlayer in the <em>Started</em>, <em>Paused</em>, <em>Prepared - * </em> or <em>PlaybackCompleted</em> state to enter the + * </em> or <em>PlaybackCompleted</em> state to enter the * <em>Stopped</em> state. * <ul> - * <li>Once in the <em>Stopped</em> state, playback cannot be started + * <li>Once in the <em>Stopped</em> state, playback cannot be started * until {@link #prepare()} or {@link #prepareAsync()} are called to set * the MediaPlayer object to the <em>Prepared</em> state again.</li> - * <li>Calling {@link #stop()} has no effect on a MediaPlayer + * <li>Calling {@link #stop()} has no effect on a MediaPlayer * object that is already in the <em>Stopped</em> state.</li> * </ul> * </li> - * <li>The playback position can be adjusted with a call to - * {@link #seekTo(int)}. + * <li>The playback position can be adjusted with a call to + * {@link #seekTo(int)}. * <ul> * <li>Although the asynchronuous {@link #seekTo(int)} * call returns right way, the actual seek operation may take a while to - * finish, especially for audio/video being streamed. When the actual - * seek operation completes, the internal player engine calls a user + * finish, especially for audio/video being streamed. When the actual + * seek operation completes, the internal player engine calls a user * supplied OnSeekComplete.onSeekComplete() if an OnSeekCompleteListener - * has been registered beforehand via + * has been registered beforehand via * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}.</li> * <li>Please * note that {@link #seekTo(int)} can also be called in the other states, @@ -242,7 +242,7 @@ import java.lang.ref.WeakReference; * </em> state.</li> * <li>Furthermore, the actual current playback position * can be retrieved with a call to {@link #getCurrentPosition()}, which - * is helpful for applications such as a Music player that need to keep + * is helpful for applications such as a Music player that need to keep * track of the playback progress.</li> * </ul> * </li> @@ -272,7 +272,7 @@ import java.lang.ref.WeakReference; * <td>Invalid States </p></td> * <td>Comments </p></td></tr> * <tr><td>getCurrentPosition </p></td> - * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, + * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, * PlaybackCompleted} </p></td> * <td>{Error}</p></td> * <td>Successful invoke of this method in a valid state does not change the @@ -282,45 +282,45 @@ import java.lang.ref.WeakReference; * <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td> * <td>{Idle, Initialized, Error} </p></td> * <td>Successful invoke of this method in a valid state does not change the - * state. Calling this method in an invalid state transfers the object + * state. Calling this method in an invalid state transfers the object * to the <em>Error</em> state. </p></td></tr> * <tr><td>getVideoHeight </p></td> - * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, + * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> * <td>Successful invoke of this method in a valid state does not change the - * state. Calling this method in an invalid state transfers the object + * state. Calling this method in an invalid state transfers the object * to the <em>Error</em> state. </p></td></tr> * <tr><td>getVideoWidth </p></td> * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> - * <td>Successful invoke of this method in a valid state does not change - * the state. Calling this method in an invalid state transfers the + * <td>Successful invoke of this method in a valid state does not change + * the state. Calling this method in an invalid state transfers the * object to the <em>Error</em> state. </p></td></tr> * <tr><td>isPlaying </p></td> - * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, + * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> * <td>Successful invoke of this method in a valid state does not change - * the state. Calling this method in an invalid state transfers the + * the state. Calling this method in an invalid state transfers the * object to the <em>Error</em> state. </p></td></tr> * <tr><td>pause </p></td> * <td>{Started, Paused}</p></td> * <td>{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}</p></td> - * <td>Successful invoke of this method in a valid state transfers the - * object to the <em>Paused</em> state. Calling this method in an + * <td>Successful invoke of this method in a valid state transfers the + * object to the <em>Paused</em> state. Calling this method in an * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> * <tr><td>prepare </p></td> * <td>{Initialized, Stopped} </p></td> * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td> - * <td>Successful invoke of this method in a valid state transfers the - * object to the <em>Prepared</em> state. Calling this method in an + * <td>Successful invoke of this method in a valid state transfers the + * object to the <em>Prepared</em> state. Calling this method in an * invalid state throws an IllegalStateException.</p></td></tr> * <tr><td>prepareAsync </p></td> * <td>{Initialized, Stopped} </p></td> * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td> - * <td>Successful invoke of this method in a valid state transfers the + * <td>Successful invoke of this method in a valid state transfers the * object to the <em>Preparing</em> state. Calling this method in an * invalid state throws an IllegalStateException.</p></td></tr> * <tr><td>release </p></td> @@ -328,18 +328,18 @@ import java.lang.ref.WeakReference; * <td>{} </p></td> * <td>After {@link #release()}, the object is no longer available. </p></td></tr> * <tr><td>reset </p></td> - * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, + * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, * PlaybackCompleted, Error}</p></td> * <td>{}</p></td> * <td>After {@link #reset()}, the object is like being just created.</p></td></tr> * <tr><td>seekTo </p></td> * <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td> * <td>{Idle, Initialized, Stopped, Error}</p></td> - * <td>Successful invoke of this method in a valid state does not change - * the state. Calling this method in an invalid state transfers the + * <td>Successful invoke of this method in a valid state does not change + * the state. Calling this method in an invalid state transfers the * object to the <em>Error</em> state. </p></td></tr> * <tr><td>setAudioStreamType </p></td> - * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, + * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> * <td>Successful invoke of this method does not change the state.</p></td></tr> @@ -347,8 +347,8 @@ import java.lang.ref.WeakReference; * <td>{Idle} </p></td> * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, * Error} </p></td> - * <td>Successful invoke of this method in a valid state transfers the - * object to the <em>Initialized</em> state. Calling this method in an + * <td>Successful invoke of this method in a valid state transfers the + * object to the <em>Initialized</em> state. Calling this method in an * invalid state throws an IllegalStateException.</p></td></tr> * <tr><td>setDisplay </p></td> * <td>any </p></td> @@ -356,11 +356,11 @@ import java.lang.ref.WeakReference; * <td>This method can be called in any state and calling it does not change * the object state. </p></td></tr> * <tr><td>setLooping </p></td> - * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, + * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> - * <td>Successful invoke of this method in a valid state does not change - * the state. Calling this method in an + * <td>Successful invoke of this method in a valid state does not change + * the state. Calling this method in an * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> * <tr><td>isLooping </p></td> * <td>any </p></td> @@ -398,7 +398,7 @@ import java.lang.ref.WeakReference; * <td>This method can be called in any state and calling it does not change * the object state. </p></td></tr> * <tr><td>setVolume </p></td> - * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, + * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, * PlaybackCompleted}</p></td> * <td>{Error}</p></td> * <td>Successful invoke of this method does not change the state. @@ -410,14 +410,14 @@ import java.lang.ref.WeakReference; * <tr><td>start </p></td> * <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td> * <td>{Idle, Initialized, Stopped, Error}</p></td> - * <td>Successful invoke of this method in a valid state transfers the - * object to the <em>Started</em> state. Calling this method in an + * <td>Successful invoke of this method in a valid state transfers the + * object to the <em>Started</em> state. Calling this method in an * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> * <tr><td>stop </p></td> * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> * <td>{Idle, Initialized, Error}</p></td> - * <td>Successful invoke of this method in a valid state transfers the - * object to the <em>Stopped</em> state. Calling this method in an + * <td>Successful invoke of this method in a valid state transfers the + * object to the <em>Stopped</em> state. Calling this method in an * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> * </table> * @@ -429,13 +429,13 @@ import java.lang.ref.WeakReference; * */ public class MediaPlayer -{ +{ static { System.loadLibrary("media_jni"); } - + private final static String TAG = "MediaPlayer"; - + private int mNativeContext; // accessed by native methods private int mListenerContext; // accessed by native methods private Surface mSurface; // accessed by native methods @@ -444,16 +444,16 @@ public class MediaPlayer private PowerManager.WakeLock mWakeLock = null; private boolean mScreenOnWhilePlaying; private boolean mStayAwake; - + /** - * Default constructor. Consider using one of the create() methods for + * Default constructor. Consider using one of the create() methods for * synchronously instantiating a MediaPlayer from a Uri or resource. * <p>When done with the MediaPlayer, you should call {@link #release()}, * to free the resources. If not released, too many MediaPlayer instances may * result in an exception.</p> */ public MediaPlayer() { - + Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); @@ -468,17 +468,27 @@ public class MediaPlayer */ native_setup(new WeakReference<MediaPlayer>(this)); } - + + /* + * Update the MediaPlayer ISurface. Call after updating mSurface. + */ + private native void _setVideoSurface(); + /** * Sets the SurfaceHolder to use for displaying the video portion of the media. * This call is optional. Not calling it when playing back a video will * result in only the audio track being played. - * + * * @param sh the SurfaceHolder to use for video display */ public void setDisplay(SurfaceHolder sh) { mSurfaceHolder = sh; - mSurface = sh.getSurface(); + if (sh != null) { + mSurface = sh.getSurface(); + } else { + mSurface = null; + } + _setVideoSurface(); updateSurfaceScreenOn(); } @@ -488,29 +498,29 @@ public class MediaPlayer * <p>When done with the MediaPlayer, you should call {@link #release()}, * to free the resources. If not released, too many MediaPlayer instances will * result in an exception.</p> - * - * @param context the Context to use + * + * @param context the Context to use * @param uri the Uri from which to get the datasource * @return a MediaPlayer object, or null if creation failed */ public static MediaPlayer create(Context context, Uri uri) { return create (context, uri, null); } - + /** * Convenience method to create a MediaPlayer for a given Uri. * On success, {@link #prepare()} will already have been called and must not be called again. * <p>When done with the MediaPlayer, you should call {@link #release()}, * to free the resources. If not released, too many MediaPlayer instances will * result in an exception.</p> - * - * @param context the Context to use + * + * @param context the Context to use * @param uri the Uri from which to get the datasource * @param holder the SurfaceHolder to use for displaying the video * @return a MediaPlayer object, or null if creation failed */ public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) { - + try { MediaPlayer mp = new MediaPlayer(); mp.setDataSource(context, uri); @@ -539,9 +549,9 @@ public class MediaPlayer * <p>When done with the MediaPlayer, you should call {@link #release()}, * to free the resources. If not released, too many MediaPlayer instances will * result in an exception.</p> - * - * @param context the Context to use - * @param resid the raw resource id (<var>R.raw.<something></var>) for + * + * @param context the Context to use + * @param resid the raw resource id (<var>R.raw.<something></var>) for * the resource to use as the datasource * @return a MediaPlayer object, or null if creation failed */ @@ -567,17 +577,17 @@ public class MediaPlayer } return null; } - + /** * Sets the data source as a content Uri. - * + * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @throws IllegalStateException if it is called in an invalid state */ public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { - + String scheme = uri.getScheme(); if(scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); @@ -607,13 +617,14 @@ public class MediaPlayer fd.close(); } } + Log.d(TAG, "Couldn't open file on client side, trying server side"); setDataSource(uri.toString()); return; } /** * Sets the data source (file-path or http/rtsp URL) to use. - * + * * @param path the path of the file, or the http/rtsp URL of the stream you want to play * @throws IllegalStateException if it is called in an invalid state */ @@ -622,57 +633,57 @@ public class MediaPlayer /** * Sets the data source (FileDescriptor) to use. It is the caller's responsibility * to close the file descriptor. It is safe to do so as soon as this call returns. - * + * * @param fd the FileDescriptor for the file you want to play * @throws IllegalStateException if it is called in an invalid state */ - public void setDataSource(FileDescriptor fd) + public void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException, IllegalStateException { // intentionally less than LONG_MAX setDataSource(fd, 0, 0x7ffffffffffffffL); } - + /** * Sets the data source (FileDescriptor) to use. It is the caller's responsibility * to close the file descriptor. It is safe to do so as soon as this call returns. - * + * * @param fd the FileDescriptor for the file you want to play * @param offset the offset into the file where the data to be played starts, in bytes * @param length the length in bytes of the data to be played * @throws IllegalStateException if it is called in an invalid state */ - public native void setDataSource(FileDescriptor fd, long offset, long length) + public native void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException; /** * Prepares the player for playback, synchronously. - * + * * After setting the datasource and the display surface, you need to either * call prepare() or prepareAsync(). For files, it is OK to call prepare(), * which blocks until MediaPlayer is ready for playback. - * + * * @throws IllegalStateException if it is called in an invalid state */ public native void prepare() throws IOException, IllegalStateException; - + /** * Prepares the player for playback, asynchronously. - * + * * After setting the datasource and the display surface, you need to either * call prepare() or prepareAsync(). For streams, you should call prepareAsync(), * which returns immediately, rather than blocking until enough data has been * buffered. - * + * * @throws IllegalStateException if it is called in an invalid state */ public native void prepareAsync() throws IllegalStateException; - + /** * Starts or resumes playback. If playback had previously been paused, * playback will continue from where it was paused. If playback had * been stopped, or never started before, playback will start at the * beginning. - * + * * @throws IllegalStateException if it is called in an invalid state */ public void start() throws IllegalStateException { @@ -681,10 +692,10 @@ public class MediaPlayer } private native void _start() throws IllegalStateException; - + /** - * Stops playback after playback has been stopped or paused. - * + * Stops playback after playback has been stopped or paused. + * * @throws IllegalStateException if the internal player engine has not been * initialized. */ @@ -694,10 +705,10 @@ public class MediaPlayer } private native void _stop() throws IllegalStateException; - + /** * Pauses playback. Call start() to resume. - * + * * @throws IllegalStateException if the internal player engine has not been * initialized. */ @@ -707,20 +718,20 @@ public class MediaPlayer } private native void _pause() throws IllegalStateException; - + /** * Set the low-level power management behavior for this MediaPlayer. This * can be used when the MediaPlayer is not playing through a SurfaceHolder * set with {@link #setDisplay(SurfaceHolder)} and thus can use the * high-level {@link #setScreenOnWhilePlaying(boolean)} feature. - * + * * <p>This function has the MediaPlayer access the low-level power manager * service to control the device's power usage while playing is occurring. * The parameter is a combination of {@link android.os.PowerManager} wake flags. * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK} * permission. * By default, no attempt is made to keep the device awake during playback. - * + * * @param context the Context to use * @param mode the power/wake mode to set * @see android.os.PowerManager @@ -742,14 +753,14 @@ public class MediaPlayer mWakeLock.acquire(); } } - + /** * Control whether we should use the attached SurfaceHolder to keep the * screen on while video playback is occurring. This is the preferred * method over {@link #setWakeMode} where possible, since it doesn't * require that the application have permission for low-level wake lock * access. - * + * * @param screenOn Supply true to keep the screen on, false to allow it * to turn off. */ @@ -759,7 +770,7 @@ public class MediaPlayer updateSurfaceScreenOn(); } } - + private void stayAwake(boolean awake) { if (mWakeLock != null) { if (awake && !mWakeLock.isHeld()) { @@ -771,61 +782,61 @@ public class MediaPlayer mStayAwake = awake; updateSurfaceScreenOn(); } - + private void updateSurfaceScreenOn() { if (mSurfaceHolder != null) { mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake); } } - + /** * Returns the width of the video. - * + * * @return the width of the video, or 0 if there is no video, * no display surface was set, or prepare()/prepareAsync() * have not completed yet */ public native int getVideoWidth(); - + /** * Returns the height of the video. - * + * * @return the height of the video, or 0 if there is no video, * no display surface was set, or prepare()/prepareAsync() * have not completed yet */ public native int getVideoHeight(); - + /** * Checks whether the MediaPlayer is playing. - * + * * @return true if currently playing, false otherwise */ public native boolean isPlaying(); - + /** * Seeks to specified time position. - * + * * @param msec the offset in milliseconds from the start to seek to * @throws IllegalStateException if the internal player engine has not been * initialized */ public native void seekTo(int msec) throws IllegalStateException; - + /** * Gets the current playback position. - * + * * @return the current position in milliseconds */ public native int getCurrentPosition(); - + /** * Gets the duration of the file. - * + * * @return the duration in milliseconds */ public native int getDuration(); - + /** * Releases resources associated with this MediaPlayer object. * It is considered good practice to call this method when you're @@ -845,7 +856,7 @@ public class MediaPlayer } private native void _release(); - + /** * Resets the MediaPlayer to its uninitialized state. After calling * this method, you will have to initialize it again by setting the @@ -857,13 +868,13 @@ public class MediaPlayer // make sure none of the listeners get called anymore mEventHandler.removeCallbacksAndMessages(null); } - + private native void _reset(); - + /** * Sets the audio stream type for this MediaPlayer. See {@link AudioManager} * for a list of stream types. - * + * * @param streamtype the audio stream type * @see android.media.AudioManager */ @@ -871,20 +882,20 @@ public class MediaPlayer /** * Sets the player to be looping or non-looping. - * + * * @param looping whether to loop or not */ public native void setLooping(boolean looping); /** * Checks whether the MediaPlayer is looping or non-looping. - * + * * @return true if the MediaPlayer is currently looping, false otherwise */ public native boolean isLooping(); /** - * Sets the volume on this player. + * Sets the volume on this player. * This API is recommended for balancing the output of audio streams * within an application. Unless you are writing an application to * control user settings, this API should be used in preference to @@ -903,7 +914,7 @@ public class MediaPlayer * @hide */ public native Bitmap getFrameAt(int msec) throws IllegalStateException; - + private native final void native_setup(Object mediaplayer_this); private native final void native_finalize(); @Override @@ -1026,7 +1037,7 @@ public class MediaPlayer { /** * Called when the media file is ready for playback. - * + * * @param mp the MediaPlayer that is ready for playback */ void onPrepared(MediaPlayer mp); @@ -1053,7 +1064,7 @@ public class MediaPlayer { /** * Called when the end of a media source is reached during playback. - * + * * @param mp the MediaPlayer that reached the end of the file */ void onCompletion(MediaPlayer mp); @@ -1080,14 +1091,14 @@ public class MediaPlayer { /** * Called to update status in buffering a media stream. - * + * * @param mp the MediaPlayer the update pertains to * @param percent the percentage (0-100) of the buffer * that has been filled thus far */ void onBufferingUpdate(MediaPlayer mp, int percent); } - + /** * Register a callback to be invoked when the status of a network * stream's buffer has changed. @@ -1100,7 +1111,7 @@ public class MediaPlayer } private OnBufferingUpdateListener mOnBufferingUpdateListener; - + /** * Interface definition of a callback to be invoked indicating * the completion of a seek operation. @@ -1109,23 +1120,23 @@ public class MediaPlayer { /** * Called to indicate the completion of a seek operation. - * + * * @param mp the MediaPlayer that issued the seek operation */ public void onSeekComplete(MediaPlayer mp); } - + /** * Register a callback to be invoked when a seek operation has been * completed. - * + * * @param listener the callback that will be run */ public void setOnSeekCompleteListener(OnSeekCompleteListener listener) { mOnSeekCompleteListener = listener; } - + private OnSeekCompleteListener mOnSeekCompleteListener; /** @@ -1136,25 +1147,25 @@ public class MediaPlayer { /** * Called to indicate the video size - * + * * @param mp the MediaPlayer associated with this callback * @param width the width of the video * @param height the height of the video */ public void onVideoSizeChanged(MediaPlayer mp, int width, int height); } - + /** * Register a callback to be invoked when the video size is * known or updated. - * + * * @param listener the callback that will be run */ public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) { mOnVideoSizeChangedListener = listener; } - + private OnVideoSizeChangedListener mOnVideoSizeChangedListener; /* Do not change these values without updating their counterparts @@ -1166,11 +1177,11 @@ public class MediaPlayer public static final int MEDIA_ERROR_UNKNOWN = 1; /** Media server died. In this case, the application must release the - * MediaPlayer object and instantiate a new one. + * MediaPlayer object and instantiate a new one. * @see android.media.MediaPlayer.OnErrorListener */ public static final int MEDIA_ERROR_SERVER_DIED = 100; - + /** The video is streamed and its container is not valid for progressive * playback i.e the video's index (e.g moov atom) is not at the start of the * file. @@ -1187,7 +1198,7 @@ public class MediaPlayer { /** * Called to indicate an error. - * + * * @param mp the MediaPlayer the error pertains to * @param what the type of error that has occurred: * <ul> @@ -1202,11 +1213,11 @@ public class MediaPlayer */ boolean onError(MediaPlayer mp, int what, int extra); } - + /** * Register a callback to be invoked when an error has happened * during an asynchronous operation. - * + * * @param listener the callback that will be run */ public void setOnErrorListener(OnErrorListener listener) @@ -1251,7 +1262,7 @@ public class MediaPlayer { /** * Called to indicate an info or a warning. - * + * * @param mp the MediaPlayer the info pertains to. * @param what the type of info or warning. * <ul> @@ -1271,7 +1282,7 @@ public class MediaPlayer /** * Register a callback to be invoked when an info/warning is available. - * + * * @param listener the callback that will be run */ public void setOnInfoListener(OnInfoListener listener) diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 5d90e6258207..be4b489eb086 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -125,6 +125,15 @@ public class MediaRecorder public static final int DEFAULT = 0; /** Microphone audio source */ public static final int MIC = 1; + + /** Voice call uplink (Tx) audio source */ + public static final int VOICE_UPLINK = 2; + + /** Voice call downlink (Rx) audio source */ + public static final int VOICE_DOWNLINK = 3; + + /** Voice call uplink + downlink audio source */ + public static final int VOICE_CALL = 4; } /** @@ -155,8 +164,19 @@ public class MediaRecorder public static final int THREE_GPP = 1; /** MPEG4 media file format*/ public static final int MPEG_4 = 2; - /** Raw AMR file format */ + + /** The following formats are audio only .aac or .amr formats **/ + /** @deprecated Deprecated in favor of AMR_NB */ + /** @todo change link when AMR_NB is exposed. Deprecated in favor of {@link MediaRecorder.OutputFormat#AMR_NB} */ public static final int RAW_AMR = 3; + /** @hide AMR NB file format */ + public static final int AMR_NB = 3; + /** @hide AMR WB file format */ + public static final int AMR_WB = 4; + /** @hide AAC ADIF file format */ + public static final int AAC_ADIF = 5; + /** @hide AAC ADTS file format */ + public static final int AAC_ADTS = 6; }; /** @@ -171,7 +191,14 @@ public class MediaRecorder public static final int DEFAULT = 0; /** AMR (Narrowband) audio codec */ public static final int AMR_NB = 1; - //public static final AAC = 2; currently unsupported + /** @hide AMR (Wideband) audio codec */ + public static final int AMR_WB = 2; + /** @hide AAC audio codec */ + public static final int AAC = 3; + /** @hide enhanced AAC audio codec */ + public static final int AAC_PLUS = 4; + /** @hide enhanced AAC plus audio codec */ + public static final int EAAC_PLUS = 5; } /** @@ -189,6 +216,46 @@ public class MediaRecorder public static final int MPEG_4_SP = 3; } + + /** + * @hide Defines the audio sampling rate. This must be set before + * setAudioEncoder() or it will be ignored. + * This parameter is used with + * {@link MediaRecorder#setParameters(String)}. + */ + public final class AudioParamSamplingRate { + /* Do not change these values without updating their counterparts + * in include/media/mediarecorder.h! + */ + private AudioParamSamplingRate() {} + public static final String AUDIO_PARAM_SAMPLING_RATE_KEY = "audio-param-sampling-rate="; + } + + /** + * @hide Defines the audio number of channels. This must be set before + * setAudioEncoder() or it will be ignored. + * This parameter is used with + * {@link MediaRecorder#setParameters(String)}. + */ + public final class AudioParamChannels { + /* Do not change these values without updating their counterparts + * in include/media/mediarecorder.h! + */ + private AudioParamChannels() {} + public static final String AUDIO_PARAM_NUMBER_OF_CHANNELS = "audio-param-number-of-channels="; + } + + /** + * @hide Defines the audio encoding bitrate. This must be set before + * setAudioEncoder() or it will be ignored. + * This parameter is used with + * {@link MediaRecorder#setParameters(String)}. + */ + public final class AudioParamEncodingBitrate{ + private AudioParamEncodingBitrate() {} + public static final String AUDIO_PARAM_ENCODING_BITRATE = "audio-param-encoding-bitrate="; + } + /** * Sets the audio source to be used for recording. If this method is not * called, the output file will not contain an audio track. The source needs @@ -203,6 +270,12 @@ public class MediaRecorder throws IllegalStateException; /** + * Gets the maximum value for audio sources. + * @see android.media.MediaRecorder.AudioSource + */ + public static final int getAudioSourceMax() { return AudioSource.VOICE_CALL; } + + /** * Sets the video source to be used for recording. If this method is not * called, the output file will not contain an video track. The source needs * to be specified before setting recording-parameters or encoders. Call @@ -317,6 +390,16 @@ public class MediaRecorder throws IllegalStateException; /** + * @hide Sets a parameter in the author engine. + * + * @param params the parameter to set. + * @see android.media.MediaRecorder.AudioParamSamplingRate + * @see android.media.MediaRecorder.AudioParamChannels + * @see android.media.MediaRecorder.AudioParamEncodingBitrate + */ + public native void setParameters(String params); + + /** * Pass in the file descriptor of the file to be written. Call this after * setOutputFormat() but before prepare(). * @@ -433,7 +516,7 @@ public class MediaRecorder { /** * Called when an error occurs while recording. - * + * * @param mr the MediaRecorder that encountered the error * @param what the type of error that has occurred: * <ul> @@ -479,7 +562,7 @@ public class MediaRecorder { /** * Called when an error occurs while recording. - * + * * @param mr the MediaRecorder that encountered the error * @param what the type of error that has occurred: * <ul> diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index ae3e181041d6..cccc0fcd53e9 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -53,7 +53,45 @@ import java.util.HashSet; import java.util.Iterator; /** - * Internal service that no-one should use directly. + * Internal service helper that no-one should use directly. + * + * The way the scan currently works is: + * - The Java MediaScannerService creates a MediaScanner (this class), and calls + * MediaScanner.scanDirectories on it. + * - scanDirectories() calls the native processDirectory() for each of the specified directories. + * - the processDirectory() JNI method wraps the provided mediascanner client in a native + * 'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner + * object (which got created when the Java MediaScanner was created). + * - native MediaScanner.processDirectory() (currently part of opencore) calls + * doProcessDirectory(), which recurses over the folder, and calls + * native MyMediaScannerClient.scanFile() for every file whose extension matches. + * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile, + * which calls doScanFile, which after some setup calls back down to native code, calling + * MediaScanner.processFile(). + * - MediaScanner.processFile() calls one of several methods, depending on the type of the + * file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA. + * - each of these methods gets metadata key/value pairs from the file, and repeatedly + * calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java + * counterparts in this file. + * - Java handleStringTag() gathers the key/value pairs that it's interested in. + * - once processFile returns and we're back in Java code in doScanFile(), it calls + * Java MyMediaScannerClient.endFile(), which takes all the data that's been + * gathered and inserts an entry in to the database. + * + * In summary: + * Java MediaScannerService calls + * Java MediaScanner scanDirectories, which calls + * Java MediaScanner processDirectory (native method), which calls + * native MediaScanner processDirectory, which calls + * native MyMediaScannerClient scanFile, which calls + * Java MyMediaScannerClient scanFile, which calls + * Java MediaScannerClient doScanFile, which calls + * Java MediaScanner processFile (native method), which calls + * native MediaScanner processFile, which calls + * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls + * native MyMediaScanner handleStringTag, which calls + * Java MyMediaScanner handleStringTag. + * Once MediaScanner processFile returns, an entry is inserted in to the database. * * {@hide} */ @@ -506,7 +544,10 @@ public class MediaScanner public void handleStringTag(String name, String value) { if (name.equalsIgnoreCase("title") || name.startsWith("title;")) { - mTitle = value.trim(); + // Don't trim() here, to preserve the special \001 character + // used to force sorting. The media provider will trim() before + // inserting the title in to the database. + mTitle = value; } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) { mArtist = value.trim(); } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")) { diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 8f05cec8e501..42edae6937d1 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -176,17 +176,20 @@ public class RingtoneManager { private static final String[] INTERNAL_COLUMNS = new String[] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE, - "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"" + "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"", + MediaStore.Audio.Media.TITLE_KEY }; private static final String[] DRM_COLUMNS = new String[] { DrmStore.Audio._ID, DrmStore.Audio.TITLE, - "\"" + DrmStore.Audio.CONTENT_URI + "\"" + "\"" + DrmStore.Audio.CONTENT_URI + "\"", + DrmStore.Audio.TITLE + " AS " + MediaStore.Audio.Media.TITLE_KEY }; private static final String[] MEDIA_COLUMNS = new String[] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE, - "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"" + "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"", + MediaStore.Audio.Media.TITLE_KEY }; /** @@ -361,7 +364,7 @@ public class RingtoneManager { final Cursor mediaCursor = getMediaRingtones(); return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor }, - MediaStore.MediaColumns.TITLE); + MediaStore.Audio.Media.DEFAULT_SORT_ORDER); } /** diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java index 4b53756db118..e5ee9a31058c 100644 --- a/media/java/android/media/ToneGenerator.java +++ b/media/java/android/media/ToneGenerator.java @@ -19,11 +19,11 @@ package android.media; /** - * This class provides methods to play DTMF tones (ITU-T Recommendation Q.23), - * call supervisory tones (3GPP TS 22.001, CEPT) and proprietary tones (3GPP TS 31.111). + * This class provides methods to play DTMF tones (ITU-T Recommendation Q.23), + * call supervisory tones (3GPP TS 22.001, CEPT) and proprietary tones (3GPP TS 31.111). * Depending on call state and routing options, tones are mixed to the downlink audio - * or output to the speaker phone or headset. - * This API is not for generating tones over the uplink audio path. + * or output to the speaker phone or headset. + * This API is not for generating tones over the uplink audio path. */ public class ToneGenerator { @@ -33,99 +33,99 @@ public class ToneGenerator * List of all available tones: These constants must be kept consistant with * the enum in ToneGenerator C++ class. */ - /** + /** * DTMF tone for key 0: 1336Hz, 941Hz, continuous</p> - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_0 = 0; /** * DTMF tone for key 1: 1209Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_1 = 1; /** * DTMF tone for key 2: 1336Hz, 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_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 - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_4 = 4; /** * DTMF tone for key 5: 1336Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_5 = 5; /** * DTMF tone for key 6: 1477Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_6 = 6; /** * DTMF tone for key 7: 1209Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_7 = 7; /** * DTMF tone for key 8: 1336Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_8 = 8; /** * DTMF tone for key 9: 1477Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_9 = 9; /** * DTMF tone for key *: 1209Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_S = 10; /** * DTMF tone for key #: 1477Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_P = 11; /** * DTMF tone for key A: 1633Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_A = 12; /** * DTMF tone for key B: 1633Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_B = 13; /** * DTMF tone for key C: 1633Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_C = 14; /** * DTMF tone for key D: 1633Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_D = 15; @@ -151,7 +151,7 @@ public class ToneGenerator * Call supervisory tone, Congestion: * CEPT, JAPAN: 425Hz, 200ms ON, 200ms OFF... * ANSI (IS-95): 480Hz+620Hz, 250ms ON, 250ms OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CONGESTION = 18; @@ -159,27 +159,28 @@ public class ToneGenerator * Call supervisory tone, Radio path acknowlegment : * CEPT, ANSI: 425Hz, 200ms ON * JAPAN: 400Hz, 1s ON, 2s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RADIO_ACK = 19; /** * Call supervisory tone, Radio path not available: 425Hz, 200ms ON, 200 OFF 3 bursts - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RADIO_NOTAVAIL = 20; /** * Call supervisory tone, Error/Special info: 950Hz+1400Hz+1800Hz, 330ms ON, 1s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_ERROR = 21; /** * Call supervisory tone, Call Waiting: * CEPT, JAPAN: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... - * ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) - * + * ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, + * (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CALL_WAITING = 22; @@ -187,42 +188,43 @@ public class ToneGenerator * Call supervisory tone, Ring Tone: * CEPT, JAPAN: 425Hz, 1s ON, 4s OFF... * ANSI (IS-95): 440Hz + 480Hz, 2s ON, 4s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RINGTONE = 23; /** * Proprietary tone, general beep: 400Hz+1200Hz, 35ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_BEEP = 24; /** * Proprietary tone, positive acknowlegement: 1200Hz, 100ms ON, 100ms OFF 2 bursts - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_ACK = 25; /** * Proprietary tone, negative acknowlegement: 300Hz+400Hz+500Hz, 400ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_NACK = 26; /** * Proprietary tone, prompt tone: 400Hz+1200Hz, 200ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_PROMPT = 27; /** * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_BEEP2 = 28; /** - * Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, each on for 250 ms + * Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, + * each on for 250 ms * * @see #ToneGenerator(int, int) */ @@ -240,7 +242,8 @@ public class ToneGenerator */ public static final int TONE_SUP_CONGESTION_ABBREV = 31; /** - * Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle + * Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone + * repeated 3 times in a 100 ms on, 100 ms off cycle * * @see #ToneGenerator(int, int) */ @@ -251,7 +254,474 @@ public class ToneGenerator * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_PIP = 33; - + /** + * CDMA Dial tone : 425Hz continuous + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_DIAL_TONE_LITE = 34; + /** + * 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 ... + * + * @see #ToneGenerator(int, int) + */ + 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... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_REORDER = 38; + /** + * + * 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 + * + * @see #ToneGenerator(int, int) + */ + 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; + /** + * + * CDMA answer tone: silent tone - defintion Frequency 0, 0ms ON, 0ms OFF + * @see #ToneGenerator(int, int) + */ + 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 + * + * @see #ToneGenerator(int, int) + */ + 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. + * + * @see #ToneGenerator(int, int) + */ + 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 + * + * @see #ToneGenerator(int, int) + */ + 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; + /** + * + * ISDN Pat5 tone: silent tone + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 = 50; + /** + * + * 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 + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 = 52; + /** + * 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 .... + * + * @see #ToneGenerator(int, int) + */ + 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 .... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_SS = 56; + /** + * 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 .... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_SS = 58; + /** + * 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 ... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_SSL = 60; + /** + * 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 .... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_SS_2 = 62; + /** + * 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 .... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_SS_2 = 64; + /** + * 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 + * + * + * @see #ToneGenerator(int, int) + */ + 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; @@ -261,10 +731,10 @@ public class ToneGenerator /** * ToneGenerator class contructor specifying output stream type and volume. - * + * * @param streamType The streame type used for tone playback (e.g. STREAM_MUSIC). * @param volume The volume of the tone, given in percentage of maximum volume (from 0-100). - * + * */ public ToneGenerator(int streamType, int volume) { native_setup(streamType, volume); @@ -272,7 +742,7 @@ public class ToneGenerator /** * This method starts the playback of a tone of the specified type. - * only one tone can play at a time: if a tone is playing while this method is called, + * only one tone can play at a time: if a tone is playing while this method is called, * this tone is stopped and replaced by the one requested. * @param toneType The type of tone generate chosen from the following list: * <ul> @@ -308,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) */ @@ -328,9 +863,10 @@ 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(); } + @SuppressWarnings("unused") private int mNativeContext; // accessed by native methods - - } diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 707db02fd948..6317fe21b702 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -125,8 +125,8 @@ static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaP return old; } -// If exception is NULL and opStatus is not OK, this method sends an error -// event to the client application; otherwise, if exception is not NULL and +// If exception is NULL and opStatus is not OK, this method sends an error +// event to the client application; otherwise, if exception is not NULL and // opStatus is not OK, this method throws the given exception to the client // application. static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) @@ -198,22 +198,37 @@ android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fil process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); } +static void setVideoSurface(const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz) +{ + jobject surface = env->GetObjectField(thiz, fields.surface); + if (surface != NULL) { + const sp<Surface>& native_surface = get_surface(env, surface); + LOGV("prepare: surface=%p (id=%d)", + native_surface.get(), native_surface->ID()); + mp->setVideoSurface(native_surface); + } +} static void -android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) +android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } - jobject surface = env->GetObjectField(thiz, fields.surface); - if (surface != NULL) { - const sp<Surface>& native_surface = get_surface(env, surface); - LOGV("prepare: surface=%p (id=%d)", - native_surface.get(), native_surface->ID()); - mp->setVideoSurface(native_surface); + setVideoSurface(mp, env, thiz); +} + +static void +android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; } + setVideoSurface(mp, env, thiz); process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); } @@ -228,7 +243,7 @@ android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz) jobject surface = env->GetObjectField(thiz, fields.surface); if (surface != NULL) { const sp<Surface>& native_surface = get_surface(env, surface); - LOGV("prepareAsync: surface=%p (id=%d)", + LOGV("prepareAsync: surface=%p (id=%d)", native_surface.get(), native_surface->ID()); mp->setVideoSurface(native_surface); } @@ -256,7 +271,7 @@ android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } - process_media_player_call( env, thiz, mp->stop(), NULL, NULL ); + process_media_player_call( env, thiz, mp->stop(), NULL, NULL ); } static void @@ -469,6 +484,7 @@ android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) static JNINativeMethod gMethods[] = { {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, + {"_setVideoSurface", "()V", (void *)android_media_MediaPlayer_setVideoSurface}, {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, {"_start", "()V", (void *)android_media_MediaPlayer_start}, diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index cac65d66600a..0273a5aac06b 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; @@ -165,7 +165,7 @@ static void android_media_MediaRecorder_setVideoSource(JNIEnv *env, jobject thiz, jint vs) { LOGV("setVideoSource(%d)", vs); - if (vs < VIDEO_SOURCE_DEFAULT || vs > VIDEO_SOURCE_CAMERA) { + if (vs < VIDEO_SOURCE_DEFAULT || vs >= VIDEO_SOURCE_LIST_END) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video source"); return; } @@ -177,10 +177,11 @@ static void android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as) { LOGV("setAudioSource(%d)", as); - if (as < AUDIO_SOURCE_DEFAULT || as > AUDIO_SOURCE_MIC) { + if (as < AUDIO_SOURCE_DEFAULT || as >= AUDIO_SOURCE_LIST_END) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source"); return; } + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed."); } @@ -201,7 +202,7 @@ static void android_media_MediaRecorder_setVideoEncoder(JNIEnv *env, jobject thiz, jint ve) { LOGV("setVideoEncoder(%d)", ve); - if (ve < VIDEO_ENCODER_DEFAULT || ve > VIDEO_ENCODER_MPEG_4_SP) { + if (ve < VIDEO_ENCODER_DEFAULT || ve >= VIDEO_ENCODER_LIST_END) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video encoder"); return; } @@ -213,7 +214,7 @@ static void android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae) { LOGV("setAudioEncoder(%d)", ae); - if (ae < AUDIO_ENCODER_DEFAULT || ae > AUDIO_ENCODER_AMR_NB) { + if (ae < AUDIO_ENCODER_DEFAULT || ae >= AUDIO_ENCODER_LIST_END) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio encoder"); return; } @@ -222,6 +223,29 @@ android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae) } static void +android_media_MediaRecorder_setParameters(JNIEnv *env, jobject thiz, jstring params) +{ + LOGV("setParameters()"); + if (params == NULL) + { + LOGE("Invalid or empty params string. This parameter will be ignored."); + return; + } + + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + + const char* params8 = env->GetStringUTFChars(params, NULL); + if (params8 == NULL) + { + LOGE("Failed to covert jstring to String8. This parameter will be ignored."); + return; + } + + process_media_recorder_call(env, mr->setParameters(String8(params8)), "java/lang/RuntimeException", "setParameter failed."); + env->ReleaseStringUTFChars(params,params8); +} + +static void android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { LOGV("setOutputFile"); @@ -384,6 +408,7 @@ static JNINativeMethod gMethods[] = { {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, + {"setParameters", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameters}, {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 0812650452bc..ce80f92dbb0e 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -44,23 +44,27 @@ SoundPool::SoundPool(jobject soundPoolRef, int maxChannels, int streamType, int LOGV("SoundPool constructor: maxChannels=%d, streamType=%d, srcQuality=%d", maxChannels, streamType, srcQuality); - if (maxChannels > 32) { - LOGW("App requested %d channels, capped at 32", maxChannels); - maxChannels = 32; + // check limits + mMaxChannels = maxChannels; + if (mMaxChannels < 1) { + mMaxChannels = 1; + } + else if (mMaxChannels > 32) { + mMaxChannels = 32; } + LOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels); mQuit = false; mSoundPoolRef = soundPoolRef; mDecodeThread = 0; - mMaxChannels = maxChannels; mStreamType = streamType; mSrcQuality = srcQuality; mAllocated = 0; mNextSampleID = 0; mNextChannelID = 0; - mChannelPool = new SoundChannel[maxChannels]; - for (int i = 0; i < maxChannels; ++i) { + mChannelPool = new SoundChannel[mMaxChannels]; + for (int i = 0; i < mMaxChannels; ++i) { mChannelPool[i].init(this); mChannels.push_back(&mChannelPool[i]); } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 1720af0a9879..e56efbb2dc22 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -50,7 +50,7 @@ AudioRecord::AudioRecord() } AudioRecord::AudioRecord( - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -61,7 +61,7 @@ AudioRecord::AudioRecord( int notificationFrames) : mStatus(NO_INIT) { - mStatus = set(streamType, sampleRate, format, channelCount, + mStatus = set(inputSource, sampleRate, format, channelCount, frameCount, flags, cbf, user, notificationFrames); } @@ -82,7 +82,7 @@ AudioRecord::~AudioRecord() } status_t AudioRecord::set( - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -104,8 +104,8 @@ status_t AudioRecord::set( return NO_INIT; } - if (streamType == DEFAULT_INPUT) { - streamType = MIC_INPUT; + if (inputSource == DEFAULT_INPUT) { + inputSource = MIC_INPUT; } if (sampleRate == 0) { @@ -157,7 +157,7 @@ status_t AudioRecord::set( // open record channel status_t status; - sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), streamType, + sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), inputSource, sampleRate, format, channelCount, frameCount, @@ -185,7 +185,6 @@ status_t AudioRecord::set( mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->out = 0; - mSampleRate = sampleRate; mFormat = format; // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk->frameCount; @@ -196,11 +195,12 @@ status_t AudioRecord::set( mRemainingFrames = notificationFrames; mUserData = user; // TODO: add audio hardware input latency here - mLatency = (1000*mFrameCount) / mSampleRate; + mLatency = (1000*mFrameCount) / sampleRate; mMarkerPosition = 0; mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; + mInputSource = (uint8_t)inputSource; return NO_ERROR; } @@ -217,11 +217,6 @@ uint32_t AudioRecord::latency() const return mLatency; } -uint32_t AudioRecord::sampleRate() const -{ - return mSampleRate; -} - int AudioRecord::format() const { return mFormat; @@ -242,6 +237,11 @@ int AudioRecord::frameSize() const return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); } +int AudioRecord::inputSource() const +{ + return (int)mInputSource; +} + // ------------------------------------------------------------------------- status_t AudioRecord::start() @@ -315,6 +315,11 @@ bool AudioRecord::stopped() const return !mActive; } +uint32_t AudioRecord::getSampleRate() +{ + return mCblk->sampleRate; +} + status_t AudioRecord::setMarkerPosition(uint32_t marker) { if (mCbf == 0) return INVALID_OPERATION; diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 289bd7552cbb..b2c067b1290f 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -243,7 +243,6 @@ status_t AudioTrack::set( mCblk->volume[0] = mCblk->volume[1] = 0x1000; mVolume[LEFT] = 1.0f; mVolume[RIGHT] = 1.0f; - mSampleRate = sampleRate; mStreamType = streamType; mFormat = format; mChannelCount = channelCount; @@ -254,7 +253,7 @@ status_t AudioTrack::set( mNotificationFrames = notificationFrames; mRemainingFrames = notificationFrames; mUserData = user; - mLatency = afLatency + (1000*mFrameCount) / mSampleRate; + mLatency = afLatency + (1000*mFrameCount) / sampleRate; mLoopCount = 0; mMarkerPosition = 0; mMarkerReached = false; @@ -281,11 +280,6 @@ int AudioTrack::streamType() const return mStreamType; } -uint32_t AudioTrack::sampleRate() const -{ - return mSampleRate; -} - int AudioTrack::format() const { return mFormat; @@ -438,24 +432,23 @@ void AudioTrack::getVolume(float* left, float* right) *right = mVolume[RIGHT]; } -void AudioTrack::setSampleRate(int rate) +status_t AudioTrack::setSampleRate(int rate) { int afSamplingRate; if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) { - return; + return NO_INIT; } // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (rate <= 0) rate = 1; - if (rate > afSamplingRate*2) rate = afSamplingRate*2; - if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE; - mCblk->sampleRate = (uint16_t)rate; + mCblk->sampleRate = rate; + return NO_ERROR; } uint32_t AudioTrack::getSampleRate() { - return uint32_t(mCblk->sampleRate); + return mCblk->sampleRate; } status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) @@ -866,7 +859,7 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const result.append(buffer); snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", mSampleRate, mStatus, mMuted); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", (mCblk == 0) ? 0 : mCblk->sampleRate, mStatus, mMuted); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 52bd7d4ebb60..eeaa54fb4e24 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -99,7 +99,7 @@ public: virtual sp<IAudioRecord> openRecord( pid_t pid, - int streamType, + int inputSource, uint32_t sampleRate, int format, int channelCount, @@ -110,7 +110,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32(pid); - data.writeInt32(streamType); + data.writeInt32(inputSource); data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelCount); @@ -384,14 +384,14 @@ status_t BnAudioFlinger::onTransact( case OPEN_RECORD: { CHECK_INTERFACE(IAudioFlinger, data, reply); pid_t pid = data.readInt32(); - int streamType = data.readInt32(); + int inputSource = data.readInt32(); uint32_t sampleRate = data.readInt32(); int format = data.readInt32(); int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); uint32_t flags = data.readInt32(); status_t status; - sp<IAudioRecord> record = openRecord(pid, streamType, + sp<IAudioRecord> record = openRecord(pid, inputSource, sampleRate, format, channelCount, bufferCount, flags, &status); reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index f37519f42bff..f18765a10843 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -164,6 +164,7 @@ public: status_t setVolume(float leftVolume, float rightVolume) { Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); data.writeFloat(leftVolume); data.writeFloat(rightVolume); remote()->transact(SET_VOLUME, data, &reply); @@ -261,6 +262,7 @@ status_t BnMediaPlayer::onTransact( return NO_ERROR; } break; case SET_VOLUME: { + CHECK_INTERFACE(IMediaPlayer, data, reply); reply->writeInt32(setVolume(data.readFloat(), data.readFloat())); return NO_ERROR; } break; diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 370e3fbe778b..01cdb6cf808f 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -195,4 +195,3 @@ status_t BnMediaPlayerService::onTransact( // ---------------------------------------------------------------------------- }; // namespace android - diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 81ee92c5a121..c22cd53d1d4a 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/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 809316a9f7b9..24e3e6f23432 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -503,7 +503,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) // TODO: In the future, we might be on the same thread if the app is // running in the same process as the media server. In that case, // this will deadlock. - // + // // The threadId hack below works around this for the care of prepare // and seekTo within the same process. // FIXME: Remember, this is a hack, it's not even a hack that is applied @@ -511,7 +511,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) if (mLockThreadId != getThreadId()) { mLock.lock(); locked = true; - } + } if (mPlayer == 0) { LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 6b26faf73613..5093f0e81ef5 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -180,7 +180,7 @@ status_t MediaRecorder::setOutputFormat(int of) LOGE("setOutputFormat called in an invalid state: %d", mCurrentState); return INVALID_OPERATION; } - if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_RAW_AMR) { + if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_AUDIO_ONLY_START) { //first non-video output format LOGE("output format (%d) is meant for audio recording only and incompatible with video recording", of); return INVALID_OPERATION; } @@ -345,7 +345,7 @@ status_t MediaRecorder::setVideoFrameRate(int frames_per_second) } if (!mIsVideoSourceSet) { LOGE("try to set video frame rate without setting video source first"); - return INVALID_OPERATION; + return INVALID_OPERATION; } status_t ret = mMediaRecorder->setVideoFrameRate(frames_per_second); @@ -475,7 +475,7 @@ status_t MediaRecorder::stop() mCurrentState = MEDIA_RECORDER_ERROR; return ret; } - + // FIXME: // stop and reset are semantically different. // We treat them the same for now, and will change this in the future. @@ -492,7 +492,7 @@ status_t MediaRecorder::reset() LOGE("media recorder is not initialized yet"); return INVALID_OPERATION; } - + doCleanUp(); status_t ret = UNKNOWN_ERROR; switch(mCurrentState) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index e0d29474dc2f..8bc410cbbe91 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -37,6 +37,7 @@ namespace android { const char* cameraPermission = "android.permission.CAMERA"; +const char* recordAudioPermission = "android.permission.RECORD_AUDIO"; static bool checkPermission(const char* permissionString) { #ifndef HAVE_ANDROID_OS @@ -86,6 +87,9 @@ status_t MediaRecorderClient::setVideoSource(int vs) status_t MediaRecorderClient::setAudioSource(int as) { LOGV("setAudioSource(%d)", as); + if (!checkPermission(recordAudioPermission)) { + return PERMISSION_DENIED; + } Mutex::Autolock lock(mLock); if (mRecorder == NULL) { LOGE("recorder is not initialized"); diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index 14fd6cedcfad..7f0ef217f2b1 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -345,9 +345,6 @@ status_t VorbisPlayer::reset() { LOGV("reset\n"); Mutex::Autolock l(mMutex); - if (mState != STATE_OPEN) { - return NO_ERROR; - } return reset_nosync(); } @@ -355,10 +352,13 @@ status_t VorbisPlayer::reset() status_t VorbisPlayer::reset_nosync() { // close file - ov_clear(&mVorbisFile); // this also closes the FILE if (mFile != NULL) { - LOGV("OOPS! Vorbis didn't close the file"); - fclose(mFile); + ov_clear(&mVorbisFile); // this also closes the FILE + if (mFile != NULL) { + LOGV("OOPS! Vorbis didn't close the file"); + fclose(mFile); + mFile = NULL; + } } mState = STATE_ERROR; diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index d0ff9ce01764..690822013a68 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <uses-library android:name="android.test.runner" /> <activity android:label="@string/app_name" diff --git a/media/tests/MediaFrameworkTest/res/raw/testmidi.mid b/media/tests/MediaFrameworkTest/res/raw/testmidi.mid Binary files differindex df84e2092c76..d4ead5314181 100644 --- a/media/tests/MediaFrameworkTest/res/raw/testmidi.mid +++ b/media/tests/MediaFrameworkTest/res/raw/testmidi.mid diff --git a/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3 b/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3 Binary files differindex 89c44b049087..b7d69f893d3d 100644 --- a/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3 +++ b/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3 diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 760b6b5134e8..e76967d921e6 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -23,27 +23,27 @@ package com.android.mediaframeworktest; * */ public class MediaNames { - + //A directory to hold all kinds of media files + public static final String MEDIA_SAMPLE_POOL = "/sdcard/media_api/samples/"; //Audio files - public static final String MP3CBR = "/sdcard/media_api/music/MP3CBR.mp3"; - public static final String MP3VBR = "/sdcard/media_api/music/MP3VBR.mp3"; + public static final String MP3CBR = "/sdcard/media_api/music/MP3_256kbps_2ch.mp3"; + public static final String MP3VBR = "/sdcard/media_api/music/MP3_256kbps_2ch_VBR.mp3"; public static final String SHORTMP3 = "/sdcard/media_api/music/SHORTMP3.mp3"; - public static final String MIDI = "/sdcard/media_api/music/MIDI.mid"; + public static final String MIDI = "/sdcard/media_api/music/ants.mid"; public static final String WMA9 = "/sdcard/media_api/music/WMA9.wma"; public static final String WMA10 = "/sdcard/media_api/music/WMA10.wma"; - public static final String WAV = "/sdcard/media_api/music/complicated_wav.wav"; - public static final String AMR = "/sdcard/media_api/music/AMRNB.amr"; - public static final String OGG = "/sdcard/media_api/music/Mists_of_Time-4T.ogg"; - public static final String OGGSHORT = "/sdcard/media_api/music/Skippy.ogg"; + public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav"; + public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr"; + public static final String OGG = "/sdcard/media_api/music/Revelation.ogg"; - public static final int MP3CBR_LENGTH = 231116; - public static final int MP3VBR_LENGTH = 126407; + public static final int MP3CBR_LENGTH = 71000; + public static final int MP3VBR_LENGTH = 71000; public static final int SHORTMP3_LENGTH = 286; - public static final int MIDI_LENGTH = 210528; + public static final int MIDI_LENGTH = 17000; public static final int WMA9_LENGTH = 126559; public static final int WMA10_LENGTH = 126559; - public static final int AMR_LENGTH = 126540; - public static final int OGG_LENGTH = 40000; + public static final int AMR_LENGTH = 37000; + public static final int OGG_LENGTH = 4000; public static final int SEEK_TIME = 10000; public static final long PAUSE_WAIT_TIME = 3000; @@ -60,29 +60,21 @@ public class MediaNames { //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp"; //local video - public static final String VIDEO_MP4 = "/sdcard/media_api/video/gingerkids.MP4"; + public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4"; public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp"; public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp"; public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp"; - public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_AAC.3gp"; - public static final String VIDEO_H263_AMR = "/sdcard/media_api/video/H263_AMR.3gp"; - public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_AAC.3gp"; - public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_AMR.3gp"; + public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp"; + public static final String VIDEO_H263_AMR = "/sdcard/media_api/video/H263_56_AMRNB_6.3gp"; + public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp"; + public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp"; public static final String VIDEO_WMV = "/sdcard/media_api/video/bugs.wmv"; - public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/h263_qcif_30fps.3gp"; - public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/mpeg4_qvga_24fps.3gp"; + public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp"; + public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp"; //ringtone public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3"; - - //streaming mp3 - public static final String STREAM_LARGE_MP3 = - "http://wms.pv.com:7070/MediaDownloadContent/mp3/BuenaVista_04_Pueblo_Nuevo.mp3"; - public static final String STREAM_SMALL_MP3 = - "http://wms.pv.com:7070/MediaDownloadContent/mp3/ID3V2_TestFile.mp3"; - public static final String STREAM_REGULAR_MP3 = - "http://wms.pv.com:7070/MediaDownloadContent/mp3/ElectricCosmo.mp3"; - + //streaming mp3 public static final String STREAM_MP3_1 = "http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3"; @@ -509,4 +501,6 @@ public class MediaNames { "http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3"; public static final String STREAM_MPEG4_QVGA_128k = "http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp"; + public static final int STREAM_H264_480_360_1411k_DURATION = 46000; + public static final int VIDEO_H263_AAC_DURATION = 501000; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java index 12eacd3402b9..ae9e102c10b4 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java @@ -19,6 +19,7 @@ package com.android.mediaframeworktest; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; import com.android.mediaframeworktest.stress.MediaRecorderStressTest; +import com.android.mediaframeworktest.stress.MediaPlayerStressTest; import junit.framework.TestSuite; @@ -28,6 +29,7 @@ public class MediaRecorderStressTestRunner extends InstrumentationTestRunner { public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(MediaRecorderStressTest.class); + suite.addTestSuite(MediaPlayerStressTest.class); return suite; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java index d9e17ea64363..0c0974c37443 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java @@ -32,8 +32,14 @@ import android.os.Looper; import android.os.SystemClock; import android.util.Log; +import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.io.FileOutputStream; +import java.util.Random; /** * Junit / Instrumentation test case for the media player api @@ -50,8 +56,9 @@ public class CodecTest { private static final Object lock = new Object(); private static final Object prepareDone = new Object(); private static final Object videoSizeChanged = new Object(); + private static final Object onCompletion = new Object(); private static boolean onPrepareSuccess = false; - + private static boolean onCompleteSuccess = false; public static String printCpuInfo(){ String cm = "dumpsys cpuinfo"; @@ -78,7 +85,9 @@ public class CodecTest { try{ mp.setDataSource(filePath); mp.prepare(); - }catch (Exception e){} + }catch (Exception e){ + Log.v(TAG, e.toString()); + } int duration = mp.getDuration(); Log.v(TAG, "Duration " + duration); mp.release(); @@ -527,7 +536,6 @@ public class CodecTest { //Verify the thumbnail Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath); - outputWidth = outThumbnail.getWidth(); outputHeight = outThumbnail.getHeight(); goldenHeight = goldenBitmap.getHeight(); @@ -537,15 +545,18 @@ public class CodecTest { if ((outputWidth != goldenWidth) || (outputHeight != goldenHeight)) return false; - //Check one line of pixel - int x = goldenHeight/2; - for (int j=0; j<goldenWidth; j++){ - if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)){ + // Check half line of pixel + int x = goldenHeight / 2; + for (int j = 1; j < goldenWidth / 2; j++) { + if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)) { Log.v(TAG, "pixel = " + goldenBitmap.getPixel(x, j)); - return false; + return false; } - } - }catch (Exception e){} + } + }catch (Exception e){ + Log.v(TAG, e.toString()); + return false; + } return true; } @@ -725,8 +736,75 @@ public class CodecTest { } return onPrepareSuccess; } - - - -} + static MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() { + public void onCompletion(MediaPlayer mp) { + synchronized (onCompletion) { + Log.v(TAG, "notify the completion callback"); + onCompletion.notify(); + onCompleteSuccess = true; + } + } + }; + + // For each media file, forward twice and backward once, then play to the end + public static boolean playMediaSamples(String filePath) throws Exception { + int duration = 0; + int curPosition = 0; + int nextPosition = 0; + int waittime = 0; + Random r = new Random(); + initializeMessageLooper(); + synchronized (lock) { + try { + lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "looper was interrupted."); + return false; + } + } + try { + mMediaPlayer.setOnCompletionListener(mCompletionListener); + Log.v(TAG, "playMediaSamples: sample file name " + filePath); + mMediaPlayer.setDataSource(filePath); + mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); + mMediaPlayer.prepare(); + duration = mMediaPlayer.getDuration(); + Log.v(TAG, "playMediaSamples: duration = " + duration); + // start to play + mMediaPlayer.start(); + // randomly play for time within (0, duration/3) + Thread.sleep(r.nextInt(duration/3)); + mMediaPlayer.pause(); + Log.v(TAG, "playMediaSamples: current position after pause: " + + mMediaPlayer.getCurrentPosition()); + // seek to position (0, 2/3*duration) + nextPosition = mMediaPlayer.getCurrentPosition() + r.nextInt(duration/3); + mMediaPlayer.seekTo(nextPosition); + Log.v(TAG, "playMediaSamples: current position after the first seek:" + + mMediaPlayer.getCurrentPosition()); + // play for another short time + mMediaPlayer.start(); + Thread.sleep(r.nextInt(duration/6)); + Log.v(TAG, "playMediaSamples: position after the second play:" + + mMediaPlayer.getCurrentPosition()); + // seek to a random position (0, duration) + mMediaPlayer.seekTo(r.nextInt(duration)); + Log.v(TAG, "playMediaSamples: current position after the second seek:" + + mMediaPlayer.getCurrentPosition()); + waittime = duration - mMediaPlayer.getCurrentPosition(); + synchronized(onCompletion){ + try { + onCompletion.wait(waittime + 30000); + }catch (Exception e) { + Log.v(TAG, "playMediaSamples are interrupted"); + return false; + } + } + terminateMessageLooper(); + }catch (Exception e) { + Log.v(TAG, "playMediaSamples:" + e.getMessage()); + } + return onCompleteSuccess; + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 8be7058babf7..ea42f53155df 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -26,6 +26,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.Suppress; +import java.io.File; + /** * Junit / Instrumentation test case for the media player api @@ -78,7 +80,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI getDuration", duratoinWithinTolerence); } - @Suppress @MediumTest public void testWMA9GetDuration() throws Exception { int duration = CodecTest.getDuration(MediaNames.WMA9); @@ -120,7 +121,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI GetCurrentPosition", currentPosition); } - @Suppress @LargeTest public void testWMA9GetCurrentPosition() throws Exception { boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); @@ -158,7 +158,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI Pause", isPaused); } - @Suppress @LargeTest public void testWMA9Pause() throws Exception { boolean isPaused = CodecTest.pause(MediaNames.WMA9); @@ -230,7 +229,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI setLooping", isLoop); } - @Suppress @LargeTest public void testWMA9SetLooping() throws Exception { boolean isLoop = CodecTest.setLooping(MediaNames.WMA9); @@ -269,7 +267,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI seekTo", isLoop); } - @Suppress @LargeTest public void testWMA9SeekTo() throws Exception { boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); @@ -308,7 +305,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra boolean isEnd = CodecTest.seekToEnd(MediaNames.MIDI); assertTrue("MIDI seekToEnd", isEnd); } - + @Suppress @LargeTest public void testWMA9SeekToEnd() throws Exception { @@ -386,7 +383,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("H264AMR SeekTo", isSeek); } - @Suppress @LargeTest public void testVideoWMVSeekTo() throws Exception { boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); @@ -408,7 +404,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra //Play a mid file which the duration is around 210 seconds @LargeTest public void testMidiResources() throws Exception { - boolean midiResources = CodecTest.resourcesPlayback(MediaFrameworkTest.midiafd,180000); + boolean midiResources = CodecTest.resourcesPlayback(MediaFrameworkTest.midiafd,16000); assertTrue("Play midi from resources", midiResources); } @@ -420,7 +416,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @MediumTest public void testPrepareAsyncReset() throws Exception { - boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_LARGE_MP3); + boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_MP3); assertTrue("PrepareAsync Reset", isReset); } @@ -456,4 +452,29 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, true); assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess); } + + //Provide a tool to play all kinds of media files in a directory + @Suppress + @LargeTest + public void testMediaSamples() throws Exception { + // load directory files + boolean onCompleteSuccess = false; + File dir = new File(MediaNames.MEDIA_SAMPLE_POOL); + String[] children = dir.list(); + if (children == null) { + Log.v("MediaPlayerApiTest:testMediaSamples", "dir is empty"); + return; + } else { + for (int i = 0; i < children.length; i++) { + //Get filename of directory + String filename = children[i]; + Log.v("MediaPlayerApiTest", + "testMediaSamples: file to be played: " + + dir + "/" + filename); + onCompleteSuccess = + CodecTest.playMediaSamples(dir + "/" + filename); + assertTrue("testMediaSamples", onCompleteSuccess); + } + } + } } 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 2f0173da8db2..84058f5401ec 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -54,20 +54,16 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi 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 H263_VIDEO_PLAYBACK_MEMOUT = - "/sdcard/h263VideoPlaybackMemOut.txt"; - private static final String H264_VIDEO_PLAYBACK_MEMOUT = - "/sdcard/h264VideoPlaybackMemOut.txt"; - private static final String WMV_VIDEO_PLAYBACK_MEMOUT = - "/sdcard/WmvVideoPlaybackMemOut.txt"; - private static final String H263_VIDEO_ONLY_RECORD_MEMOUT = - "/sdcard/recordH263VideoOnlyMemOut.txt"; - private static final String MP4_VIDEO_ONLY_RECORD_MEMOUT = - "/sdcard/recordMPEG4VideoOnlyMemOut.txt"; - private static final String H263_VIDEO_AUDIO_RECORD_MEMOUT = - "/sdcard/recordVideoH263AudioMemOut.txt"; - private static final String AUDIO_ONLY_RECORD_MEMOUT = - "/sdcard/recordAudioOnlyMemOut.txt"; + private static final String MEDIA_MEMORY_OUTPUT = + "/sdcard/mediaMemOutput.txt"; + + //the tolerant memory leak + private static final int MAX_ACCEPTED_MEMORY_LEAK_KB = 150; + + private static int mStartMemory = 0; + private static int mEndMemory = 0; + private static int mStartPid = 0; + private static int mEndPid = 0; public MediaPlayerPerformance() { @@ -253,8 +249,21 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi //Write the ps output to the file public void getMemoryWriteToLog(Writer output) { + String memusage = null; + memusage = captureMediaserverInfo(); + Log.v(TAG, memusage); + try { + //Write to file output + output.write(memusage); + } catch (Exception e) { + e.toString(); + } + } + + public String captureMediaserverInfo() { String cm = "ps mediaserver"; String memoryUsage = null; + int ch; try { Process p = Runtime.getRuntime().exec(cm); @@ -267,18 +276,48 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi } catch (IOException e) { Log.v(TAG, e.toString()); } - String[] poList = memoryUsage.split("\r|\n|\r\n"); String memusage = poList[1].concat("\n"); - Log.v(TAG, memusage); - try { - //Write to file output - output.write(memusage); - } catch (Exception e) { - e.toString(); - } + return memusage; } + public int getMediaserverPid(){ + String memoryUsage = null; + int pidvalue = 0; + memoryUsage = captureMediaserverInfo(); + String[] poList2 = memoryUsage.split("\t|\\s+"); + String pid = poList2[1]; + pidvalue = Integer.parseInt(pid); + Log.v(TAG, "PID = " + pidvalue); + return pidvalue; + } + + public int getMediaserverVsize(){ + String memoryUsage = captureMediaserverInfo(); + String[] poList2 = memoryUsage.split("\t|\\s+"); + String vsize = poList2[3]; + int vsizevalue = Integer.parseInt(vsize); + Log.v(TAG, "VSIZE = " + vsizevalue); + return vsizevalue; + } + + public boolean validateMemoryResult (int startPid, int startMemory, Writer output) throws Exception { + mEndPid = getMediaserverPid(); + mEndMemory = getMediaserverVsize(); + + //Write the total memory different into the output file + output.write("The total diff = " + (mEndMemory - startMemory)); + output.write("\n\n"); + //mediaserver crash + if (startPid != mEndPid){ + output.write("mediaserver died. Test failed\n"); + return false; + } + //memory leak greter than the tolerant + if ((mEndMemory - startMemory) > MAX_ACCEPTED_MEMORY_LEAK_KB ) + return false; + return true; + } @Suppress public void testWmaParseTime() throws Exception { @@ -290,87 +329,151 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi // Test case 1: Capture the memory usage after every 20 h263 playback @LargeTest public void testH263VideoPlaybackMemoryUsage() throws Exception { - File h263MemoryOut = new File(H263_VIDEO_PLAYBACK_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(h263MemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File h263MemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(h263MemoryOut, true)); + output.write("H263 Video Playback Only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("H263 playback memory test", memoryResult); } // Test case 2: Capture the memory usage after every 20 h264 playback @LargeTest public void testH264VideoPlaybackMemoryUsage() throws Exception { - File h264MemoryOut = new File(H264_VIDEO_PLAYBACK_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(h264MemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File h264MemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(h264MemoryOut, true)); + output.write("H264 Video Playback only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { mediaStressPlayback(MediaNames.VIDEO_H264_AMR); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("H264 playback memory test", memoryResult); } // Test case 3: Capture the memory usage after each 20 WMV playback @LargeTest public void testWMVVideoPlaybackMemoryUsage() throws Exception { - File wmvMemoryOut = new File(WMV_VIDEO_PLAYBACK_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(wmvMemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File wmvMemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(wmvMemoryOut, true)); + output.write("WMV video playback only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { mediaStressPlayback(MediaNames.VIDEO_WMV); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("wmv playback memory test", memoryResult); } // Test case 4: Capture the memory usage after every 20 video only recorded @LargeTest public void testH263RecordVideoOnlyMemoryUsage() throws Exception { - File videoH263RecordOnlyMemoryOut = new File(H263_VIDEO_ONLY_RECORD_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File videoH263RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(videoH263RecordOnlyMemoryOut, true)); + output.write("H263 video record only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("H263 record only memory test", memoryResult); } // Test case 5: Capture the memory usage after every 20 video only recorded @LargeTest public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception { - File videoMp4RecordOnlyMemoryOut = new File(MP4_VIDEO_ONLY_RECORD_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File videoMp4RecordOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(videoMp4RecordOnlyMemoryOut, true)); + output.write("MPEG4 video record only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.MPEG_4_SP, MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, true); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("mpeg4 record only memory test", memoryResult); } - // Test case 6: Capture the memory usage after every 20 video and audio recorded + // Test case 6: Capture the memory usage after every 20 video and audio + // recorded @LargeTest public void testRecordVidedAudioMemoryUsage() throws Exception { - File videoRecordAudioMemoryOut = new File(H263_VIDEO_AUDIO_RECORD_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File videoRecordAudioMemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(videoRecordAudioMemoryOut, true)); + output.write("Audio and h263 video record\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { stressVideoRecord(20, 352, 288, MediaRecorder.VideoEncoder.H263, MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("H263 audio video record memory test", memoryResult); } // Test case 7: Capture the memory usage after every 20 audio only recorded @LargeTest public void testRecordAudioOnlyMemoryUsage() throws Exception { - File audioOnlyMemoryOut = new File(AUDIO_ONLY_RECORD_MEMOUT); - Writer output = new BufferedWriter(new FileWriter(audioOnlyMemoryOut)); + boolean memoryResult = false; + mStartPid = getMediaserverPid(); + mStartMemory = getMediaserverVsize(); + + File audioOnlyMemoryOut = new File(MEDIA_MEMORY_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(audioOnlyMemoryOut, true)); + output.write("Audio record only\n"); + getMemoryWriteToLog(output); for (int i = 0; i < NUM_STRESS_LOOP; i++) { stressAudioRecord(MediaNames.RECORDER_OUTPUT); getMemoryWriteToLog(output); } + output.write("\n"); + memoryResult = validateMemoryResult(mStartPid, mStartMemory, output); output.close(); + assertTrue("audio record only memory test", memoryResult); } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java new file mode 100644 index 000000000000..5e213d704bbe --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.stress; + +import com.android.mediaframeworktest.MediaFrameworkTest; + +import android.hardware.Camera; +import android.media.MediaPlayer; +import android.media.MediaRecorder; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.SurfaceHolder; + +import com.android.mediaframeworktest.MediaNames; + +import java.util.Random; + +/** + * Junit / Instrumentation test case for the media player + */ +public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaPlayerStressTest"; + private MediaRecorder mRecorder; + private Camera mCamera; + + private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY = 10; + private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY_SHORT = 5; + private static final int NUMBER_OF_STRESS_LOOPS = 1000; + private static final int PLAYBACK_END_TOLERANCE = 5000; + private static final int WAIT_UNTIL_PLAYBACK_FINISH = 515000 ; + + public MediaPlayerStressTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @LargeTest + public void testStressHWDecoderRelease() throws Exception { + SurfaceHolder mSurfaceHolder; + long randomseed = System.currentTimeMillis(); + Random generator = new Random(randomseed); + Log.v(TAG, "Random seed: " + randomseed); + int video_duration = MediaNames.STREAM_H264_480_360_1411k_DURATION; + int random_play_time; + + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) { + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k); + mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); + mp.prepare(); + mp.start(); + // seek and play + for (int j = 0; j < generator.nextInt(10); j++) { + random_play_time = + generator.nextInt(MediaNames.STREAM_H264_480_360_1411k_DURATION / 2); + Log.v(TAG, "Play time = " + random_play_time); + Thread.sleep(random_play_time); + int seek_time = MediaNames.STREAM_H264_480_360_1411k_DURATION / 2; + Log.v(TAG, "Seek time = " + seek_time); + mp.seekTo(seek_time); + } + mp.release(); + } + + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } + + @LargeTest + public void testStressGetCurrentPosition() throws Exception { + SurfaceHolder mSurfaceHolder; + long randomseed = System.currentTimeMillis(); + Random generator = new Random(randomseed); + Log.v(TAG, "Random seed: " + randomseed); + int video_duration = MediaNames.VIDEO_H263_AAC_DURATION; + int random_play_time = 0; + int random_seek_time = 0; + + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) { + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(MediaNames.VIDEO_H263_AMR); + mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); + mp.prepare(); + mp.start(); + // Random seek and play + for (int j = 0; j < generator.nextInt(10); j++) { + random_play_time = + generator.nextInt(video_duration / 2); + Log.v(TAG, "Play time = " + random_play_time); + Thread.sleep(random_play_time); + random_seek_time = + generator.nextInt(video_duration / 2); + Log.v(TAG, "Seek time = " + random_seek_time); + mp.seekTo(random_seek_time); + } + //wait until the movie finish and check the current position + //Make sure the wait time is long enough + long wait_until_playback_finish = video_duration - random_seek_time + PLAYBACK_END_TOLERANCE * 2; + Thread.sleep(wait_until_playback_finish); + Log.v(TAG, "CurrentPosition = " + mp.getCurrentPosition()); + if ( mp.isPlaying() || mp.getCurrentPosition() > (video_duration + PLAYBACK_END_TOLERANCE)){ + assertTrue("Current PlayTime greater than duration", false); + } + mp.release(); + } + + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } +} + 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 dbf937cf9e0d..69e93a1bc164 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/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java index 4e365ef86c2e..36b6ea077571 100644 --- a/opengl/java/com/google/android/gles_jni/GLImpl.java +++ b/opengl/java/com/google/android/gles_jni/GLImpl.java @@ -19,6 +19,12 @@ package com.google.android.gles_jni; +import android.app.ActivityThread; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.os.Build; +import android.util.Log; + import java.nio.Buffer; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10Ext; @@ -43,9 +49,30 @@ public class GLImpl implements GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack { public GLImpl() { } - public void glGetPointerv(int pname, java.nio.Buffer[] params) { - throw new UnsupportedOperationException("glGetPointerv"); - } + public void glGetPointerv(int pname, java.nio.Buffer[] params) { + throw new UnsupportedOperationException("glGetPointerv"); + } + + private static boolean allowIndirectBuffers(String appName) { + boolean result = false; + int version = 0; + IPackageManager pm = ActivityThread.getPackageManager(); + try { + ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0); + if (applicationInfo != null) { + version = applicationInfo.targetSdkVersion; + } + } catch (android.os.RemoteException e) { + // ignore + } + Log.e("OpenGLES", String.format( + "Application %s (SDK target %d) called a GL11 Pointer method with an indirect Buffer.", + appName, version)); + if (version <= Build.VERSION_CODES.CUPCAKE) { + result = true; + } + return result; + } // C function void glActiveTexture ( GLenum texture ) diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp index 8fa7566aacc0..3e9c6a542709 100644 --- a/opengl/libagl/array.cpp +++ b/opengl/libagl/array.cpp @@ -951,6 +951,8 @@ void compileElement__generic(ogles_context_t* c, v->index = first; first &= vertex_cache_t::INDEX_MASK; const GLubyte* vp = c->arrays.vertex.element(first); + v->obj.z = 0; + v->obj.w = 0x10000; c->arrays.vertex.fetch(c, v->obj.v, vp); c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj); c->arrays.perspective(c, v); @@ -966,6 +968,8 @@ void compileElements__generic(ogles_context_t* c, do { v->flags = 0; v->index = first++; + v->obj.z = 0; + v->obj.w = 0x10000; c->arrays.vertex.fetch(c, v->obj.v, vp); c->arrays.mvp_transform(mvp, &v->clip, &v->obj); c->arrays.perspective(c, v); diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp index 25c41d0c5b9d..8ae32cc0f0e1 100644 --- a/opengl/libagl/light.cpp +++ b/opengl/libagl/light.cpp @@ -38,13 +38,14 @@ static void lightVertex(ogles_context_t* c, vertex_t* v); static void lightVertexMaterial(ogles_context_t* c, vertex_t* v); static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s); -static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b); static __attribute__((noinline)) void vnorm3(GLfixed* d, const GLfixed* a); static inline void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a); +static inline void vss3(GLfixed* d, + const GLfixed* m, GLfixed s, const GLfixed* a); static inline void vmla3(GLfixed* d, const GLfixed* m0, const GLfixed* m1, const GLfixed* a); static inline void vmul3(GLfixed* d, @@ -151,18 +152,10 @@ void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { } static inline -void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) { - const GLfixed wa = a[3]; - const GLfixed wb = b[3]; - if (ggl_likely(wa == wb)) { - d[0] = a[0] - b[0]; - d[1] = a[1] - b[1]; - d[2] = a[2] - b[2]; - } else { - d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa)); - d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa)); - d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa)); - } +void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { + d[0] = gglMulSubx(m[0], s, a[0]); + d[1] = gglMulSubx(m[1], s, a[1]); + d[2] = gglMulSubx(m[2], s, a[2]); } static inline @@ -227,7 +220,7 @@ static inline void validate_light_mvi(ogles_context_t* c) const int i = 31 - gglClz(en); en &= ~(1<<i); light_t& l = c->lighting.lights[i]; - c->transforms.mvui.point3(&c->transforms.mvui, + c->transforms.mvui.point4(&c->transforms.mvui, &l.objPosition, &l.position); vnorm3(l.normalizedObjPosition.v, l.objPosition.v); } @@ -318,6 +311,11 @@ void lightVertexMaterial(ogles_context_t* c, vertex_t* v) vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); + // this is just a flag to tell if we have a specular component + l.implicitSpecular.v[3] = + l.implicitSpecular.r | + l.implicitSpecular.g | + l.implicitSpecular.b; } // emission and ambient for the whole scene vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, @@ -343,7 +341,11 @@ void lightVertex(ogles_context_t* c, vertex_t* v) vec4_t n; c->arrays.normal.fetch(c, n.v, c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK)); - if (c->transforms.rescaleNormals == GL_NORMALIZE) + + // TODO: right now we handle GL_RESCALE_NORMALS as if ti were + // GL_NORMALIZE. We could optimize this by scaling mvui + // appropriately instead. + if (c->transforms.rescaleNormals) vnorm3(n.v, n.v); const material_t& material = c->lighting.front; @@ -360,7 +362,8 @@ void lightVertex(ogles_context_t* c, vertex_t* v) // compute vertex-to-light vector if (ggl_unlikely(l.position.w)) { - vsub3w(d.v, l.objPosition.v, v->obj.v); + // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex + vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v); sqDist = dot3(d.v, d.v); vscale3(d.v, d.v, gglSqrtRecipx(sqDist)); } else { diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp index f175cdad6e6a..0b68dc06f5e3 100644 --- a/opengl/libagl/matrix.cpp +++ b/opengl/libagl/matrix.cpp @@ -55,7 +55,7 @@ static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o); static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o); static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o); static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o); -static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o); // ---------------------------------------------------------------------------- #if 0 @@ -209,7 +209,8 @@ void mvui_transform_t::picker() { flags = 0; ops = OP_ALL; - point3 = normal__generic; + point3 = point4__mvui; + point4 = point4__mvui; } void transform_t::dump(const char* what) @@ -596,66 +597,19 @@ void transform_state_t::update_mvit() void transform_state_t::update_mvui() { + GLfloat r[16]; const GLfloat* const mv = modelview.top().elements(); - - /* - When transforming normals, we can use the upper 3x3 matrix, see: - http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html - */ - // Also note that: - // l(obj) = tr(M).l(eye) for infinite light - // l(obj) = inv(M).l(eye) for local light - - const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE; - if (ggl_likely((!(ops & ~OP_ROTATE)) || - (rescaleNormals && modelview.isRigidBody()))) { - // if the modelview matrix is a rigid body transformation - // (translation, rotation, uniform scaling), then we can bypass - // the inverse by transposing the matrix. - GLfloat rescale = 1.0f; - if (rescaleNormals == GL_RESCALE_NORMAL) { - if (!(ops & ~OP_UNIFORM_SCALE)) { - rescale = reciprocalf(mv[I(0,0)]); - } else { - rescale = rsqrtf( - sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)])); - } - } - GLfixed* const x = mvui.matrix.m; - for (int i=0 ; i<3 ; i++) { - x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale); - x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale); - x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale); - } - mvui.picker(); - return; - } - - GLfloat r[3][3]; - r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]); - r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]); - r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]); - r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]); - r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]); - r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]); - r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]); - r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]); - r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]); - - GLfloat rdet; - if (rescaleNormals == GL_RESCALE_NORMAL) { - rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2])); - } else { - rdet = reciprocalf( - r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]); - } + // TODO: we need a faster invert, especially for when the modelview + // is a rigid-body matrix + invert(r, mv); GLfixed* const x = mvui.matrix.m; - for (int i=0 ; i<3 ; i++) { - x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet); - x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet); - x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet); + for (int i=0 ; i<4 ; i++) { + x[I(i,0)] = gglFloatToFixed(r[I(i,0)]); + x[I(i,1)] = gglFloatToFixed(r[I(i,1)]); + x[I(i,2)] = gglFloatToFixed(r[I(i,2)]); + x[I(i,4)] = gglFloatToFixed(r[I(i,3)]); } mvui.picker(); } @@ -783,14 +737,19 @@ void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]); } -void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { +void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + // this used for transforming light positions back to object space. + // Lights have 3 components positions, so w is always 1. + // however, it is used as a switch for directional lights, so we need + // to preserve it. const GLfixed* const m = mx->matrix.m; const GLfixed rx = rhs->x; const GLfixed ry = rhs->y; const GLfixed rz = rhs->z; - lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); - lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]); - lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]); + lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); + lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); + lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); + lhs->w = rhs->w; } diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index b6f534b7ea50..14a910c87336 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -900,7 +900,7 @@ void glTexParameteriv( memcpy(textureObject->crop_rect, params, 4*sizeof(GLint)); break; default: - ogles_error(c, GL_INVALID_ENUM); + texParameterx(target, pname, GLfixed(params[0]), c); return; } } @@ -919,6 +919,13 @@ void glTexParameterx( texParameterx(target, pname, param, c); } +void glTexParameteri( + GLenum target, GLenum pname, GLint param) +{ + ogles_context_t* c = ogles_context_t::get(); + texParameterx(target, pname, GLfixed(param), c); +} + // ---------------------------------------------------------------------------- #if 0 #pragma mark - diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 5ba6b76c10ec..23304d55b336 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -39,7 +39,7 @@ LOCAL_SRC_FILES:= \ GLES_CM/gl.cpp.arm \ # -LOCAL_SHARED_LIBRARIES += libcutils libutils libui libEGL +LOCAL_SHARED_LIBRARIES += libcutils libEGL LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libGLESv1_CM diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 25e31ee7f271..de323b392a9e 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/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp index f9dc5f17212e..4c902c8574a0 100644 --- a/opengl/libs/EGL/gpu.cpp +++ b/opengl/libs/EGL/gpu.cpp @@ -118,6 +118,11 @@ request_gpu_t* gpu_acquire(void* user) return 0; } + if (info.regs == 0) { + LOGD("requestGPU() failed"); + return 0; + } + bool failed = false; request_gpu_t* gpu = &gRegions; memset(gpu, 0, sizeof(*gpu)); diff --git a/opengl/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk index 46958d3c3c55..e193483f5d69 100644 --- a/opengl/tests/angeles/Android.mk +++ b/opengl/tests/angeles/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= app-linux.c demo.c.arm LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui LOCAL_MODULE:= angeles -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) @@ -13,5 +13,5 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= gpustate.c LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM LOCAL_MODULE:= gpustate -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk index a448f0d46161..31b7d9a48703 100644 --- a/opengl/tests/filter/Android.mk +++ b/opengl/tests/filter/Android.mk @@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= test-opengl-filter -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/finish/Android.mk b/opengl/tests/finish/Android.mk index 26836c1caf4c..8b46cd7f1773 100644 --- a/opengl/tests/finish/Android.mk +++ b/opengl/tests/finish/Android.mk @@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= test-opengl-finish -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk new file mode 100644 index 000000000000..9563e617f3aa --- /dev/null +++ b/opengl/tests/lighting1709/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := LightingTest +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml new file mode 100644 index 000000000000..6c23d422f5ec --- /dev/null +++ b/opengl/tests/lighting1709/AndroidManifest.xml @@ -0,0 +1,13 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.lightingtest"> + + <application> + <activity android:name="ClearActivity" android:label="LightingTest"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java new file mode 100644 index 000000000000..3ae8c5ca29d7 --- /dev/null +++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java @@ -0,0 +1,174 @@ +/* + * 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. + */ + +package com.android.lightingtest; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; + +public class ClearActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mGLView = new ClearGLSurfaceView(this); + setContentView(mGLView); + } + + @Override + protected void onPause() { + super.onPause(); + mGLView.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + mGLView.onResume(); + } + private GLSurfaceView mGLView; +} + +class ClearGLSurfaceView extends GLSurfaceView { + public ClearGLSurfaceView(Context context) { + super(context); + mRenderer = new ClearRenderer(); + setRenderer(mRenderer); + } + + ClearRenderer mRenderer; +} + +class ClearRenderer implements GLSurfaceView.Renderer { + public ClearRenderer() { + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + // Do nothing special. + } + + public void onSurfaceChanged(GL10 gl, int w, int h) { + // Compute the projection matrix + gl.glMatrixMode(GL10.GL_PROJECTION); + gl.glLoadIdentity(); + + // Compute the boundaries of the frustum + float fl = (float) (-(w / 2)) / 288; + float fr = (float) (w / 2) / 288; + float ft = (float) (h / 2) / 288; + float fb = (float) (-(h / 2)) / 288; + + // Set the view frustum + gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f); + + // Set the viewport dimensions + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glViewport(0, 0, w, h); + } + + public void onDrawFrame(GL10 gl) { + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + + final float lightOff[] = {0.0f, 0.0f, 0.0f, 1.0f}; + final float lightAmbient[] = {5.0f, 0.0f, 0.0f, 1.0f}; + final float lightDiffuse[] = {0.0f, 2.0f, 0.0f, 0.0f}; + final float lightPosSpot[] = {0.0f, 0.0f, -8.0f, 1.0f}; + + final float pos[] = { + -5.0f, -1.5f, 0.0f, + 0.0f, -1.5f, 0.0f, + 5.0f, -1.5f, 0.0f, + }; + + final float v[] = new float[9]; + ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4); + vbb.order(ByteOrder.nativeOrder()); + FloatBuffer vb = vbb.asFloatBuffer(); + + gl.glDisable(GL10.GL_DITHER); + + gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0); + gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0); + gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0); + gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0); + gl.glEnable(GL10.GL_LIGHT0); + + gl.glEnable(GL10.GL_LIGHTING); + + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glNormal3f(0, 0, 1); + + + // draw first 3 triangles, without using transforms + for (int i=0 ; i<3 ; i++) { + v[0] = -1; v[1] =-1; v[2] = -10; + v[3] = 0; v[4] = 1; v[5] = -10; + v[6] = 1; v[7] =-1; v[8] = -10; + for (int j=0 ; j<3 ; j++) { + v[j*3+0] -= pos[i*3+0]; + v[j*3+1] -= pos[i*3+1]; + v[j*3+2] -= pos[i*3+2]; + } + vb.put(v).position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb); + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3); + } + + // draw the 2nd batch this time with transforms + v[0] = -1; v[1] =-1; v[2] = -10; + v[3] = 0; v[4] = 1; v[5] = -10; + v[6] = 1; v[7] =-1; v[8] = -10; + vb.put(v).position(0); + gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb); + + // draw lower left triangle + gl.glPushMatrix(); + gl.glTranslatef(pos[0], pos[1], pos[2]); + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3); + gl.glPopMatrix(); + + // draw lower middle triangle + gl.glPushMatrix(); + gl.glTranslatef(pos[3], pos[4], pos[5]); + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3); + gl.glPopMatrix(); + + // draw lower right triangle + gl.glPushMatrix(); + gl.glTranslatef(pos[6], pos[7], pos[8]); + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3); + gl.glPopMatrix(); + } + + public int[] getConfigSpec() { + int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }; + return configSpec; + } +} + diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk index a8c622092645..8d5f56de2bba 100644 --- a/opengl/tests/textures/Android.mk +++ b/opengl/tests/textures/Android.mk @@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= test-opengl-textures -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk index 5cd1f048a309..76fd8dddc533 100644 --- a/opengl/tests/tritex/Android.mk +++ b/opengl/tests/tritex/Android.mk @@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= test-opengl-tritex -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index 011a6edf01e6..9bff0b282d49 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -4,14 +4,31 @@ set -e rm -rf out generated mkdir out + +# Create dummy Java files for Android APIs that are used by the code we generate. +# This allows us to test the generated code without building the rest of Android. + mkdir -p out/javax/microedition/khronos/opengles mkdir -p out/com/google/android/gles_jni +mkdir -p out/android/app mkdir -p out/android/graphics mkdir -p out/android/opengl +mkdir -p out/android/content +mkdir -p out/android/content/pm +mkdir -p out/android/os +mkdir -p out/android/util echo "package android.graphics;" > out/android/graphics/Canvas.java echo "public interface Canvas {}" >> out/android/graphics/Canvas.java +echo "package android.app; import android.content.pm.IPackageManager; public class ActivityThread { public static final ActivityThread currentActivityThread() { return null; } public static final String currentPackageName(){ return null; } public static IPackageManager getPackageManager() { return null;} }" > out/android/app/ActivityThread.java +# echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java +echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java +echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java +echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 3;}; }" > out/android/os/Build.java +echo "package android.os; public class RemoteException extends Exception {}" > out/android/os/RemoteException.java +echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java + GLFILE=out/javax/microedition/khronos/opengles/GL.java cp stubs/jsr239/GLHeader.java-if $GLFILE diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java index b0997b15a1d6..73403575cf28 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -890,16 +890,10 @@ public class JniCodeEmitter { cname + " = (" + cfunc.getArgType(cIndex).getDeclaration() + - ") _env->GetDirectBufferAddress(" + - (mUseCPlusPlus ? "" : "_env, ") + + ") getDirectBufferPointer(_env, " + cname + "_buf);"); String iii = " "; - out.println(iii + indent + "if ( ! " + cname + " ) {"); - out.println(iii + iii + indent + - (mUseCPlusPlus ? "_env" : "(*_env)") + - "->ThrowNew(" + - (mUseCPlusPlus ? "" : "_env, ") + - "IAEClass, \"Must use a native order direct Buffer\");"); + out.println(iii + indent + "if ( ! " + cname + " ) {"); out.println(iii + iii + indent + "return;"); out.println(iii + indent + "}"); } else { diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp index 3948fd3a696f..e1c09f4614d6 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp @@ -132,6 +132,19 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + char* buf = (char*) _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf += position << elementSizeShift; + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + return (void*) buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp index 11c6087bf42a..4494643bf338 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp +++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp @@ -44,9 +44,11 @@ static jclass OOMEClass; static jclass UOEClass; static jclass IAEClass; static jclass AIOOBEClass; +static jclass G11ImplClass; static jmethodID getBasePointerID; static jmethodID getBaseArrayID; static jmethodID getBaseArrayOffsetID; +static jmethodID allowIndirectBuffersID; static jfieldID positionID; static jfieldID limitID; static jfieldID elementSizeShiftID; @@ -62,13 +64,17 @@ nativeClassInitBuffer(JNIEnv *_env) jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl"); + G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal); + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); - + allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal, + "allowIndirectBuffers", "(Ljava/lang/String;)Z"); positionID = _env->GetFieldID(bufferClass, "position", "I"); limitID = _env->GetFieldID(bufferClass, "limit", "I"); elementSizeShiftID = @@ -118,6 +124,9 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + if (*array == NULL) { + return (void*) NULL; + } offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); @@ -132,6 +141,45 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +extern "C" { +extern char* __progname; +} + +static bool +allowIndirectBuffers(JNIEnv *_env) { + static jint sIndirectBufferCompatability; + if (sIndirectBufferCompatability == 0) { + jobject appName = _env->NewStringUTF(::__progname); + sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1; + } + return sIndirectBufferCompatability == 2; +} + +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + if (!buffer) { + return NULL; + } + void* buf = _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf = ((char*) buf) + (position << elementSizeShift); + } else { + if (allowIndirectBuffers(_env)) { + jarray array = 0; + jint remaining; + buf = getPointer(_env, buffer, &array, &remaining); + if (array) { + releasePointer(_env, array, buf, 0); + } + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + } + return buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl index db3a41c01797..fe60c5d50af4 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl +++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl @@ -18,6 +18,12 @@ package com.google.android.gles_jni; +import android.app.ActivityThread; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.os.Build; +import android.util.Log; + import java.nio.Buffer; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10Ext; @@ -42,7 +48,28 @@ public class GLImpl implements GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack { public GLImpl() { } - public void glGetPointerv(int pname, java.nio.Buffer[] params) { - throw new UnsupportedOperationException("glGetPointerv"); - } + public void glGetPointerv(int pname, java.nio.Buffer[] params) { + throw new UnsupportedOperationException("glGetPointerv"); + } + + private static boolean allowIndirectBuffers(String appName) { + boolean result = false; + int version = 0; + IPackageManager pm = ActivityThread.getPackageManager(); + try { + ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0); + if (applicationInfo != null) { + version = applicationInfo.targetSdkVersion; + } + } catch (android.os.RemoteException e) { + // ignore + } + Log.e("OpenGLES", String.format( + "Application %s (SDK target %d) called a GL11 Pointer method with an indirect Buffer.", + appName, version)); + if (version <= Build.VERSION_CODES.CUPCAKE) { + result = true; + } + return result; + } diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index 4abc337927ba..d84572b2715a 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -2,18 +2,14 @@ package="com.android.providers.settings" android:sharedUserId="android.uid.system"> - <!-- Permission to write Gservices in SettingsProvider --> - <permission android:name="android.permission.WRITE_GSERVICES" - android:label="@string/permlab_writeGservices" - android:description="@string/permdesc_writeGservices" - android:protectionLevel="signature" /> - <application android:allowClearUserData="false" - android:label="Settings Storage" + android:label="@string/app_label" + android:process="system" + android:backupAgent="SettingsBackupAgent" android:icon="@drawable/ic_launcher_settings"> <provider android:name="SettingsProvider" android:authorities="settings" - android:process="system" android:multiprocess="false" + android:multiprocess="false" android:writePermission="android.permission.WRITE_SETTINGS" android:initOrder="100" /> </application> diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 275ff3ac1702..f8adaa12e43d 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -31,11 +31,17 @@ <bool name="def_bluetooth_on">false</bool> <bool name="def_install_non_market_apps">false</bool> - <!-- Comma-separated list of providers. --> - <string name="def_location_providers_allowed">network</string> + <!-- Comma-separated list of location providers. + Network location is off by default because it requires + user opt-in via Setup Wizard or Settings. + --> + <string name="def_location_providers_allowed">gps</string> <!-- 0 == mobile, 1 == wifi. --> <integer name="def_network_preference">1</integer> <bool name="def_usb_mass_storage_enabled">true</bool> <bool name="def_wifi_on">false</bool> <bool name="def_networks_available_notification_on">true</bool> + + <bool name="def_backup_enabled">false</bool> + <string name="def_backup_transport"></string> </resources> diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml index 8a000919a223..9ca575e30ea9 100644 --- a/packages/SettingsProvider/res/values/strings.xml +++ b/packages/SettingsProvider/res/values/strings.xml @@ -17,7 +17,6 @@ */ --> <resources> - <string name="permlab_writeGservices">Write Gservices settings.</string> - <string name="permdesc_writeGservices">Allows the application to - change the settings in Gservices.</string> + <!-- Name of the activity for Settings storage. --> + <string name="app_label">Settings Storage</string> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index b9d567c76169..6dd117544156 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -64,8 +64,8 @@ class DatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "SettingsProvider"; private static final String DATABASE_NAME = "settings.db"; - private static final int DATABASE_VERSION = 34; - + private static final int DATABASE_VERSION = 35; + private Context mContext; public DatabaseHelper(Context context) { @@ -81,7 +81,7 @@ class DatabaseHelper extends SQLiteOpenHelper { ");"); db.execSQL("CREATE INDEX secureIndex1 ON secure (name);"); } - + @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE system (" + @@ -134,7 +134,7 @@ class DatabaseHelper extends SQLiteOpenHelper { public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { Log.w(TAG, "Upgrading settings database from version " + oldVersion + " to " + currentVersion); - + int upgradeVersion = oldVersion; // Pattern for upgrade blocks: @@ -143,7 +143,7 @@ class DatabaseHelper extends SQLiteOpenHelper { // .. your upgrade logic.. // upgradeVersion = [the DATABASE_VERSION you set] // } - + if (upgradeVersion == 20) { /* * Version 21 is part of the volume control refresh. There is no @@ -156,7 +156,7 @@ class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 21; } - + if (upgradeVersion < 22) { upgradeVersion = 22; // Upgrade the lock gesture storage location and format @@ -186,7 +186,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 24; } - + if (upgradeVersion == 24) { db.beginTransaction(); try { @@ -213,7 +213,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 26; } - + if (upgradeVersion == 26) { // This introduces the new secure settings table. db.beginTransaction(); @@ -225,12 +225,12 @@ class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 27; } - + if (upgradeVersion == 27) { // Copy settings values from 'system' to 'secure' and delete them from 'system' SQLiteStatement insertStmt = null; SQLiteStatement deleteStmt = null; - + db.beginTransaction(); try { insertStmt = @@ -271,11 +271,11 @@ class DatabaseHelper extends SQLiteOpenHelper { Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS, }; - + for (String setting : settingsToMove) { insertStmt.bindString(1, setting); insertStmt.execute(); - + deleteStmt.bindString(1, setting); deleteStmt.execute(); } @@ -291,7 +291,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 28; } - + if (upgradeVersion == 28 || upgradeVersion == 29) { // Note: The upgrade to 28 was flawed since it didn't delete the old // setting first before inserting. Combining 28 and 29 with the @@ -313,10 +313,10 @@ class DatabaseHelper extends SQLiteOpenHelper { } finally { db.endTransaction(); } - + upgradeVersion = 30; } - + if (upgradeVersion == 30) { /* * Upgrade 31 clears the title for all quick launch shortcuts so the @@ -373,7 +373,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 33; } - + if (upgradeVersion == 33) { // Set the default zoom controls to: tap-twice to bring up +/- db.beginTransaction(); @@ -386,6 +386,20 @@ class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 34; } + if (upgradeVersion == 34) { + db.beginTransaction(); + try { + SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + + " VALUES(?,?);"); + loadSecure35Settings(stmt); + stmt.close(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + upgradeVersion = 35; + } + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion + ", must wipe the settings provider"); @@ -405,7 +419,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } private void upgradeLockPatternLocation(SQLiteDatabase db) { - Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'", + Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'", null, null, null, null); if (c.getCount() > 0) { c.moveToFirst(); @@ -414,7 +428,7 @@ class DatabaseHelper extends SQLiteOpenHelper { // Convert lock pattern try { LockPatternUtils lpu = new LockPatternUtils(mContext.getContentResolver()); - List<LockPatternView.Cell> cellPattern = + List<LockPatternView.Cell> cellPattern = LockPatternUtils.stringToPattern(lockPattern); lpu.saveLockPattern(cellPattern); } catch (IllegalArgumentException e) { @@ -542,12 +556,12 @@ class DatabaseHelper extends SQLiteOpenHelper { AudioManager.RINGER_MODE_NORMAL); loadVibrateSetting(db, false); - + // By default, only the ring/notification and system streams are affected loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED, (1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) | (1 << AudioManager.STREAM_SYSTEM)); - + loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED, ((1 << AudioManager.STREAM_MUSIC) | (1 << AudioManager.STREAM_RING) | @@ -561,7 +575,7 @@ class DatabaseHelper extends SQLiteOpenHelper { if (deleteOld) { db.execSQL("DELETE FROM system WHERE name='" + Settings.System.VIBRATE_ON + "'"); } - + SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); @@ -576,79 +590,89 @@ class DatabaseHelper extends SQLiteOpenHelper { private void loadSettings(SQLiteDatabase db) { loadSystemSettings(db); - loadSecureSettings(db); + loadSecureSettings(db); } - + private void loadSystemSettings(SQLiteDatabase db) { SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - + Resources r = mContext.getResources(); - loadSetting(stmt, Settings.Secure.CURRENT_ACTIVE_PHONE, - RILConstants.CDMA_PHONE); + loadBooleanSetting(stmt, Settings.System.DIM_SCREEN, R.bool.def_dim_screen); - loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN, + loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN, "1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0); 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); - + loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS, R.string.def_airplane_mode_radios); - + loadBooleanSetting(stmt, Settings.System.AUTO_TIME, R.bool.def_auto_time); // Sync time to NITZ - + loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS, R.integer.def_screen_brightness); - + loadDefaultAnimationSettings(stmt); loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, R.bool.def_accelerometer_rotation); - - // Default date format based on build - loadSetting(stmt, Settings.System.DATE_FORMAT, - SystemProperties.get("ro.com.android.dateformat", - "MM-dd-yyyy")); + stmt.close(); } - + private void loadDefaultAnimationSettings(SQLiteStatement stmt) { loadFractionSetting(stmt, Settings.System.WINDOW_ANIMATION_SCALE, R.fraction.def_window_animation_scale, 1); loadFractionSetting(stmt, Settings.System.TRANSITION_ANIMATION_SCALE, R.fraction.def_window_transition_scale, 1); } - + private void loadSecureSettings(SQLiteDatabase db) { SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + " VALUES(?,?);"); - + loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON, R.bool.def_bluetooth_on); - + // Data roaming default, based on build - loadSetting(stmt, Settings.Secure.DATA_ROAMING, + loadSetting(stmt, Settings.Secure.DATA_ROAMING, "true".equalsIgnoreCase( - SystemProperties.get("ro.com.android.dataroaming", - "false")) ? 1 : 0); - + SystemProperties.get("ro.com.android.dataroaming", + "false")) ? 1 : 0); + loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, R.bool.def_install_non_market_apps); - + loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, R.string.def_location_providers_allowed); - + loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE, R.integer.def_network_preference); - + loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED, R.bool.def_usb_mass_storage_enabled); - + loadBooleanSetting(stmt, Settings.Secure.WIFI_ON, R.bool.def_wifi_on); loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, @@ -660,51 +684,61 @@ class DatabaseHelper extends SQLiteOpenHelper { } // Set the preferred network mode to 0 = Global, CDMA default - loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, + int type = SystemProperties.getInt("ro.telephony.default_network", RILConstants.PREFERRED_NETWORK_MODE); + loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type); // Enable or disable Cell Broadcast SMS loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS, RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED); // Set the preferred cdma subscription to 0 = Subscription from RUIM, when available - loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, + loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, RILConstants.PREFERRED_CDMA_SUBSCRIPTION); // Don't do this. The SystemServer will initialize ADB_ENABLED from a // persistent system property instead. //loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0); - + // Allow mock locations default, based on build - loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION, + loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION, "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0); + + loadSecure35Settings(stmt); stmt.close(); } + private void loadSecure35Settings(SQLiteStatement stmt) { + loadBooleanSetting(stmt, Settings.Secure.BACKUP_ENABLED, + R.bool.def_backup_enabled); + + loadStringSetting(stmt, Settings.Secure.BACKUP_TRANSPORT, + R.string.def_backup_transport); + } + private void loadSetting(SQLiteStatement stmt, String key, Object value) { stmt.bindString(1, key); stmt.bindString(2, value.toString()); stmt.execute(); } - + private void loadStringSetting(SQLiteStatement stmt, String key, int resid) { loadSetting(stmt, key, mContext.getResources().getString(resid)); } - + private void loadBooleanSetting(SQLiteStatement stmt, String key, int resid) { loadSetting(stmt, key, mContext.getResources().getBoolean(resid) ? "1" : "0"); } - + private void loadIntegerSetting(SQLiteStatement stmt, String key, int resid) { loadSetting(stmt, key, Integer.toString(mContext.getResources().getInteger(resid))); } - + private void loadFractionSetting(SQLiteStatement stmt, String key, int resid, int base) { loadSetting(stmt, key, Float.toString(mContext.getResources().getFraction(resid, base, base))); } } - diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java new file mode 100644 index 000000000000..b6bc8a596414 --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.BackupHelperAgent; +import android.bluetooth.BluetoothDevice; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.media.AudioManager; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +/** + * Performs backup and restore of the System and Secure settings. + * List of settings that are backed up are stored in the Settings.java file + */ +public class SettingsBackupAgent extends BackupHelperAgent { + + private static final String KEY_SYSTEM = "system"; + private static final String KEY_SECURE = "secure"; + private static final String KEY_SYNC = "sync_providers"; + private static final String KEY_LOCALE = "locale"; + + private static String[] sortedSystemKeys = null; + private static String[] sortedSecureKeys = null; + + private static final byte[] EMPTY_DATA = new byte[0]; + + private static final String TAG = "SettingsBackupAgent"; + + private static final int COLUMN_ID = 0; + private static final int COLUMN_NAME = 1; + private static final int COLUMN_VALUE = 2; + + private static final String[] PROJECTION = { + Settings.NameValueTable._ID, + Settings.NameValueTable.NAME, + Settings.NameValueTable.VALUE + }; + + private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf"; + private static final String FILE_BT_ROOT = "/data/misc/hcid/"; + + private SettingsHelper mSettingsHelper; + + public void onCreate() { + mSettingsHelper = new SettingsHelper(this); + super.onCreate(); + } + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + + byte[] systemSettingsData = getSystemSettings(); + byte[] secureSettingsData = getSecureSettings(); + byte[] syncProviders = mSettingsHelper.getSyncProviders(); + byte[] locale = mSettingsHelper.getLocaleData(); + + data.writeEntityHeader(KEY_SYSTEM, systemSettingsData.length); + data.writeEntityData(systemSettingsData, systemSettingsData.length); + + data.writeEntityHeader(KEY_SECURE, secureSettingsData.length); + data.writeEntityData(secureSettingsData, secureSettingsData.length); + + data.writeEntityHeader(KEY_SYNC, syncProviders.length); + data.writeEntityData(syncProviders, syncProviders.length); + + data.writeEntityHeader(KEY_LOCALE, locale.length); + data.writeEntityData(locale, locale.length); + + backupFile(FILE_WIFI_SUPPLICANT, data); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, + ParcelFileDescriptor newState) throws IOException { + + enableWifi(false); + enableBluetooth(false); + + while (data.readNextHeader()) { + final String key = data.getKey(); + final int size = data.getDataSize(); + if (KEY_SYSTEM.equals(key)) { + restoreSettings(data, Settings.System.CONTENT_URI); + } else if (KEY_SECURE.equals(key)) { + restoreSettings(data, Settings.Secure.CONTENT_URI); +// TODO: Re-enable WIFI restore when we figure out a solution for the permissions +// } else if (FILE_WIFI_SUPPLICANT.equals(key)) { +// restoreFile(FILE_WIFI_SUPPLICANT, data); + } else if (KEY_SYNC.equals(key)) { + mSettingsHelper.setSyncProviders(data); + } else if (KEY_LOCALE.equals(key)) { + byte[] localeData = new byte[size]; + data.readEntityData(localeData, 0, size); + mSettingsHelper.setLocaleData(localeData); + } else { + data.skipEntityData(); + } + } + } + + private byte[] getSystemSettings() { + Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, + null, null, Settings.NameValueTable.NAME); + // Copy and sort the array + if (sortedSystemKeys == null) { + sortedSystemKeys = copyAndSort(Settings.System.SETTINGS_TO_BACKUP); + } + byte[] result = extractRelevantValues(sortedCursor, sortedSystemKeys); + sortedCursor.close(); + return result; + } + + private byte[] getSecureSettings() { + Cursor sortedCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, + null, null, Settings.NameValueTable.NAME); + // Copy and sort the array + if (sortedSecureKeys == null) { + sortedSecureKeys = copyAndSort(Settings.Secure.SETTINGS_TO_BACKUP); + } + byte[] result = extractRelevantValues(sortedCursor, sortedSecureKeys); + sortedCursor.close(); + return result; + } + + private void restoreSettings(BackupDataInput data, Uri contentUri) { + ContentValues cv = new ContentValues(2); + byte[] settings = new byte[data.getDataSize()]; + try { + data.readEntityData(settings, 0, settings.length); + } catch (IOException ioe) { + Log.e(TAG, "Couldn't read entity data"); + return; + } + int pos = 0; + while (pos < settings.length) { + int length = readInt(settings, pos); + pos += 4; + String settingName = length > 0? new String(settings, pos, length) : null; + pos += length; + length = readInt(settings, pos); + pos += 4; + String settingValue = length > 0? new String(settings, pos, length) : null; + pos += length; + if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) { + //Log.i(TAG, "Restore " + settingName + " = " + settingValue); + if (mSettingsHelper.restoreValue(settingName, settingValue)) { + cv.clear(); + cv.put(Settings.NameValueTable.NAME, settingName); + cv.put(Settings.NameValueTable.VALUE, settingValue); + getContentResolver().insert(contentUri, cv); + } + } + } + } + + private String[] copyAndSort(String[] keys) { + String[] sortedKeys = new String[keys.length]; + System.arraycopy(keys, 0, sortedKeys, 0, keys.length); + Arrays.sort(sortedKeys); + return sortedKeys; + } + + /** + * Given a cursor sorted by key name and a set of keys sorted by name, + * extract the required keys and values and write them to a byte array. + * @param sortedCursor + * @param sortedKeys + * @return + */ + byte[] extractRelevantValues(Cursor sortedCursor, String[] sortedKeys) { + byte[][] values = new byte[sortedKeys.length * 2][]; // keys and values + if (!sortedCursor.moveToFirst()) { + Log.e(TAG, "Couldn't read from the cursor"); + return new byte[0]; + } + int keyIndex = 0; + int totalSize = 0; + while (!sortedCursor.isAfterLast()) { + String name = sortedCursor.getString(COLUMN_NAME); + while (sortedKeys[keyIndex].compareTo(name.toString()) < 0) { + keyIndex++; + if (keyIndex == sortedKeys.length) break; + } + if (keyIndex < sortedKeys.length && name.equals(sortedKeys[keyIndex])) { + String value = sortedCursor.getString(COLUMN_VALUE); + byte[] nameBytes = name.toString().getBytes(); + totalSize += 4 + nameBytes.length; + values[keyIndex * 2] = nameBytes; + byte[] valueBytes; + if (TextUtils.isEmpty(value)) { + valueBytes = null; + totalSize += 4; + } else { + valueBytes = value.toString().getBytes(); + totalSize += 4 + valueBytes.length; + //Log.i(TAG, "Backing up " + name + " = " + value); + } + values[keyIndex * 2 + 1] = valueBytes; + keyIndex++; + } + if (keyIndex == sortedKeys.length || !sortedCursor.moveToNext()) { + break; + } + } + + byte[] result = new byte[totalSize]; + int pos = 0; + for (int i = 0; i < sortedKeys.length * 2; i++) { + if (values[i] != null) { + pos = writeInt(result, pos, values[i].length); + pos = writeBytes(result, pos, values[i]); + } + } + return result; + } + + private void backupFile(String filename, BackupDataOutput data) { + try { + File file = new File(filename); + if (file.exists()) { + byte[] bytes = new byte[(int) file.length()]; + FileInputStream fis = new FileInputStream(file); + int offset = 0; + int got = 0; + do { + got = fis.read(bytes, offset, bytes.length - offset); + if (got > 0) offset += got; + } while (offset < bytes.length && got > 0); + data.writeEntityHeader(filename, bytes.length); + data.writeEntityData(bytes, bytes.length); + } else { + data.writeEntityHeader(filename, 0); + data.writeEntityData(EMPTY_DATA, 0); + } + } catch (IOException ioe) { + Log.w(TAG, "Couldn't backup " + filename); + } + } + + private void restoreFile(String filename, BackupDataInput data) { + byte[] bytes = new byte[data.getDataSize()]; + if (bytes.length <= 0) return; + try { + data.readEntityData(bytes, 0, bytes.length); + FileOutputStream fos = new FileOutputStream(filename); + fos.write(bytes); + } catch (IOException ioe) { + Log.w(TAG, "Couldn't restore " + filename); + } + } + + /** + * Write an int in BigEndian into the byte array. + * @param out byte array + * @param pos current pos in array + * @param value integer to write + * @return the index after adding the size of an int (4) + */ + private int writeInt(byte[] out, int pos, int value) { + out[pos + 0] = (byte) ((value >> 24) & 0xFF); + out[pos + 1] = (byte) ((value >> 16) & 0xFF); + out[pos + 2] = (byte) ((value >> 8) & 0xFF); + out[pos + 3] = (byte) ((value >> 0) & 0xFF); + return pos + 4; + } + + private int writeBytes(byte[] out, int pos, byte[] value) { + System.arraycopy(value, 0, out, pos, value.length); + return pos + value.length; + } + + private int readInt(byte[] in, int pos) { + int result = + ((in[pos ] & 0xFF) << 24) | + ((in[pos + 1] & 0xFF) << 16) | + ((in[pos + 2] & 0xFF) << 8) | + ((in[pos + 3] & 0xFF) << 0); + return result; + } + + private void enableWifi(boolean enable) { + WifiManager wfm = (WifiManager) getSystemService(Context.WIFI_SERVICE); + if (wfm != null) { + wfm.setWifiEnabled(enable); + } + } + + private void enableBluetooth(boolean enable) { + BluetoothDevice bt = (BluetoothDevice) getSystemService(Context.BLUETOOTH_SERVICE); + if (bt != null) { + if (!enable) { + bt.disable(); + } else { + bt.enable(); + } + } + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java new file mode 100644 index 000000000000..2c5775af683a --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import java.util.Locale; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.backup.BackupDataInput; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IContentService; +import android.content.res.Configuration; +import android.location.LocationManager; +import android.media.AudioManager; +import android.os.IHardwareService; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +public class SettingsHelper { + private static final String TAG = "SettingsHelper"; + + private Context mContext; + private AudioManager mAudioManager; + private IContentService mContentService; + private static final String[] PROVIDERS = { "gmail-ls", "calendar", "contacts" }; + + private boolean mSilent; + private boolean mVibrate; + + public SettingsHelper(Context context) { + mContext = context; + mAudioManager = (AudioManager) context + .getSystemService(Context.AUDIO_SERVICE); + mContentService = ContentResolver.getContentService(); + } + + /** + * Sets the property via a call to the appropriate API, if any, and returns + * whether or not the setting should be saved to the database as well. + * @param name the name of the setting + * @param value the string value of the setting + * @return whether to continue with writing the value to the database. In + * some cases the data will be written by the call to the appropriate API, + * and in some cases the property value needs to be modified before setting. + */ + public boolean restoreValue(String name, String value) { + if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) { + setBrightness(Integer.parseInt(value)); + } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) { + setSoundEffects(Integer.parseInt(value) == 1); + } else if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { + setGpsLocation(value); + return false; + } + return true; + } + + private void setGpsLocation(String value) { + final String GPS = LocationManager.GPS_PROVIDER; + boolean enabled = + GPS.equals(value) || + value.startsWith(GPS + ",") || + value.endsWith("," + GPS) || + value.contains("," + GPS + ","); + Settings.Secure.setLocationProviderEnabled( + mContext.getContentResolver(), GPS, enabled); + } + + private void setSoundEffects(boolean enable) { + if (enable) { + mAudioManager.loadSoundEffects(); + } else { + mAudioManager.unloadSoundEffects(); + } + } + + private void setBrightness(int brightness) { + try { + IHardwareService hardware = IHardwareService.Stub + .asInterface(ServiceManager.getService("hardware")); + if (hardware != null) { + hardware.setBacklights(brightness); + } + } catch (RemoteException doe) { + + } + } + + private void setRingerMode() { + if (mSilent) { + mAudioManager.setRingerMode(mVibrate ? AudioManager.RINGER_MODE_VIBRATE : + AudioManager.RINGER_MODE_SILENT); + } else { + mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, + mVibrate ? AudioManager.VIBRATE_SETTING_ON + : AudioManager.VIBRATE_SETTING_OFF); + } + } + + byte[] getSyncProviders() { + byte[] sync = new byte[1 + PROVIDERS.length]; + try { + sync[0] = (byte) (mContentService.getListenForNetworkTickles() ? 1 : 0); + for (int i = 0; i < PROVIDERS.length; i++) { + sync[i + 1] = (byte) + (mContentService.getSyncProviderAutomatically(PROVIDERS[i]) ? 1 : 0); + } + } catch (RemoteException re) { + Log.w(TAG, "Unable to backup sync providers"); + return sync; + } + return sync; + } + + void setSyncProviders(BackupDataInput backup) { + byte[] sync = new byte[backup.getDataSize()]; + + try { + backup.readEntityData(sync, 0, sync.length); + mContentService.setListenForNetworkTickles(sync[0] == 1); + for (int i = 0; i < PROVIDERS.length; i++) { + mContentService.setSyncProviderAutomatically(PROVIDERS[i], sync[i + 1] > 0); + } + } catch (RemoteException re) { + Log.w(TAG, "Unable to restore sync providers"); + } catch (java.io.IOException ioe) { + Log.w(TAG, "Unable to read sync settings"); + } + } + + byte[] getLocaleData() { + Configuration conf = mContext.getResources().getConfiguration(); + final Locale loc = conf.locale; + String localeString = loc.getLanguage(); + String country = loc.getCountry(); + if (!TextUtils.isEmpty(country)) { + localeString += "_" + country; + } + return localeString.getBytes(); + } + + /** + * Sets the locale specified. Input data is the equivalent of "ll_cc".getBytes(), where + * "ll" is the language code and "cc" is the country code. + * @param data the locale string in bytes. + */ + void setLocaleData(byte[] data) { + // Check if locale was set by the user: + Configuration conf = mContext.getResources().getConfiguration(); + Locale loc = conf.locale; + if (conf.userSetLocale) return; // Don't change if user set it in the SetupWizard + + final String[] availableLocales = mContext.getAssets().getLocales(); + String localeCode = new String(data); + String language = new String(data, 0, 2); + String country = data.length > 4 ? new String(data, 3, 2) : ""; + loc = null; + for (int i = 0; i < availableLocales.length; i++) { + if (availableLocales[i].equals(localeCode)) { + loc = new Locale(language, country); + break; + } + } + if (loc == null) return; // Couldn't find the saved locale in this version of the software + + try { + IActivityManager am = ActivityManagerNative.getDefault(); + Configuration config = am.getConfiguration(); + config.locale = loc; + // indicate this isn't some passing default - the user wants this remembered + config.userSetLocale = true; + + am.updateConfiguration(config); + } catch (RemoteException e) { + // Intentionally left blank + } + + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 6f430c43c09e..2abf8b332aaa 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -16,6 +16,9 @@ package com.android.providers.settings; +import java.io.FileNotFoundException; + +import android.backup.BackupManager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; @@ -27,6 +30,7 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.media.RingtoneManager; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.DrmStore; import android.provider.MediaStore; @@ -34,8 +38,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import java.io.FileNotFoundException; - public class SettingsProvider extends ContentProvider { private static final String TAG = "SettingsProvider"; private static final boolean LOCAL_LOGV = false; @@ -44,6 +46,8 @@ public class SettingsProvider extends ContentProvider { private static final String TABLE_OLD_FAVORITES = "old_favorites"; private DatabaseHelper mOpenHelper; + + private BackupManager mBackupManager; /** * Decode a content URL into the table, projection, and arguments @@ -137,6 +141,8 @@ public class SettingsProvider extends ContentProvider { SystemProperties.set(property, Long.toString(version)); } + // Inform the backup manager about a data change + mBackupManager.dataChanged(); // Now send the notification through the content framework. String notify = uri.getQueryParameter("notify"); @@ -158,20 +164,25 @@ public class SettingsProvider extends ContentProvider { getContext().checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot write secure settings table"); - + throw new SecurityException( + String.format("Permission denial: writing to secure settings requires %1$s", + android.Manifest.permission.WRITE_SECURE_SETTINGS)); + // TODO: Move gservices into its own provider so we don't need this nonsense. } else if ("gservices".equals(args.table) && getContext().checkCallingOrSelfPermission( android.Manifest.permission.WRITE_GSERVICES) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot write gservices table"); + throw new SecurityException( + String.format("Permission denial: writing to gservices settings requires %1$s", + android.Manifest.permission.WRITE_GSERVICES)); } } @Override public boolean onCreate() { mOpenHelper = new DatabaseHelper(getContext()); + mBackupManager = new BackupManager(getContext()); return true; } diff --git a/packages/SubscribedFeedsProvider/AndroidManifest.xml b/packages/SubscribedFeedsProvider/AndroidManifest.xml index 6ecda48a7cb5..ca00a9b5999f 100644 --- a/packages/SubscribedFeedsProvider/AndroidManifest.xml +++ b/packages/SubscribedFeedsProvider/AndroidManifest.xml @@ -10,7 +10,7 @@ <application android:process="system" android:allowClearUserData="false" android:icon="@drawable/app_icon" - android:label="Sync Feeds"> + android:label="@string/app_label"> <uses-library android:name="com.google.android.gtalkservice" /> <provider android:name="SubscribedFeedsProvider" android:authorities="subscribedfeeds" android:syncable="false" diff --git a/packages/SubscribedFeedsProvider/res/values/strings.xml b/packages/SubscribedFeedsProvider/res/values/strings.xml new file mode 100644 index 000000000000..072571d034b5 --- /dev/null +++ b/packages/SubscribedFeedsProvider/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- Title of the feed synchronization activity. --> + <string name="app_label">Sync Feeds</string> +</resources> + diff --git a/packages/TtsService/Android.mk b/packages/TtsService/Android.mk new file mode 100644 index 000000000000..2737fb4387c6 --- /dev/null +++ b/packages/TtsService/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := user + +LOCAL_SRC_FILES := $(call all-subdir-java-files) \ + +LOCAL_PACKAGE_NAME := TtsService +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml new file mode 100755 index 000000000000..bd17ba0a9a2f --- /dev/null +++ b/packages/TtsService/AndroidManifest.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.tts"> + <application android:label="TTS Service"> + <service android:enabled="true" + android:name=".TtsService" + android:label="TTS Service"> + <intent-filter> + <action android:name="android.intent.action.START_TTS_SERVICE"/> + <category android:name="android.intent.category.TTS"/> + </intent-filter> + </service> + </application> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> +</manifest> diff --git a/packages/TtsService/MODULE_LICENSE_APACHE2 b/packages/TtsService/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/packages/TtsService/MODULE_LICENSE_APACHE2 diff --git a/packages/TtsService/NOTICE b/packages/TtsService/NOTICE new file mode 100644 index 000000000000..64aaa8dbd68e --- /dev/null +++ b/packages/TtsService/NOTICE @@ -0,0 +1,190 @@ + + 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. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk new file mode 100755 index 000000000000..665d6d20fc41 --- /dev/null +++ b/packages/TtsService/jni/Android.mk @@ -0,0 +1,31 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + android_tts_SynthProxy.cpp + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) + +LOCAL_SHARED_LIBRARIES := \ + libandroid_runtime \ + libnativehelper \ + libmedia \ + libutils \ + libcutils + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + + +LOCAL_MODULE:= libttssynthproxy + +LOCAL_ARM_MODE := arm + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) + diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp new file mode 100644 index 000000000000..1958ba936946 --- /dev/null +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -0,0 +1,778 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <unistd.h> + +#define LOG_TAG "SynthProxy" + +#include <utils/Log.h> +#include <nativehelper/jni.h> +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <tts/TtsEngine.h> +#include <media/AudioTrack.h> + +#include <dlfcn.h> + +#define DEFAULT_TTS_RATE 16000 +#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT +#define DEFAULT_TTS_NB_CHANNELS 1 +#define DEFAULT_TTS_BUFFERSIZE 1024 + +#define USAGEMODE_PLAY_IMMEDIATELY 0 +#define USAGEMODE_WRITE_TO_FILE 1 + +using namespace android; + +// ---------------------------------------------------------------------------- +struct fields_t { + jfieldID synthProxyFieldJniData; + jclass synthProxyClass; + jmethodID synthProxyMethodPost; +}; + +struct afterSynthData_t { + jint jniStorage; + int usageMode; + FILE* outputFile; +}; + +// ---------------------------------------------------------------------------- +static fields_t javaTTSFields; + +// ---------------------------------------------------------------------------- +class SynthProxyJniStorage { + public : + //jclass tts_class; + jobject tts_ref; + TtsEngine* mNativeSynthInterface; + AudioTrack* mAudioOut; + uint32_t mSampleRate; + AudioSystem::audio_format mAudFormat; + int mNbChannels; + int8_t * mBuffer; + size_t mBufferSize; + + SynthProxyJniStorage() { + //tts_class = NULL; + tts_ref = NULL; + mNativeSynthInterface = NULL; + mAudioOut = NULL; + mSampleRate = DEFAULT_TTS_RATE; + mAudFormat = DEFAULT_TTS_FORMAT; + mNbChannels = DEFAULT_TTS_NB_CHANNELS; + mBufferSize = DEFAULT_TTS_BUFFERSIZE; + mBuffer = new int8_t[mBufferSize]; + } + + ~SynthProxyJniStorage() { + killAudio(); + if (mNativeSynthInterface) { + mNativeSynthInterface->shutdown(); + mNativeSynthInterface = NULL; + } + delete mBuffer; + } + + void killAudio() { + if (mAudioOut) { + mAudioOut->stop(); + delete mAudioOut; + mAudioOut = NULL; + } + } + + void createAudioOut(uint32_t rate, AudioSystem::audio_format format, + int channel) { + mSampleRate = rate; + mAudFormat = format; + mNbChannels = channel; + + // TODO use the TTS stream type + int streamType = AudioSystem::MUSIC; + + // retrieve system properties to ensure successful creation of the + // AudioTrack object for playback + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { + afSampleRate = 44100; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { + afFrameCount = 2048; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { + afLatency = 500; + } + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + if (minBufCount < 2) minBufCount = 2; + int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate; + + mAudioOut = new AudioTrack(streamType, rate, format, channel, + minFrameCount > 4096 ? minFrameCount : 4096, + 0, 0, 0, 0); // not using an AudioTrack callback + + if (mAudioOut->initCheck() != NO_ERROR) { + LOGI("AudioTrack error"); + delete mAudioOut; + mAudioOut = NULL; + } else { + //LOGI("AudioTrack OK"); + mAudioOut->start(); + LOGI("AudioTrack started"); + } + } +}; + + +// ---------------------------------------------------------------------------- +void prepAudioTrack(SynthProxyJniStorage* pJniData, + uint32_t rate, AudioSystem::audio_format format, int channel) +{ + // Don't bother creating a new audiotrack object if the current + // object is already set. + if ( pJniData->mAudioOut && + (rate == pJniData->mSampleRate) && + (format == pJniData->mAudFormat) && + (channel == pJniData->mNbChannels) ){ + return; + } + if (pJniData->mAudioOut){ + pJniData->killAudio(); + } + pJniData->createAudioOut(rate, format, channel); +} + + +// ---------------------------------------------------------------------------- +/* + * Callback from TTS engine. + * Directly speaks using AudioTrack or write to file + */ +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) { + //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize); + + if (userdata == NULL){ + LOGE("userdata == NULL"); + return TTS_CALLBACK_HALT; + } + afterSynthData_t* pForAfter = (afterSynthData_t*)userdata; + SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage); + + if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){ + //LOGV("Direct speech"); + + if (wav == NULL) { + delete pForAfter; + LOGI("Null: speech has completed"); + } + + if (bufferSize > 0) { + prepAudioTrack(pJniData, rate, format, channel); + if (pJniData->mAudioOut) { + pJniData->mAudioOut->write(wav, bufferSize); + //LOGV("AudioTrack wrote: %d bytes", bufferSize); + } else { + LOGE("Can't play, null audiotrack"); + } + } + } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) { + LOGV("Save to file"); + if (wav == NULL) { + delete pForAfter; + LOGV("Null: speech has completed"); + return TTS_CALLBACK_HALT; + } + if (bufferSize > 0){ + fwrite(wav, 1, bufferSize, pForAfter->outputFile); + } + } + // 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 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."); + if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) { + // only delete for direct playback. When writing to a file, we still have work to do + // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there. + 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 + // to update bufferSize to let the TTS engine know how much it can write the + // next time it calls this function. + bufferSize = pJniData->mBufferSize; + + return TTS_CALLBACK_CONTINUE; +} + + +// ---------------------------------------------------------------------------- +static void +android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz, + jobject weak_this, jstring nativeSoLib) +{ + SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage(); + + prepAudioTrack(pJniStorage, + DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS); + + const char *nativeSoLibNativeString = + env->GetStringUTFChars(nativeSoLib, 0); + + void *engine_lib_handle = dlopen(nativeSoLibNativeString, + RTLD_NOW | RTLD_LOCAL); + if (engine_lib_handle==NULL) { + LOGI("engine_lib_handle==NULL"); + // TODO report error so the TTS can't be used + } else { + TtsEngine *(*get_TtsEngine)() = + reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine")); + + pJniStorage->mNativeSynthInterface = (*get_TtsEngine)(); + + if (pJniStorage->mNativeSynthInterface) { + pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB); + } + } + + // we use a weak reference so the SynthProxy object can be garbage collected. + pJniStorage->tts_ref = env->NewGlobalRef(weak_this); + + // save the JNI resources so we can use them (and free them) later + env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, + (int)pJniStorage); + + env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString); +} + + +static void +android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData) +{ + if (jniData) { + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + delete pSynthData; + } +} + + +static int +android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData, + jstring language, jstring country, jstring variant) +{ + int result = TTS_LANG_NOT_SUPPORTED; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data"); + return result; + } + + 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); + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString, + countryNativeString, variantNativeString); + } + env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); + return result; +} + + +static int +android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, + jstring language, jstring country, jstring variant) +{ + int result = TTS_LANG_NOT_SUPPORTED; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data"); + return result; + } + + 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); + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->setLanguage(langNativeString, + countryNativeString, variantNativeString); + } + env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); + return result; +} + + +static int +android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData, + jstring language, jstring country, jstring variant) +{ + int result = TTS_LANG_NOT_SUPPORTED; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data"); + return result; + } + + 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); + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, + countryNativeString, variantNativeString); + } + env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); + + return result; +} + + +static int +android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData, + jint speechRate) +{ + int result = TTS_FAILURE; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data"); + return result; + } + + int bufSize = 10; + char buffer [bufSize]; + sprintf(buffer, "%d", speechRate); + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + LOGI("setting speech rate to %d", speechRate); + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->setProperty("rate", buffer, bufSize); + } + + return result; +} + + +static int +android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData, + jint pitch) +{ + int result = TTS_FAILURE; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data"); + return result; + } + + int bufSize = 10; + char buffer [bufSize]; + sprintf(buffer, "%d", pitch); + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + LOGI("setting pitch to %d", pitch); + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->setProperty("pitch", buffer, bufSize); + } + + return result; +} + + +static int +android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, + jstring textJavaString, jstring filenameJavaString) +{ + int result = TTS_FAILURE; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data"); + return result; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + if (!pSynthData->mNativeSynthInterface) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle"); + return result; + } + + // Retrieve audio parameters before writing the file header + AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT; + uint32_t rate = DEFAULT_TTS_RATE; + int channels = DEFAULT_TTS_NB_CHANNELS; + pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels); + + if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format"); + return result; + } + + const char *filenameNativeString = + env->GetStringUTFChars(filenameJavaString, 0); + const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); + + afterSynthData_t* pForAfter = new (afterSynthData_t); + pForAfter->jniStorage = jniData; + pForAfter->usageMode = USAGEMODE_WRITE_TO_FILE; + + pForAfter->outputFile = fopen(filenameNativeString, "wb"); + + if (pForAfter->outputFile == NULL) { + LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file"); + delete pForAfter; + return result; + } + + // Write 44 blank bytes for WAV header, then come back and fill them in + // after we've written the audio data + char header[44]; + fwrite(header, 1, 44, pForAfter->outputFile); + + unsigned int unique_identifier; + + result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, + pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter); + + long filelen = ftell(pForAfter->outputFile); + + int samples = (((int)filelen) - 44) / 2; + header[0] = 'R'; + header[1] = 'I'; + header[2] = 'F'; + header[3] = 'F'; + ((uint32_t *)(&header[4]))[0] = filelen - 8; + header[8] = 'W'; + header[9] = 'A'; + header[10] = 'V'; + header[11] = 'E'; + + header[12] = 'f'; + header[13] = 'm'; + header[14] = 't'; + header[15] = ' '; + + ((uint32_t *)(&header[16]))[0] = 16; // size of fmt + + int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1); + + ((unsigned short *)(&header[20]))[0] = 1; // format + ((unsigned short *)(&header[22]))[0] = channels; // channels + ((uint32_t *)(&header[24]))[0] = rate; // samplerate + ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate + ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align + ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample + + header[36] = 'd'; + header[37] = 'a'; + header[38] = 't'; + header[39] = 'a'; + + ((uint32_t *)(&header[40]))[0] = samples * 2; // size of data + + // Skip back to the beginning and rewrite the header + fseek(pForAfter->outputFile, 0, SEEK_SET); + fwrite(header, 1, 44, pForAfter->outputFile); + + fflush(pForAfter->outputFile); + fclose(pForAfter->outputFile); + + delete pForAfter; + pForAfter = NULL; + + env->ReleaseStringUTFChars(textJavaString, textNativeString); + env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString); + + return result; +} + + +static int +android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, + jstring textJavaString) +{ + int result = TTS_FAILURE; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_speak(): invalid JNI data"); + return result; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + + if (pSynthData->mAudioOut) { + pSynthData->mAudioOut->stop(); + pSynthData->mAudioOut->start(); + } + + afterSynthData_t* pForAfter = new (afterSynthData_t); + pForAfter->jniStorage = jniData; + pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY; + + if (pSynthData->mNativeSynthInterface) { + const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); + result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, + pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter); + env->ReleaseStringUTFChars(textJavaString, textNativeString); + } + + return result; +} + + +static int +android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData) +{ + int result = TTS_FAILURE; + + if (jniData == 0) { + LOGE("android_tts_SynthProxy_stop(): invalid JNI data"); + return result; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + + if (pSynthData->mNativeSynthInterface) { + result = pSynthData->mNativeSynthInterface->stop(); + } + if (pSynthData->mAudioOut) { + pSynthData->mAudioOut->stop(); + } + + return result; +} + + +static void +android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_shutdown(): invalid JNI data"); + return; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + if (pSynthData->mNativeSynthInterface) { + pSynthData->mNativeSynthInterface->shutdown(); + pSynthData->mNativeSynthInterface = NULL; + } +} + + +// TODO add buffer format +static void +android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData, + int bufferPointer, int bufferSize) +{ +LOGI("android_tts_SynthProxy_playAudioBuffer"); + if (jniData == 0) { + LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data"); + return; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + short* wav = (short*) bufferPointer; + pSynthData->mAudioOut->write(wav, bufferSize); + //LOGI("AudioTrack wrote: %d bytes", bufferSize); +} + + +static jobjectArray +android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data"); + return NULL; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + + if (pSynthData->mNativeSynthInterface) { + size_t bufSize = 100; + char lang[bufSize]; + char country[bufSize]; + char variant[bufSize]; + memset(lang, 0, bufSize); + memset(country, 0, bufSize); + memset(variant, 0, bufSize); + jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3, + env->FindClass("java/lang/String"), env->NewStringUTF("")); + pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant); + env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang)); + env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country)); + env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant)); + return retLocale; + } else { + return NULL; + } +} + + +JNIEXPORT int JNICALL +android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_getRate(): invalid JNI data"); + return 0; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + size_t bufSize = 100; + + char buf[bufSize]; + memset(buf, 0, bufSize); + // TODO check return codes + if (pSynthData->mNativeSynthInterface) { + pSynthData->mNativeSynthInterface->getProperty("rate", buf, &bufSize); + } + return atoi(buf); +} + +// Dalvik VM type signatures +static JNINativeMethod gMethods[] = { + { "native_stop", + "(I)I", + (void*)android_tts_SynthProxy_stop + }, + { "native_speak", + "(ILjava/lang/String;)I", + (void*)android_tts_SynthProxy_speak + }, + { "native_synthesizeToFile", + "(ILjava/lang/String;Ljava/lang/String;)I", + (void*)android_tts_SynthProxy_synthesizeToFile + }, + { "native_isLanguageAvailable", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)android_tts_SynthProxy_isLanguageAvailable + }, + { "native_setLanguage", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)android_tts_SynthProxy_setLanguage + }, + { "native_loadLanguage", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)android_tts_SynthProxy_loadLanguage + }, + { "native_setSpeechRate", + "(II)I", + (void*)android_tts_SynthProxy_setSpeechRate + }, + { "native_setPitch", + "(II)I", + (void*)android_tts_SynthProxy_setPitch + }, + { "native_playAudioBuffer", + "(III)V", + (void*)android_tts_SynthProxy_playAudioBuffer + }, + { "native_getLanguage", + "(I)[Ljava/lang/String;", + (void*)android_tts_SynthProxy_getLanguage + }, + { "native_getRate", + "(I)I", + (void*)android_tts_SynthProxy_getRate + }, + { "native_shutdown", + "(I)V", + (void*)android_tts_SynthProxy_shutdown + }, + { "native_setup", + "(Ljava/lang/Object;Ljava/lang/String;)V", + (void*)android_tts_SynthProxy_native_setup + }, + { "native_finalize", + "(I)V", + (void*)android_tts_SynthProxy_native_finalize + } +}; + +#define SP_JNIDATA_FIELD_NAME "mJniData" +#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava" + +static const char* const kClassPathName = "android/tts/SynthProxy"; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + jclass clazz; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed\n"); + goto bail; + } + assert(env != NULL); + + clazz = env->FindClass(kClassPathName); + if (clazz == NULL) { + LOGE("Can't find %s", kClassPathName); + goto bail; + } + + javaTTSFields.synthProxyClass = clazz; + javaTTSFields.synthProxyFieldJniData = NULL; + javaTTSFields.synthProxyMethodPost = NULL; + + javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz, + SP_JNIDATA_FIELD_NAME, "I"); + if (javaTTSFields.synthProxyFieldJniData == NULL) { + LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME); + goto bail; + } + + javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz, + SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V"); + if (javaTTSFields.synthProxyMethodPost == NULL) { + LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME); + goto bail; + } + + if (jniRegisterNativeMethods( + env, kClassPathName, gMethods, NELEM(gMethods)) < 0) + goto bail; + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + + bail: + return result; +} diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java new file mode 100755 index 000000000000..bb16b14dbceb --- /dev/null +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * 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.tts; + +import android.util.Log; +import java.lang.ref.WeakReference; + +/** + * @hide + * + * The SpeechSynthesis class provides a high-level api to create and play + * synthesized speech. This class is used internally to talk to a native + * TTS library that implements the interface defined in + * frameworks/base/include/tts/TtsEngine.h + * + */ +@SuppressWarnings("unused") +public class SynthProxy { + + // + // External API + // + + /** + * Constructor; pass the location of the native TTS .so to use. + */ + public SynthProxy(String nativeSoLib) { + Log.e("TTS is loading", nativeSoLib); + native_setup(new WeakReference<SynthProxy>(this), nativeSoLib); + } + + /** + * Stops and clears the AudioTrack. + */ + public int stop() { + return native_stop(mJniData); + } + + /** + * Synthesize speech and speak it directly using AudioTrack. + */ + public int speak(String text) { + return native_speak(mJniData, text); + } + + /** + * Synthesize speech to a file. The current implementation writes a valid + * WAV file to the given path, assuming it is writable. Something like + * "/sdcard/???.wav" is recommended. + */ + public int synthesizeToFile(String text, String filename) { + return native_synthesizeToFile(mJniData, text, filename); + } + + /** + * Queries for language support. + * Return codes are defined in android.speech.tts.TextToSpeech + */ + public int isLanguageAvailable(String language, String country, String variant) { + return native_isLanguageAvailable(mJniData, language, country, variant); + } + + /** + * Sets the language. + */ + public int setLanguage(String language, String country, String variant) { + return native_setLanguage(mJniData, language, country, variant); + } + + /** + * Loads the language: it's not set, but prepared for use later. + */ + public int loadLanguage(String language, String country, String variant) { + return native_loadLanguage(mJniData, language, country, variant); + } + + /** + * Sets the speech rate. + */ + public final int setSpeechRate(int speechRate) { + return native_setSpeechRate(mJniData, speechRate); + } + + /** + * Sets the pitch of the synthesized voice. + */ + public final int setPitch(int pitch) { + return native_setPitch(mJniData, pitch); + } + + /** + * Plays the given audio buffer. + */ + public void playAudioBuffer(int bufferPointer, int bufferSize) { + native_playAudioBuffer(mJniData, bufferPointer, bufferSize); + } + + /** + * Returns the currently set language, country and variant information. + */ + public String[] getLanguage() { + return native_getLanguage(mJniData); + } + + /** + * Gets the currently set rate. + */ + public int getRate() { + return native_getRate(mJniData); + } + + /** + * Shuts down the native synthesizer. + */ + public void shutdown() { + native_shutdown(mJniData); + } + + // + // Internal + // + + protected void finalize() { + native_finalize(mJniData); + mJniData = 0; + } + + static { + System.loadLibrary("ttssynthproxy"); + } + + private final static String TAG = "SynthProxy"; + + /** + * Accessed by native methods + */ + private int mJniData = 0; + + private native final void native_setup(Object weak_this, + String nativeSoLib); + + private native final void native_finalize(int jniData); + + private native final int native_stop(int jniData); + + private native final int native_speak(int jniData, String text); + + private native final int native_synthesizeToFile(int jniData, String text, String filename); + + private native final int native_isLanguageAvailable(int jniData, String language, + String country, String variant); + + private native final int native_setLanguage(int jniData, String language, String country, + String variant); + + private native final int native_loadLanguage(int jniData, String language, String country, + String variant); + + private native final int native_setSpeechRate(int jniData, int speechRate); + + private native final int native_setPitch(int jniData, int speechRate); + + // TODO add buffer format + private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize); + + private native final String[] native_getLanguage(int jniData); + + private native final int native_getRate(int jniData); + + private native final void native_shutdown(int jniData); + + + /** + * Callback from the C layer + */ + @SuppressWarnings("unused") + private static void postNativeSpeechSynthesizedInJava(Object tts_ref, + int bufferPointer, int bufferSize) { + + Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer + + " bufferSize: " + bufferSize); + + SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get(); + // TODO notify TTS service of synthesis/playback completion, + // method definition to be changed. + } +} diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java new file mode 100755 index 000000000000..a713edf0bd89 --- /dev/null +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -0,0 +1,936 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * 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.tts; + +import android.app.Service; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.preference.PreferenceManager; +import android.speech.tts.ITts.Stub; +import android.speech.tts.ITtsCallback; +import android.speech.tts.TextToSpeech; +import android.util.Log; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @hide Synthesizes speech from text. This is implemented as a service so that + * other applications can call the TTS without needing to bundle the TTS + * in the build. + * + */ +public class TtsService extends Service implements OnCompletionListener { + + private static class SpeechItem { + public static final int TEXT = 0; + public static final int EARCON = 1; + public static final int SILENCE = 2; + public static final int TEXT_TO_FILE = 3; + public String mText = ""; + public ArrayList<String> mParams = null; + public int mType = TEXT; + public long mDuration = 0; + public String mFilename = null; + + public SpeechItem(String text, ArrayList<String> params, int itemType) { + mText = text; + mParams = params; + mType = itemType; + } + + public SpeechItem(long silenceTime) { + mDuration = silenceTime; + mType = SILENCE; + } + + public SpeechItem(String text, ArrayList<String> params, int itemType, String filename) { + mText = text; + mParams = params; + mType = itemType; + mFilename = filename; + } + + } + + /** + * Contains the information needed to access a sound resource; the name of + * the package that contains the resource and the resID of the resource + * within that package. + */ + private static class SoundResource { + public String mSourcePackageName = null; + public int mResId = -1; + public String mFilename = null; + + public SoundResource(String packageName, int id) { + mSourcePackageName = packageName; + mResId = id; + mFilename = null; + } + + public SoundResource(String file) { + mSourcePackageName = null; + mResId = -1; + mFilename = file; + } + } + + private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; + private static final int MAX_FILENAME_LENGTH = 250; + + private static final String ACTION = "android.intent.action.START_TTS_SERVICE"; + private static final String CATEGORY = "android.intent.category.TTS"; + private static final String PKGNAME = "android.tts"; + + final RemoteCallbackList<android.speech.tts.ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>(); + + private Boolean mIsSpeaking; + private ArrayList<SpeechItem> mSpeechQueue; + private HashMap<String, SoundResource> mEarcons; + private HashMap<String, SoundResource> mUtterances; + private MediaPlayer mPlayer; + private TtsService mSelf; + + private ContentResolver mResolver; + + private final ReentrantLock speechQueueLock = new ReentrantLock(); + private final ReentrantLock synthesizerLock = new ReentrantLock(); + + private SynthProxy nativeSynth; + @Override + public void onCreate() { + super.onCreate(); + //Log.i("TTS", "TTS starting"); + + mResolver = getContentResolver(); + + String soLibPath = "/system/lib/libttspico.so"; + nativeSynth = new SynthProxy(soLibPath); + + mSelf = this; + mIsSpeaking = false; + + mEarcons = new HashMap<String, SoundResource>(); + mUtterances = new HashMap<String, SoundResource>(); + + mSpeechQueue = new ArrayList<SpeechItem>(); + mPlayer = null; + + setDefaultSettings(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + // Don't hog the media player + cleanUpPlayer(); + + nativeSynth.shutdown(); + + // Unregister all callbacks. + mCallbacks.kill(); + } + + + private void setDefaultSettings() { + setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant()); + + // speech rate + setSpeechRate(getDefaultRate()); + } + + + private boolean isDefaultEnforced() { + return (android.provider.Settings.Secure.getInt(mResolver, + android.provider.Settings.Secure.TTS_USE_DEFAULTS, + TextToSpeech.Engine.FALLBACK_TTS_USE_DEFAULTS) + == 1 ); + } + + + private int getDefaultRate() { + return android.provider.Settings.Secure.getInt(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_RATE, + TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_RATE); + } + + + private String getDefaultLanguage() { + String defaultLang = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_LANG); + if (defaultLang == null) { + // no setting found, use the current Locale to determine the default language + return Locale.getDefault().getISO3Language(); + } else { + return defaultLang; + } + } + + + private String getDefaultCountry() { + String defaultCountry = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY); + if (defaultCountry == null) { + // no setting found, use the current Locale to determine the default country + return Locale.getDefault().getISO3Country(); + } else { + return defaultCountry; + } + } + + + private String getDefaultLocVariant() { + String defaultVar = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_VARIANT); + if (defaultVar == null) { + // no setting found, use the current Locale to determine the default variant + return Locale.getDefault().getVariant(); + } else { + return defaultVar; + } + } + + + private int setSpeechRate(int rate) { + if (isDefaultEnforced()) { + return nativeSynth.setSpeechRate(getDefaultRate()); + } else { + return nativeSynth.setSpeechRate(rate); + } + } + + + private int setPitch(int pitch) { + return nativeSynth.setPitch(pitch); + } + + + private int isLanguageAvailable(String lang, String country, String variant) { + //Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")"); + return nativeSynth.isLanguageAvailable(lang, country, variant); + } + + + private String[] getLanguage() { + return nativeSynth.getLanguage(); + } + + + private int setLanguage(String lang, String country, String variant) { + //Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")"); + if (isDefaultEnforced()) { + return nativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(), + getDefaultLocVariant()); + } else { + return nativeSynth.setLanguage(lang, country, variant); + } + } + + + /** + * Adds a sound resource to the TTS. + * + * @param text + * The text that should be associated with the sound resource + * @param packageName + * The name of the package which has the sound resource + * @param resId + * The resource ID of the sound within its package + */ + private void addSpeech(String text, String packageName, int resId) { + mUtterances.put(text, new SoundResource(packageName, resId)); + } + + /** + * Adds a sound resource to the TTS. + * + * @param text + * The text that should be associated with the sound resource + * @param filename + * The filename of the sound resource. This must be a complete + * path like: (/sdcard/mysounds/mysoundbite.mp3). + */ + private void addSpeech(String text, String filename) { + mUtterances.put(text, new SoundResource(filename)); + } + + /** + * Adds a sound resource to the TTS as an earcon. + * + * @param earcon + * The text that should be associated with the sound resource + * @param packageName + * The name of the package which has the sound resource + * @param resId + * The resource ID of the sound within its package + */ + private void addEarcon(String earcon, String packageName, int resId) { + mEarcons.put(earcon, new SoundResource(packageName, resId)); + } + + /** + * Adds a sound resource to the TTS as an earcon. + * + * @param earcon + * The text that should be associated with the sound resource + * @param filename + * The filename of the sound resource. This must be a complete + * path like: (/sdcard/mysounds/mysoundbite.mp3). + */ + private void addEarcon(String earcon, String filename) { + mEarcons.put(earcon, new SoundResource(filename)); + } + + /** + * Speaks the given text using the specified queueing mode and parameters. + * + * @param text + * The 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 int speak(String text, int queueMode, ArrayList<String> params) { + if (queueMode == 0) { + stop(); + } + mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + return TextToSpeech.TTS_SUCCESS; + } + + /** + * Plays the earcon using the specified queueing mode and parameters. + * + * @param earcon + * The earcon that should be played + * @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 int playEarcon(String earcon, int queueMode, + ArrayList<String> params) { + if (queueMode == 0) { + stop(); + } + mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + return TextToSpeech.TTS_SUCCESS; + } + + /** + * Stops all speech output and removes any utterances still in the queue. + */ + private int stop() { + Log.i("TTS", "Stopping"); + mSpeechQueue.clear(); + + int result = nativeSynth.stop(); + mIsSpeaking = false; + if (mPlayer != null) { + try { + mPlayer.stop(); + } catch (IllegalStateException e) { + // Do nothing, the player is already stopped. + } + } + Log.i("TTS", "Stopped"); + return result; + } + + public void onCompletion(MediaPlayer arg0) { + processSpeechQueue(); + } + + private int playSilence(long duration, int queueMode, + ArrayList<String> params) { + if (queueMode == 0) { + stop(); + } + mSpeechQueue.add(new SpeechItem(duration)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + return TextToSpeech.TTS_SUCCESS; + } + + private void silence(final long duration) { + class SilenceThread implements Runnable { + public void run() { + try { + Thread.sleep(duration); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + processSpeechQueue(); + } + } + } + Thread slnc = (new Thread(new SilenceThread())); + slnc.setPriority(Thread.MIN_PRIORITY); + slnc.start(); + } + + private void speakInternalOnly(final String text, + final ArrayList<String> params) { + class SynthThread implements Runnable { + public void run() { + boolean synthAvailable = false; + try { + synthAvailable = synthesizerLock.tryLock(); + if (!synthAvailable) { + Thread.sleep(100); + Thread synth = (new Thread(new SynthThread())); + synth.setPriority(Thread.MIN_PRIORITY); + synth.start(); + return; + } + if (params != null){ + String language = ""; + String country = ""; + String variant = ""; + for (int i = 0; i < params.size() - 1; i = i + 2){ + String param = params.get(i); + if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ + setSpeechRate(Integer.parseInt(params.get(i+1))); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ + language = params.get(i+1); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ + country = params.get(i+1); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){ + variant = params.get(i+1); + } + } + if (language.length() > 0){ + setLanguage(language, country, variant); + } + } + nativeSynth.speak(text); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + // This check is needed because finally will always run; + // even if the + // method returns somewhere in the try block. + if (synthAvailable) { + synthesizerLock.unlock(); + } + processSpeechQueue(); + } + } + } + Thread synth = (new Thread(new SynthThread())); + synth.setPriority(Thread.MIN_PRIORITY); + synth.start(); + } + + private void synthToFileInternalOnly(final String text, + final ArrayList<String> params, final String filename) { + class SynthThread implements Runnable { + public void run() { + Log.i("TTS", "Synthesizing to " + filename); + boolean synthAvailable = false; + try { + synthAvailable = synthesizerLock.tryLock(); + if (!synthAvailable) { + Thread.sleep(100); + Thread synth = (new Thread(new SynthThread())); + synth.setPriority(Thread.MIN_PRIORITY); + synth.start(); + return; + } + if (params != null){ + String language = ""; + String country = ""; + String variant = ""; + for (int i = 0; i < params.size() - 1; i = i + 2){ + String param = params.get(i); + if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ + setSpeechRate(Integer.parseInt(params.get(i+1))); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ + language = params.get(i+1); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ + country = params.get(i+1); + } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){ + variant = params.get(i+1); + } + } + if (language.length() > 0){ + setLanguage(language, country, variant); + } + } + nativeSynth.synthesizeToFile(text, filename); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + // This check is needed because finally will always run; + // even if the + // method returns somewhere in the try block. + if (synthAvailable) { + synthesizerLock.unlock(); + } + processSpeechQueue(); + } + } + } + Thread synth = (new Thread(new SynthThread())); + synth.setPriority(Thread.MIN_PRIORITY); + synth.start(); + } + + private SoundResource getSoundResource(SpeechItem speechItem) { + SoundResource sr = null; + String text = speechItem.mText; + if (speechItem.mType == SpeechItem.SILENCE) { + // Do nothing if this is just silence + } else if (speechItem.mType == SpeechItem.EARCON) { + sr = mEarcons.get(text); + } else { + sr = mUtterances.get(text); + } + 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. + final int N = mCallbacks.beginBroadcast(); + for (int i = 0; i < N; i++) { + try { + mCallbacks.getBroadcastItem(i).markReached(mark); + } catch (RemoteException e) { + // The RemoteCallbackList will take care of removing + // the dead object for us. + } + } + mCallbacks.finishBroadcast(); + 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 { + speechQueueAvailable = speechQueueLock.tryLock(); + if (!speechQueueAvailable) { + return; + } + if (mSpeechQueue.size() < 1) { + mIsSpeaking = false; + broadcastTtsQueueProcessingCompleted(); + return; + } + + SpeechItem currentSpeechItem = mSpeechQueue.get(0); + mIsSpeaking = true; + SoundResource sr = getSoundResource(currentSpeechItem); + // Synth speech as needed - synthesizer should call + // processSpeechQueue to continue running the queue + Log.i("TTS processing: ", currentSpeechItem.mText); + if (sr == null) { + if (currentSpeechItem.mType == SpeechItem.TEXT) { + currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem); + speakInternalOnly(currentSpeechItem.mText, + currentSpeechItem.mParams); + } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) { + synthToFileInternalOnly(currentSpeechItem.mText, + currentSpeechItem.mParams, currentSpeechItem.mFilename); + } else { + // This is either silence or an earcon that was missing + silence(currentSpeechItem.mDuration); + } + } else { + cleanUpPlayer(); + if (sr.mSourcePackageName == PKGNAME) { + // Utterance is part of the TTS library + mPlayer = MediaPlayer.create(this, sr.mResId); + } else if (sr.mSourcePackageName != null) { + // Utterance is part of the app calling the library + Context ctx; + try { + ctx = this.createPackageContext(sr.mSourcePackageName, + 0); + } catch (NameNotFoundException e) { + e.printStackTrace(); + mSpeechQueue.remove(0); // Remove it from the queue and + // move on + mIsSpeaking = false; + return; + } + mPlayer = MediaPlayer.create(ctx, sr.mResId); + } else { + // Utterance is coming from a file + mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename)); + } + + // Check if Media Server is dead; if it is, clear the queue and + // give up for now - hopefully, it will recover itself. + if (mPlayer == null) { + mSpeechQueue.clear(); + mIsSpeaking = false; + return; + } + mPlayer.setOnCompletionListener(this); + try { + mPlayer.start(); + } catch (IllegalStateException e) { + mSpeechQueue.clear(); + mIsSpeaking = false; + cleanUpPlayer(); + return; + } + } + if (mSpeechQueue.size() > 0) { + mSpeechQueue.remove(0); + } + } finally { + // This check is needed because finally will always run; even if the + // method returns somewhere in the try block. + if (speechQueueAvailable) { + speechQueueLock.unlock(); + } + } + } + + private void cleanUpPlayer() { + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } + + /** + * Synthesizes the given text to a file using the specified parameters. + * + * @param text + * The String of 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 synthesizeToFile(String text, ArrayList<String> params, + String filename) { + // Don't allow a filename that is too long + if (filename.length() > MAX_FILENAME_LENGTH) { + return false; + } + // Don't allow anything longer than the max text length; since this + // is synthing to a file, don't even bother splitting it. + if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){ + return false; + } + mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT_TO_FILE, filename)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + return true; + } + + @Override + public IBinder onBind(Intent intent) { + if (ACTION.equals(intent.getAction())) { + for (String category : intent.getCategories()) { + if (category.equals(CATEGORY)) { + return mBinder; + } + } + } + return null; + } + + private final android.speech.tts.ITts.Stub mBinder = new Stub() { + + public void registerCallback(ITtsCallback cb) { + if (cb != null) + mCallbacks.register(cb); + } + + public void unregisterCallback(ITtsCallback cb) { + if (cb != null) + mCallbacks.unregister(cb); + } + + /** + * Speaks the given text using the specified queueing mode and + * parameters. + * + * @param text + * The 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 int speak(String text, int queueMode, String[] params) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.speak(text, queueMode, speakingParams); + } + + /** + * Plays the earcon using the specified queueing mode and parameters. + * + * @param earcon + * The earcon that should be played + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. + */ + public int playEarcon(String earcon, int queueMode, String[] params) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.playEarcon(earcon, queueMode, speakingParams); + } + + /** + * Plays the silence using the specified queueing mode and parameters. + * + * @param duration + * The duration of the silence that should be played + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. + */ + public int playSilence(long duration, int queueMode, String[] params) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.playSilence(duration, queueMode, speakingParams); + } + + /** + * Stops all speech output and removes any utterances still in the + * queue. + */ + public int stop() { + return mSelf.stop(); + } + + /** + * Returns whether or not the TTS is speaking. + * + * @return Boolean to indicate whether or not the TTS is speaking + */ + public boolean isSpeaking() { + return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1)); + } + + /** + * Adds a sound resource to the TTS. + * + * @param text + * The text that should be associated with the sound resource + * @param packageName + * The name of the package which has the sound resource + * @param resId + * The resource ID of the sound within its package + */ + public void addSpeech(String text, String packageName, int resId) { + mSelf.addSpeech(text, packageName, resId); + } + + /** + * Adds a sound resource to the TTS. + * + * @param text + * The text that should be associated with the sound resource + * @param filename + * The filename of the sound resource. This must be a + * complete path like: (/sdcard/mysounds/mysoundbite.mp3). + */ + public void addSpeechFile(String text, String filename) { + mSelf.addSpeech(text, filename); + } + + /** + * Adds a sound resource to the TTS as an earcon. + * + * @param earcon + * The text that should be associated with the sound resource + * @param packageName + * The name of the package which has the sound resource + * @param resId + * The resource ID of the sound within its package + */ + public void addEarcon(String earcon, String packageName, int resId) { + mSelf.addEarcon(earcon, packageName, resId); + } + + /** + * Adds a sound resource to the TTS as an earcon. + * + * @param earcon + * The text that should be associated with the sound resource + * @param filename + * The filename of the sound resource. This must be a + * complete path like: (/sdcard/mysounds/mysoundbite.mp3). + */ + public void addEarconFile(String earcon, String filename) { + mSelf.addEarcon(earcon, filename); + } + + /** + * Sets the speech rate for the TTS. Note that this will only have an + * effect on synthesized speech; it will not affect pre-recorded speech. + * + * @param speechRate + * The speech rate that should be used + */ + public int setSpeechRate(int speechRate) { + return mSelf.setSpeechRate(speechRate); + } + + /** + * 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 int setPitch(int pitch) { + return mSelf.setPitch(pitch); + } + + /** + * Returns the level of support for the specified language. + * + * @param lang the three letter ISO language code. + * @param country the three letter ISO country code. + * @param variant the variant code associated with the country and language pair. + * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, + * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in + * android.speech.tts.TextToSpeech. + */ + public int isLanguageAvailable(String lang, String country, String variant) { + return mSelf.isLanguageAvailable(lang, country, variant); + } + + /** + * Returns the currently set language / country / variant strings representing the + * language used by the TTS engine. + * @return null is no language is set, or an array of 3 string containing respectively + * the language, country and variant. + */ + public String[] getLanguage() { + return mSelf.getLanguage(); + } + + /** + * Sets the speech rate for the TTS, which affects the synthesized voice. + * + * @param lang the three letter ISO language code. + * @param country the three letter ISO country code. + * @param variant the variant code associated with the country and language pair. + */ + public int setLanguage(String lang, String country, String variant) { + return mSelf.setLanguage(lang, country, variant); + } + + /** + * Synthesizes the given text to a file using the specified + * parameters. + * + * @param text + * The String of 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 synthesizeToFile(String text, String[] params, + String filename) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.synthesizeToFile(text, speakingParams, filename); + } + + }; + +} diff --git a/packages/VpnServices/Android.mk b/packages/VpnServices/Android.mk new file mode 100644 index 000000000000..eb27ed5d2171 --- /dev/null +++ b/packages/VpnServices/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := user + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := + +LOCAL_PACKAGE_NAME := VpnServices +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +######################## +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/VpnServices/AndroidManifest.xml b/packages/VpnServices/AndroidManifest.xml new file mode 100644 index 000000000000..6092e302c9bf --- /dev/null +++ b/packages/VpnServices/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.vpn" + android:sharedUserId="android.uid.system" + > + <application android:label="@string/app_label"> + + <service android:name=".VpnServiceBinder" android:process=":remote"> + <intent-filter> + <!-- These are the interfaces supported by the service, which + you can bind to. --> + <action android:name="android.net.vpn.IVpnService" /> + <!-- This is an action code you can use to select the service + without explicitly supplying the implementation class. --> + <action android:name="android.net.vpn.SERVICE" /> + </intent-filter> + </service> + + </application> + + <uses-permission android:name="android.permission.INTERNET"></uses-permission> +</manifest> diff --git a/packages/VpnServices/MODULE_LICENSE_APACHE2 b/packages/VpnServices/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/packages/VpnServices/MODULE_LICENSE_APACHE2 diff --git a/packages/VpnServices/NOTICE b/packages/VpnServices/NOTICE new file mode 100644 index 000000000000..c5b1efa7aac7 --- /dev/null +++ b/packages/VpnServices/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/packages/VpnServices/res/drawable/vpn_connected.png b/packages/VpnServices/res/drawable/vpn_connected.png Binary files differnew file mode 100644 index 000000000000..65fc6db787bf --- /dev/null +++ b/packages/VpnServices/res/drawable/vpn_connected.png diff --git a/packages/VpnServices/res/drawable/vpn_disconnected.png b/packages/VpnServices/res/drawable/vpn_disconnected.png Binary files differnew file mode 100644 index 000000000000..2440c6909ef5 --- /dev/null +++ b/packages/VpnServices/res/drawable/vpn_disconnected.png diff --git a/packages/VpnServices/res/values/strings.xml b/packages/VpnServices/res/values/strings.xml new file mode 100755 index 000000000000..074655e1d083 --- /dev/null +++ b/packages/VpnServices/res/values/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Title for the VPN Services activity. --> + <string name="app_label">VPN Services</string> + + <string name="vpn_notification_title_connected">%s VPN connected</string> + <string name="vpn_notification_title_disconnected">%s VPN disconnected</string> + <string name="vpn_notification_hint_disconnected">Touch to reconnect to a VPN.</string> +</resources> + diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java new file mode 100644 index 000000000000..7dd9d9ea4744 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java @@ -0,0 +1,243 @@ +/* + * 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.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.SystemProperties; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Proxy to start, stop and interact with an Android service defined in init.rc. + * The android service is expected to accept connection through Unix domain + * socket. When the proxy successfully starts the service, it will establish a + * socket connection with the service. The socket serves two purposes: (1) send + * commands to the service; (2) for the proxy to know whether the service is + * alive. + * + * After the service receives commands from the proxy, it should return either + * 0 if the service will close the socket (and the proxy will re-establish + * another connection immediately after), or 1 if the socket is remained alive. + */ +public class AndroidServiceProxy extends ProcessProxy { + private static final int WAITING_TIME = 15; // sec + + private static final String SVC_STATE_CMD_PREFIX = "init.svc."; + private static final String SVC_START_CMD = "ctl.start"; + private static final String SVC_STOP_CMD = "ctl.stop"; + private static final String SVC_STATE_RUNNING = "running"; + private static final String SVC_STATE_STOPPED = "stopped"; + + private static final int END_OF_ARGUMENTS = 255; + + private String mServiceName; + private String mSocketName; + private LocalSocket mKeepaliveSocket; + private boolean mControlSocketInUse; + private Integer mSocketResult = null; + private String mTag; + + /** + * Creates a proxy with the service name. + * @param serviceName the service name + */ + public AndroidServiceProxy(String serviceName) { + mServiceName = serviceName; + mSocketName = serviceName; + mTag = "SProxy_" + serviceName; + } + + @Override + public String getName() { + return "Service " + mServiceName; + } + + @Override + public synchronized void stop() { + if (isRunning()) setResultAndCloseControlSocket(-1); + SystemProperties.set(SVC_STOP_CMD, mServiceName); + } + + /** + * Sends a command with arguments to the service through the control socket. + */ + public void sendCommand(String ...args) throws IOException { + OutputStream out = getControlSocketOutput(); + for (String arg : args) outputString(out, arg); + out.write(END_OF_ARGUMENTS); + out.flush(); + checkSocketResult(); + } + + /** + * {@inheritDoc} + * The method returns when the service exits. + */ + @Override + protected void performTask() throws IOException { + String svc = mServiceName; + Log.d(mTag, "+++++ Execute: " + svc); + SystemProperties.set(SVC_START_CMD, svc); + + boolean success = blockUntil(SVC_STATE_RUNNING, WAITING_TIME); + + if (success) { + Log.d(mTag, "----- Running: " + svc + ", create keepalive socket"); + LocalSocket s = mKeepaliveSocket = createServiceSocket(); + setState(ProcessState.RUNNING); + + if (s == null) { + // no socket connection, stop hosting the service + stop(); + return; + } + try { + for (;;) { + InputStream in = s.getInputStream(); + int data = in.read(); + if (data >= 0) { + Log.d(mTag, "got data from keepalive socket: " + data); + + if (data == 0) { + // re-establish the connection: + // synchronized here so that checkSocketResult() + // returns when new mKeepaliveSocket is available for + // next cmd + synchronized (this) { + setResultAndCloseControlSocket((byte) data); + s = mKeepaliveSocket = createServiceSocket(); + } + } else { + // keep the socket + setSocketResult(data); + } + } else { + // service is gone + if (mControlSocketInUse) setSocketResult(-1); + break; + } + } + Log.d(mTag, "keepalive connection closed"); + } catch (IOException e) { + Log.d(mTag, "keepalive socket broken: " + e.getMessage()); + } + + // Wait 5 seconds for the service to exit + success = blockUntil(SVC_STATE_STOPPED, 5); + Log.d(mTag, "stopping " + svc + ", success? " + success); + } else { + setState(ProcessState.STOPPED); + throw new IOException("cannot start service: " + svc); + } + } + + private LocalSocket createServiceSocket() throws IOException { + LocalSocket s = new LocalSocket(); + LocalSocketAddress a = new LocalSocketAddress(mSocketName, + LocalSocketAddress.Namespace.RESERVED); + + // try a few times in case the service has not listen()ed + IOException excp = null; + for (int i = 0; i < 10; i++) { + try { + s.connect(a); + return s; + } catch (IOException e) { + Log.d(mTag, "service not yet listen()ing; try again"); + excp = e; + sleep(500); + } + } + throw excp; + } + + private OutputStream getControlSocketOutput() throws IOException { + if (mKeepaliveSocket != null) { + mControlSocketInUse = true; + mSocketResult = null; + return mKeepaliveSocket.getOutputStream(); + } else { + throw new IOException("no control socket available"); + } + } + + private synchronized void checkSocketResult() throws IOException { + try { + // will be notified when the result comes back from service + if (mSocketResult == null) wait(); + } catch (InterruptedException e) { + Log.d(mTag, "checkSocketResult(): " + e); + } finally { + mControlSocketInUse = false; + if ((mSocketResult == null) || (mSocketResult < 0)) { + throw new IOException("socket error, result from service: " + + mSocketResult); + } + } + } + + private synchronized void setSocketResult(int result) { + if (mControlSocketInUse) { + mSocketResult = result; + notifyAll(); + } + } + + private void setResultAndCloseControlSocket(int result) { + setSocketResult(result); + try { + mKeepaliveSocket.shutdownInput(); + mKeepaliveSocket.shutdownOutput(); + mKeepaliveSocket.close(); + } catch (IOException e) { + Log.e(mTag, "close keepalive socket", e); + } finally { + mKeepaliveSocket = null; + } + } + + /** + * Waits for the process to be in the expected state. The method returns + * false if after the specified duration (in seconds), the process is still + * not in the expected state. + */ + private boolean blockUntil(String expectedState, int waitTime) { + String cmd = SVC_STATE_CMD_PREFIX + mServiceName; + int sleepTime = 200; // ms + int n = waitTime * 1000 / sleepTime; + for (int i = 0; i < n; i++) { + if (expectedState.equals(SystemProperties.get(cmd))) { + Log.d(mTag, mServiceName + " is " + expectedState + " after " + + (i * sleepTime) + " msec"); + break; + } + sleep(sleepTime); + } + return expectedState.equals(SystemProperties.get(cmd)); + } + + private void outputString(OutputStream out, String s) throws IOException { + byte[] bytes = s.getBytes(); + out.write(bytes.length); + out.write(bytes); + out.flush(); + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java new file mode 100644 index 000000000000..6abf81c60c6f --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vpn; + +import android.net.vpn.L2tpIpsecPskProfile; + +import java.io.IOException; + +/** + * The service that manages the preshared key based L2TP-over-IPSec VPN + * connection. + */ +class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> { + private static final String IPSEC_DAEMON = "racoon"; + + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + String hostIp = getHostIp(); + L2tpIpsecPskProfile p = getProfile(); + + // IPSEC + AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON); + ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT, + p.getPresharedKey()); + + sleep(2000); // 2 seconds + + // L2TP + MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp, + L2tpService.L2TP_PORT, + (p.isSecretEnabled() ? p.getSecretString() : null), + username, password); + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java new file mode 100644 index 000000000000..825953c33ac9 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vpn; + +import android.net.vpn.L2tpIpsecProfile; +import android.security.CertTool; + +import java.io.IOException; + +/** + * The service that manages the certificate based L2TP-over-IPSec VPN connection. + */ +class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { + private static final String IPSEC_DAEMON = "racoon"; + + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + String hostIp = getHostIp(); + + // IPSEC + AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON); + ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT, + getUserkeyPath(), getUserCertPath(), getCaCertPath()); + + sleep(2000); // 2 seconds + + // L2TP + L2tpIpsecProfile p = getProfile(); + MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp, + L2tpService.L2TP_PORT, + (p.isSecretEnabled() ? p.getSecretString() : null), + username, password); + } + + private String getCaCertPath() { + return CertTool.getInstance().getCaCertificate( + getProfile().getCaCertificate()); + } + + private String getUserCertPath() { + return CertTool.getInstance().getUserCertificate( + getProfile().getUserCertificate()); + } + + private String getUserkeyPath() { + return CertTool.getInstance().getUserPrivateKey( + getProfile().getUserCertificate()); + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java new file mode 100644 index 000000000000..9273f3557a8e --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java @@ -0,0 +1,38 @@ +/* + * 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.L2tpProfile; + +import java.io.IOException; + +/** + * The service that manages the L2TP VPN connection. + */ +class L2tpService extends VpnService<L2tpProfile> { + static final String L2TP_DAEMON = "l2tp"; + static final String L2TP_PORT = "1701"; + + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + 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 000000000000..16d253a41b11 --- /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.sendCommand(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 new file mode 100644 index 000000000000..f0bbc3488798 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java @@ -0,0 +1,85 @@ +/* + * 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.util.Log; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; + +/** + * Proxy to perform a command with arguments. + */ +public class NormalProcessProxy extends ProcessProxy { + private Process mProcess; + private String[] mArgs; + private String mTag; + + /** + * Creates a proxy with the arguments. + * @param args the argument list with the first one being the command + */ + public NormalProcessProxy(String ...args) { + if ((args == null) || (args.length == 0)) { + throw new IllegalArgumentException(); + } + mArgs = args; + mTag = "PProxy_" + getName(); + } + + @Override + public String getName() { + return mArgs[0]; + } + + @Override + public synchronized void stop() { + if (isStopped()) return; + getHostThread().interrupt(); + // TODO: not sure how to reliably kill a process + mProcess.destroy(); + setState(ProcessState.STOPPING); + } + + @Override + protected void performTask() throws IOException, InterruptedException { + String[] args = mArgs; + Log.d(mTag, "+++++ Execute: " + getEntireCommand()); + ProcessBuilder pb = new ProcessBuilder(args); + setState(ProcessState.RUNNING); + Process p = mProcess = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + p.getInputStream())); + while (true) { + String line = reader.readLine(); + if ((line == null) || isStopping()) break; + Log.d(mTag, line); + } + + Log.d(mTag, "----- p.waitFor(): " + getName()); + p.waitFor(); + Log.d(mTag, "----- Done: " + getName()); + } + + private CharSequence getEntireCommand() { + String[] args = mArgs; + StringBuilder sb = new StringBuilder(args[0]); + for (int i = 1; i < args.length; i++) sb.append(' ').append(args[i]); + return sb; + } +} 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 000000000000..01362a55e46b --- /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 new file mode 100644 index 000000000000..50fbf4b87822 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java @@ -0,0 +1,210 @@ +/* + * 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.os.ConditionVariable; + +import java.io.IOException; + +/** + * A proxy class that spawns a process to accomplish a certain task. + */ +public abstract class ProcessProxy { + /** + * Defines the interface to call back when the process is finished or an + * error occurs during execution. + */ + public static interface Callback { + /** + * Called when the process is finished. + * @param proxy the proxy that hosts the process + */ + void done(ProcessProxy proxy); + + /** + * Called when some error occurs. + * @param proxy the proxy that hosts the process + */ + void error(ProcessProxy proxy, Throwable error); + } + + protected enum ProcessState { + STOPPED, STARTING, RUNNING, STOPPING, ERROR + } + + private ProcessState mState = ProcessState.STOPPED; + private ConditionVariable mLock = new ConditionVariable(); + private Thread mThread; + + /** + * Returns the name of the process. + */ + public abstract String getName(); + + /** + * Starts the process with a callback. + * @param callback the callback to get notified when the process is finished + * or an error occurs during execution + * @throws IOException when the process is already running or failed to + * start + */ + public synchronized void start(final Callback callback) throws IOException { + if (!isStopped()) { + throw new IOException("Process is already running: " + this); + } + mLock.close(); + setState(ProcessState.STARTING); + Thread thread = new Thread(new Runnable() { + public void run() { + try { + performTask(); + setState(ProcessState.STOPPED); + mLock.open(); + if (callback != null) callback.done(ProcessProxy.this); + } catch (Throwable e) { + setState(ProcessState.ERROR); + if (callback != null) callback.error(ProcessProxy.this, e); + } finally { + mThread = null; + } + } + }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + mThread = thread; + if (!waitUntilRunning()) { + throw new IOException("Failed to start the process: " + this); + } + } + + /** + * Starts the process. + * @throws IOException when the process is already running or failed to + * start + */ + public synchronized void start() throws IOException { + start(null); + if (!waitUntilDone()) { + throw new IOException("Failed to complete the process: " + this); + } + } + + /** + * Returns the thread that hosts the process. + */ + public Thread getHostThread() { + return mThread; + } + + /** + * Blocks the current thread until the hosted process is finished. + * + * @return true if the process is finished normally; false if an error + * occurs + */ + public boolean waitUntilDone() { + while (!mLock.block(1000)) { + if (isStopped() || isInError()) break; + } + return isStopped(); + } + + /** + * Blocks the current thread until the hosted process is running. + * + * @return true if the process is running normally; false if the process + * is in another state + */ + private boolean waitUntilRunning() { + for (;;) { + if (!isStarting()) break; + } + return isRunning(); + } + + /** + * Stops and destroys the process. + */ + public abstract void stop(); + + /** + * Checks whether the process is finished. + * @return true if the process is stopped + */ + public boolean isStopped() { + return (mState == ProcessState.STOPPED); + } + + /** + * Checks whether the process is being stopped. + * @return true if the process is being stopped + */ + public boolean isStopping() { + return (mState == ProcessState.STOPPING); + } + + /** + * Checks whether the process is being started. + * @return true if the process is being started + */ + public boolean isStarting() { + return (mState == ProcessState.STARTING); + } + + /** + * Checks whether the process is running. + * @return true if the process is running + */ + public boolean isRunning() { + return (mState == ProcessState.RUNNING); + } + + /** + * Checks whether some error has occurred and the process is stopped. + * @return true if some error has occurred and the process is stopped + */ + public boolean isInError() { + return (mState == ProcessState.ERROR); + } + + /** + * Performs the actual task. Subclasses must make sure that the method + * is blocked until the task is done or an error occurs. + */ + protected abstract void performTask() + throws IOException, InterruptedException; + + /** + * Sets the process state. + * @param state the new state to be in + */ + protected void setState(ProcessState state) { + mState = state; + } + + /** + * Makes the current thread sleep for the specified time. + * @param msec time to sleep in miliseconds + */ + protected void sleep(int msec) { + try { + Thread.currentThread().sleep(msec); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java new file mode 100644 index 000000000000..a60788aad24f --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -0,0 +1,520 @@ +/* + * 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.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; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * The service base class for managing a type of VPN connection. + */ +abstract class VpnService<E extends VpnProfile> { + private static final int NOTIFICATION_ID = 1; + + private static final String DNS1 = "net.dns1"; + private static final String DNS2 = "net.dns2"; + private static final String VPN_DNS1 = "vpn.dns1"; + private static final String VPN_DNS2 = "vpn.dns2"; + private static final String VPN_UP = "vpn.up"; + private static final String VPN_IS_UP = "1"; + private static final String VPN_IS_DOWN = "0"; + + private static final String REMOTE_IP = "net.ipremote"; + private static final String DNS_DOMAIN_SUFFICES = "net.dns.search"; + + private final String TAG = VpnService.class.getSimpleName(); + + E mProfile; + VpnServiceBinder mContext; + + private VpnState mState = VpnState.IDLE; + private boolean mInError; + + // connection settings + private String mOriginalDns1; + private String mOriginalDns2; + private String mVpnDns1 = ""; + private String mVpnDns2 = ""; + private String mOriginalDomainSuffices; + private String mHostIp; + + private long mStartTime; // VPN connection start time + + // for helping managing multiple Android services + private ServiceHelper mServiceHelper = new ServiceHelper(); + + // for helping showing, updating notification + private NotificationHelper mNotification = new NotificationHelper(); + + /** + * Establishes a VPN connection with the specified username and password. + */ + protected abstract void connect(String serverIp, String username, + String password) throws IOException; + + /** + * Tears down the VPN connection. The base class simply terminates all the + * Android services. A subclass may need to do some clean-up before that. + */ + protected void disconnect() { + } + + /** + * Starts an Android service defined in init.rc. + */ + protected AndroidServiceProxy startService(String serviceName) + throws IOException { + return mServiceHelper.startService(serviceName); + } + + /** + * Returns the VPN profile associated with the connection. + */ + protected E getProfile() { + return mProfile; + } + + /** + * Returns the host IP for establishing the VPN connection. + */ + protected String getHostIp() throws IOException { + if (mHostIp == null) mHostIp = reallyGetHostIp(); + return mHostIp; + } + + /** + * Returns the IP address of the specified host name. + */ + protected String getIp(String hostName) throws IOException { + 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(); + } + throw new IOException("Default gateway is not available"); + } + + /** + * Sets the system property. The method is blocked until the value is + * settled in. + * @param name the name of the property + * @param value the value of the property + * @throws IOException if it fails to set the property within 2 seconds + */ + protected void setSystemProperty(String name, String value) + throws IOException { + SystemProperties.set(name, value); + for (int i = 0; i < 5; i++) { + String v = SystemProperties.get(name); + if (v.equals(value)) { + return; + } else { + Log.d(TAG, "sys_prop: wait for " + name + " to settle in"); + sleep(400); + } + } + throw new IOException("Failed to set system property: " + name); + } + + void setContext(VpnServiceBinder context, E profile) { + mContext = context; + mProfile = profile; + } + + VpnState getState() { + return mState; + } + + synchronized void onConnect(String username, String password) + throws IOException { + mState = VpnState.CONNECTING; + broadcastConnectivity(VpnState.CONNECTING); + + String serverIp = getIp(getProfile().getServerName()); + + onBeforeConnect(); + connect(serverIp, username, password); + waitUntilConnectedOrTimedout(); + } + + synchronized void onDisconnect(boolean cleanUpServices) { + try { + mState = VpnState.DISCONNECTING; + broadcastConnectivity(VpnState.DISCONNECTING); + mNotification.showDisconnect(); + + // subclass implementation + if (cleanUpServices) disconnect(); + + mServiceHelper.stop(); + } catch (Throwable e) { + Log.e(TAG, "onError()", e); + onFinalCleanUp(); + } + } + + synchronized void onError() { + // error may occur during or after connection setup + // and it may be due to one or all services gone + mInError = true; + switch (mState) { + case CONNECTED: + onDisconnect(true); + break; + + case CONNECTING: + onDisconnect(false); + break; + } + } + + private void onBeforeConnect() { + mNotification.disableNotification(); + + SystemProperties.set(VPN_DNS1, "-"); + SystemProperties.set(VPN_DNS2, "-"); + SystemProperties.set(VPN_UP, VPN_IS_DOWN); + Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_UP)); + } + + private void waitUntilConnectedOrTimedout() { + sleep(2000); // 2 seconds + for (int i = 0; i < 60; i++) { + if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) { + onConnected(); + return; + } + sleep(500); // 0.5 second + } + + synchronized (this) { + if (mState == VpnState.CONNECTING) { + Log.d(TAG, " connecting timed out !!"); + onError(); + } + } + } + + private synchronized void onConnected() { + Log.d(TAG, "onConnected()"); + + saveVpnDnsProperties(); + saveAndSetDomainSuffices(); + startConnectivityMonitor(); + + mState = VpnState.CONNECTED; + broadcastConnectivity(VpnState.CONNECTED); + } + + private synchronized void onFinalCleanUp() { + Log.d(TAG, "onFinalCleanUp()"); + + if (mState == VpnState.IDLE) return; + + // keep the notification when error occurs + if (!mInError) mNotification.disableNotification(); + + restoreOriginalDnsProperties(); + restoreOriginalDomainSuffices(); + mState = VpnState.IDLE; + broadcastConnectivity(VpnState.IDLE); + + // stop the service itself + mContext.stopSelf(); + } + + private synchronized void onOneServiceGone() { + switch (mState) { + case IDLE: + case DISCONNECTING: + break; + + default: + onError(); + } + } + + private synchronized void onAllServicesGone() { + switch (mState) { + case IDLE: + break; + + case DISCONNECTING: + // daemons are gone; now clean up everything + onFinalCleanUp(); + break; + + default: + onError(); + } + } + + private void restoreOriginalDnsProperties() { + // restore only if they are not overridden + if (mVpnDns1.equals(SystemProperties.get(DNS1))) { + Log.d(TAG, String.format("restore original dns prop: %s --> %s", + SystemProperties.get(DNS1), mOriginalDns1)); + Log.d(TAG, String.format("restore original dns prop: %s --> %s", + SystemProperties.get(DNS2), mOriginalDns2)); + SystemProperties.set(DNS1, mOriginalDns1); + SystemProperties.set(DNS2, mOriginalDns2); + } + } + + private void saveVpnDnsProperties() { + mOriginalDns1 = mOriginalDns2 = ""; + for (int i = 0; i < 10; i++) { + mVpnDns1 = SystemProperties.get(VPN_DNS1); + mVpnDns2 = SystemProperties.get(VPN_DNS2); + if (mOriginalDns1.equals(mVpnDns1)) { + Log.d(TAG, "wait for vpn dns to settle in..." + i); + sleep(500); + } else { + mOriginalDns1 = SystemProperties.get(DNS1); + mOriginalDns2 = SystemProperties.get(DNS2); + SystemProperties.set(DNS1, mVpnDns1); + SystemProperties.set(DNS2, mVpnDns2); + Log.d(TAG, String.format("save original dns prop: %s, %s", + mOriginalDns1, mOriginalDns2)); + Log.d(TAG, String.format("set vpn dns prop: %s, %s", + mVpnDns1, mVpnDns2)); + return; + } + } + Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??"); + } + + private void saveAndSetDomainSuffices() { + mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES); + Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices); + String list = mProfile.getDomainSuffices(); + if (!TextUtils.isEmpty(list)) { + SystemProperties.set(DNS_DOMAIN_SUFFICES, list); + } + } + + private void restoreOriginalDomainSuffices() { + Log.d(TAG, "restore original dns search --> " + mOriginalDomainSuffices); + SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices); + } + + private void broadcastConnectivity(VpnState s) { + new VpnManager(mContext).broadcastConnectivity(mProfile.getName(), s); + } + + private void startConnectivityMonitor() { + mStartTime = System.currentTimeMillis(); + + new Thread(new Runnable() { + public void run() { + Log.d(TAG, " +++++ connectivity monitor running"); + try { + for (;;) { + synchronized (VpnService.this) { + if (mState != VpnState.CONNECTED) break; + mNotification.update(); + checkConnectivity(); + VpnService.this.wait(1000); // 1 second + } + } + } catch (InterruptedException e) { + Log.e(TAG, "connectivity monitor", e); + } + Log.d(TAG, " ----- connectivity monitor stopped"); + } + }).start(); + } + + private void checkConnectivity() { + checkDnsProperties(); + } + + private void checkDnsProperties() { + String dns1 = SystemProperties.get(DNS1); + if (!mVpnDns1.equals(dns1)) { + Log.w(TAG, " @@ !!! dns being overridden"); + onError(); + } + } + + private String reallyGetHostIp() throws IOException { + 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"); + } + + protected void sleep(int ms) { + try { + Thread.currentThread().sleep(ms); + } catch (InterruptedException e) { + } + } + + 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 { + private List<AndroidServiceProxy> mServiceList = + new ArrayList<AndroidServiceProxy>(); + + // starts an Android service + AndroidServiceProxy startService(String serviceName) + throws IOException { + AndroidServiceProxy service = new AndroidServiceProxy(serviceName); + mServiceList.add(service); + service.start(this); + return service; + } + + // stops all the Android services + void stop() { + if (mServiceList.isEmpty()) { + onFinalCleanUp(); + } else { + for (AndroidServiceProxy s : mServiceList) s.stop(); + } + } + + //@Override + public void done(ProcessProxy p) { + Log.d(TAG, "service done: " + p.getName()); + commonCallback((AndroidServiceProxy) p); + } + + //@Override + public void error(ProcessProxy p, Throwable e) { + Log.e(TAG, "service error: " + p.getName(), e); + commonCallback((AndroidServiceProxy) p); + } + + private void commonCallback(AndroidServiceProxy service) { + mServiceList.remove(service); + onOneServiceGone(); + if (mServiceList.isEmpty()) onAllServicesGone(); + } + } + + // Helper class for showing, updating notification. + private class NotificationHelper { + void update() { + String title = getNotificationTitle(true); + Notification n = new Notification(R.drawable.vpn_connected, title, + mStartTime); + n.setLatestEventInfo(mContext, title, + getNotificationMessage(true), prepareNotificationIntent()); + n.flags |= Notification.FLAG_NO_CLEAR; + n.flags |= Notification.FLAG_ONGOING_EVENT; + enableNotification(n); + } + + void showDisconnect() { + String title = getNotificationTitle(false); + Notification n = new Notification(R.drawable.vpn_disconnected, + title, System.currentTimeMillis()); + n.setLatestEventInfo(mContext, title, + getNotificationMessage(false), prepareNotificationIntent()); + n.flags |= Notification.FLAG_AUTO_CANCEL; + disableNotification(); + enableNotification(n); + } + + void disableNotification() { + ((NotificationManager) mContext.getSystemService( + Context.NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID); + } + + private void enableNotification(Notification n) { + ((NotificationManager) mContext.getSystemService( + Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, n); + } + + private PendingIntent prepareNotificationIntent() { + return PendingIntent.getActivity(mContext, 0, + new VpnManager(mContext).createSettingsActivityIntent(), 0); + } + + private String getNotificationTitle(boolean connected) { + String formatString = connected + ? mContext.getString( + R.string.vpn_notification_title_connected) + : mContext.getString( + R.string.vpn_notification_title_disconnected); + return String.format(formatString, mProfile.getName()); + } + + private String getFormattedTime(long duration) { + long hours = duration / 3600; + StringBuilder sb = new StringBuilder(); + if (hours > 0) sb.append(hours).append(':'); + sb.append(String.format("%02d:%02d", (duration % 3600 / 60), + (duration % 60))); + return sb.toString(); + } + + private String getNotificationMessage(boolean connected) { + if (connected) { + long time = (System.currentTimeMillis() - mStartTime) / 1000; + return getFormattedTime(time); + } else { + return mContext.getString( + R.string.vpn_notification_hint_disconnected); + } + } + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java new file mode 100644 index 000000000000..617875e64d2c --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -0,0 +1,116 @@ +/* + * 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.app.Service; +import android.content.Intent; +import android.net.vpn.IVpnService; +import android.net.vpn.L2tpIpsecProfile; +import android.net.vpn.L2tpIpsecPskProfile; +import android.net.vpn.L2tpProfile; +import android.net.vpn.PptpProfile; +import android.net.vpn.VpnManager; +import android.net.vpn.VpnProfile; +import android.net.vpn.VpnState; +import android.os.IBinder; +import android.util.Log; + +import java.io.IOException; + +/** + * The service class for managing a VPN connection. It implements the + * {@link IVpnService} binder interface. + */ +public class VpnServiceBinder extends Service { + private final String TAG = VpnServiceBinder.class.getSimpleName(); + + // The actual implementation is delegated to the VpnService class. + private VpnService<? extends VpnProfile> mService; + + private final IBinder mBinder = new IVpnService.Stub() { + public boolean connect(VpnProfile p, String username, String password) { + return VpnServiceBinder.this.connect(p, username, password); + } + + public void disconnect() { + if (mService != null) mService.onDisconnect(true); + } + + public void checkStatus(VpnProfile p) { + VpnServiceBinder.this.checkStatus(p); + } + }; + + public IBinder onBind(Intent intent) { + return mBinder; + } + + private synchronized boolean connect( + VpnProfile p, String username, String password) { + if (mService != null) return false; + try { + mService = createService(p); + mService.onConnect(username, password); + return true; + } catch (Throwable e) { + Log.e(TAG, "connect()", e); + if (mService != null) mService.onError(); + return false; + } + } + + private synchronized void checkStatus(VpnProfile p) { + if (mService == null) broadcastConnectivity(p.getName(), VpnState.IDLE); + + if (!p.getName().equals(mService.mProfile.getName())) { + broadcastConnectivity(p.getName(), VpnState.IDLE); + } else { + broadcastConnectivity(p.getName(), mService.getState()); + } + } + + private VpnService<? extends VpnProfile> createService(VpnProfile p) { + switch (p.getType()) { + case L2TP: + L2tpService l2tp = new L2tpService(); + l2tp.setContext(this, (L2tpProfile) p); + return l2tp; + + case PPTP: + PptpService pptp = new PptpService(); + pptp.setContext(this, (PptpProfile) p); + return pptp; + + case L2TP_IPSEC_PSK: + L2tpIpsecPskService psk = new L2tpIpsecPskService(); + psk.setContext(this, (L2tpIpsecPskProfile) p); + return psk; + + case L2TP_IPSEC: + L2tpIpsecService l2tpIpsec = new L2tpIpsecService(); + l2tpIpsec.setContext(this, (L2tpIpsecProfile) p); + return l2tpIpsec; + + default: + return null; + } + } + + private void broadcastConnectivity(String name, VpnState s) { + new VpnManager(this).broadcastConnectivity(name, s); + } +} diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java new file mode 100644 index 000000000000..c205fc03ac4f --- /dev/null +++ b/services/java/com/android/server/AccessibilityManagerService.java @@ -0,0 +1,668 @@ +/* + ** Copyright 2009, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +package com.android.server; + +import static android.util.Config.LOGV; + +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.HandlerCaller.SomeArgs; + +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.accessibilityservice.IEventListener; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.text.TextUtils; +import android.text.TextUtils.SimpleStringSplitter; +import android.util.Log; +import android.util.SparseArray; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.IAccessibilityManager; +import android.view.accessibility.IAccessibilityManagerClient; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This class is instantiated by the system as a system level service and can be + * accessed only by the system. The task of this service is to be a centralized + * event dispatch for {@link AccessibilityEvent}s generated across all processes + * on the device. Events are dispatched to {@link AccessibilityService}s. + * + * @hide + */ +public class AccessibilityManagerService extends IAccessibilityManager.Stub + implements HandlerCaller.Callback { + + private static final String LOG_TAG = "AccessibilityManagerService"; + + private static int sIdCounter = 0; + + private static final int OWN_PROCESS_ID = android.os.Process.myPid(); + + private static final int DO_SET_SERVICE_INFO = 10; + + final HandlerCaller mCaller; + + final Context mContext; + + final Object mLock = new Object(); + + final List<Service> mServices = new ArrayList<Service>(); + + final List<IAccessibilityManagerClient> mClients = + new ArrayList<IAccessibilityManagerClient>(); + + final Map<ComponentName, Service> mComponentNameToServiceMap = + new HashMap<ComponentName, Service>(); + + private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>(); + + private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); + + private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); + + private PackageManager mPackageManager; + + private int mHandledFeedbackTypes = 0; + + private boolean mIsEnabled; + + /** + * Handler for delayed event dispatch. + */ + private Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message message) { + Service service = (Service) message.obj; + int eventType = message.arg1; + + synchronized (mLock) { + notifyEventListenerLocked(service, eventType); + AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); + service.mPendingEvents.remove(eventType); + tryRecycleLocked(oldEvent); + } + } + }; + + /** + * Creates a new instance. + * + * @param context A {@link Context} instance. + */ + AccessibilityManagerService(Context context) { + mContext = context; + mPackageManager = mContext.getPackageManager(); + mCaller = new HandlerCaller(context, this); + + registerPackageChangeAndBootCompletedBroadcastReceiver(); + registerSettingsContentObservers(); + + synchronized (mLock) { + populateAccessibilityServiceListLocked(); + } + } + + /** + * Registers a {@link BroadcastReceiver} for the events of + * adding/changing/removing/restarting a package and boot completion. + */ + private void registerPackageChangeAndBootCompletedBroadcastReceiver() { + Context context = mContext; + + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + populateAccessibilityServiceListLocked(); + manageServicesLocked(); + + if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { + mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; + updateClientsLocked(); + } + } + } + }; + + // package changes + IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + packageFilter.addDataScheme("package"); + context.registerReceiver(broadcastReceiver, packageFilter); + + // boot completed + IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(broadcastReceiver, bootFiler); + } + + /** + * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED} + * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings. + */ + private void registerSettingsContentObservers() { + ContentResolver contentResolver = mContext.getContentResolver(); + + Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED); + contentResolver.registerContentObserver(enabledUri, false, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; + + synchronized (mLock) { + if (mIsEnabled) { + manageServicesLocked(); + } else { + unbindAllServicesLocked(); + } + updateClientsLocked(); + } + } + }); + + Uri providersUri = + Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + contentResolver.registerContentObserver(providersUri, false, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + synchronized (mLock) { + manageServicesLocked(); + } + } + }); + } + + public void addClient(IAccessibilityManagerClient client) { + synchronized (mLock) { + try { + client.setEnabled(mIsEnabled); + mClients.add(client); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re); + } + } + } + + public boolean sendAccessibilityEvent(AccessibilityEvent event) { + synchronized (mLock) { + notifyAccessibilityServicesDelayedLocked(event, false); + notifyAccessibilityServicesDelayedLocked(event, true); + } + // event not scheduled for dispatch => recycle + if (mHandledFeedbackTypes == 0) { + event.recycle(); + } else { + mHandledFeedbackTypes = 0; + } + + return (OWN_PROCESS_ID != Binder.getCallingPid()); + } + + public List<ServiceInfo> getAccessibilityServiceList() { + synchronized (mLock) { + return mInstalledServices; + } + } + + public void interrupt() { + synchronized (mLock) { + for (int i = 0, count = mServices.size(); i < count; i++) { + Service service = mServices.get(i); + try { + service.mServiceInterface.onInterrupt(); + } catch (RemoteException re) { + if (re instanceof DeadObjectException) { + Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); + if (removeDeadServiceLocked(service)) { + count--; + i--; + } + } else { + Log.e(LOG_TAG, "Error during sending interrupt request to " + + service.mService, re); + } + } + } + } + } + + public void executeMessage(Message message) { + switch (message.what) { + case DO_SET_SERVICE_INFO: + SomeArgs arguments = ((SomeArgs) message.obj); + + AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1; + Service service = (Service) arguments.arg2; + + synchronized (mLock) { + service.mEventTypes = info.eventTypes; + service.mFeedbackType = info.feedbackType; + String[] packageNames = info.packageNames; + if (packageNames != null) { + service.mPackageNames.addAll(Arrays.asList(packageNames)); + } + service.mNotificationTimeout = info.notificationTimeout; + service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0; + } + return; + default: + Log.w(LOG_TAG, "Unknown message type: " + message.what); + } + } + + /** + * Populates the cached list of installed {@link AccessibilityService}s. + */ + private void populateAccessibilityServiceListLocked() { + mInstalledServices.clear(); + + List<ResolveInfo> installedServices = mPackageManager.queryIntentServices( + new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES); + + for (int i = 0, count = installedServices.size(); i < count; i++) { + mInstalledServices.add(installedServices.get(i).serviceInfo); + } + } + + /** + * Performs {@link AccessibilityService}s delayed notification. The delay is configurable + * and denotes the period after the last event before notifying the service. + * + * @param event The event. + * @param isDefault True to notify default listeners, not default services. + */ + private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, + boolean isDefault) { + for (int i = 0, count = mServices.size(); i < count; i++) { + Service service = mServices.get(i); + + if (service.mIsDefault == isDefault) { + if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) { + mHandledFeedbackTypes |= service.mFeedbackType; + notifyAccessibilityServiceDelayedLocked(service, event); + } + } + } + } + + /** + * Performs an {@link AccessibilityService} delayed notification. The delay is configurable + * and denotes the period after the last event before notifying the service. + * + * @param service The service. + * @param event The event. + */ + private void notifyAccessibilityServiceDelayedLocked(Service service, + AccessibilityEvent event) { + synchronized (mLock) { + int eventType = event.getEventType(); + AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); + service.mPendingEvents.put(eventType, event); + + int what = eventType | (service.mId << 16); + if (oldEvent != null) { + mHandler.removeMessages(what); + tryRecycleLocked(oldEvent); + } + + Message message = mHandler.obtainMessage(what, service); + message.arg1 = event.getEventType(); + mHandler.sendMessageDelayed(message, service.mNotificationTimeout); + } + } + + /** + * Recycles an event if it can be safely recycled. The condition is that no + * not notified service is interested in the event. + * + * @param event The event. + */ + private void tryRecycleLocked(AccessibilityEvent event) { + int eventType = event.getEventType(); + List<Service> services = mServices; + + // linear in the number of service which is not large + for (int i = 0, count = services.size(); i < count; i++) { + Service service = services.get(i); + if (service.mPendingEvents.get(eventType) == event) { + return; + } + } + + event.recycle(); + } + + /** + * Notifies a service for a scheduled event given the event type. + * + * @param service The service. + * @param eventType The type of the event to dispatch. + */ + private void notifyEventListenerLocked(Service service, int eventType) { + IEventListener listener = service.mServiceInterface; + AccessibilityEvent event = service.mPendingEvents.get(eventType); + + try { + listener.onAccessibilityEvent(event); + if (LOGV) { + Log.i(LOG_TAG, "Event " + event + " sent to " + listener); + } + } catch (RemoteException re) { + if (re instanceof DeadObjectException) { + Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); + synchronized (mLock) { + removeDeadServiceLocked(service); + } + } else { + Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re); + } + } + } + + /** + * Removes a dead service. + * + * @param service The service. + * @return True if the service was removed, false otherwise. + */ + private boolean removeDeadServiceLocked(Service service) { + mServices.remove(service); + mHandler.removeMessages(service.mId); + + if (LOGV) { + Log.i(LOG_TAG, "Dead service " + service.mService + " removed"); + } + + if (mServices.isEmpty()) { + mIsEnabled = false; + updateClientsLocked(); + } + + return true; + } + + /** + * Determines if given event can be dispatched to a service based on the package of the + * event source and already notified services for that event type. Specifically, a + * service is notified if it is interested in events from the package and no other service + * providing the same feedback type has been notified. Exception are services the + * provide generic feedback (feedback type left as a safety net for unforeseen feedback + * types) which are always notified. + * + * @param service The potential receiver. + * @param event The event. + * @param handledFeedbackTypes The feedback types for which services have been notified. + * @return True if the listener should be notified, false otherwise. + */ + private boolean canDispathEventLocked(Service service, AccessibilityEvent event, + int handledFeedbackTypes) { + + if (!service.isConfigured()) { + return false; + } + + if (!service.mService.isBinderAlive()) { + removeDeadServiceLocked(service); + return false; + } + + int eventType = event.getEventType(); + if ((service.mEventTypes & eventType) != eventType) { + return false; + } + + Set<String> packageNames = service.mPackageNames; + CharSequence packageName = event.getPackageName(); + + if (packageNames.isEmpty() || packageNames.contains(packageName)) { + int feedbackType = service.mFeedbackType; + if ((handledFeedbackTypes & feedbackType) != feedbackType + || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) { + return true; + } + } + + return false; + } + + /** + * Manages services by starting enabled ones and stopping disabled ones. + */ + private void manageServicesLocked() { + populateEnabledServicesLocked(mEnabledServices); + updateServicesStateLocked(mInstalledServices, mEnabledServices); + } + + /** + * Unbinds all bound services. + */ + private void unbindAllServicesLocked() { + List<Service> services = mServices; + + for (int i = 0, count = services.size(); i < count; i++) { + Service service = services.get(i); + + service.unbind(); + mComponentNameToServiceMap.remove(service.mComponentName); + } + services.clear(); + } + + /** + * Populates a list with the {@link ComponentName}s of all enabled + * {@link AccessibilityService}s. + * + * @param enabledServices The list. + */ + private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) { + enabledServices.clear(); + + String servicesValue = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + + if (servicesValue != null) { + TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(servicesValue); + while (splitter.hasNext()) { + ComponentName enabledService = ComponentName.unflattenFromString(splitter.next()); + enabledServices.add(enabledService); + } + } + } + + /** + * Updates the state of each service by starting (or keeping running) enabled ones and + * stopping the rest. + * + * @param installedServices All installed {@link AccessibilityService}s. + * @param enabledServices The {@link ComponentName}s of the enabled services. + */ + private void updateServicesStateLocked(List<ServiceInfo> installedServices, + Set<ComponentName> enabledServices) { + + Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; + List<Service> services = mServices; + + for (int i = 0, count = installedServices.size(); i < count; i++) { + ServiceInfo intalledService = installedServices.get(i); + ComponentName componentName = new ComponentName(intalledService.packageName, + intalledService.name); + Service service = componentNameToServiceMap.get(componentName); + + if (enabledServices.contains(componentName)) { + if (service == null) { + new Service(componentName).bind(); + } + } else { + if (service != null) { + service.unbind(); + componentNameToServiceMap.remove(componentName); + services.remove(service); + } + } + } + } + + /** + * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients. + */ + private void updateClientsLocked() { + for (int i = 0, count = mClients.size(); i < count; i++) { + try { + mClients.get(i).setEnabled(mIsEnabled); + } catch (RemoteException re) { + mClients.remove(i); + count--; + } + } + } + + /** + * This class represents an accessibility service. It stores all per service + * data required for the service management, provides API for starting/stopping the + * service and is responsible for adding/removing the service in the data structures + * for service management. The class also exposes configuration interface that is + * passed to the service it represents as soon it is bound. It also serves as the + * connection for the service. + */ + class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection { + int mId = 0; + + IBinder mService; + + IEventListener mServiceInterface; + + int mEventTypes; + + int mFeedbackType; + + Set<String> mPackageNames = new HashSet<String>(); + + boolean mIsDefault; + + long mNotificationTimeout; + + boolean mIsActive; + + ComponentName mComponentName; + + Intent mIntent; + + // the events pending events to be dispatched to this service + final SparseArray<AccessibilityEvent> mPendingEvents = + new SparseArray<AccessibilityEvent>(); + + Service(ComponentName componentName) { + mId = sIdCounter++; + mComponentName = componentName; + mIntent = new Intent().setComponent(mComponentName); + } + + /** + * Binds to the accessibility service. + */ + public void bind() { + if (mService == null) { + mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); + } + } + + /** + * Unbinds form the accessibility service and removes it from the data + * structures for service management. + */ + public void unbind() { + if (mService != null) { + mContext.unbindService(this); + } + } + + /** + * Returns if the service is configured i.e. at least event types of interest + * and feedback type must be set. + * + * @return True if the service is configured, false otherwise. + */ + public boolean isConfigured() { + return (mEventTypes != 0 && mFeedbackType != 0); + } + + public void setServiceInfo(AccessibilityServiceInfo info) { + mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget(); + } + + public void onServiceConnected(ComponentName componentName, IBinder service) { + mService = service; + mServiceInterface = IEventListener.Stub.asInterface(service); + + try { + mServiceInterface.setConnection(this); + synchronized (mLock) { + if (!mServices.contains(this)) { + mServices.add(this); + mComponentNameToServiceMap.put(componentName, this); + } + } + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re); + } + } + + public void onServiceDisconnected(ComponentName componentName) { + synchronized (mLock) { + Service service = mComponentNameToServiceMap.remove(componentName); + mServices.remove(service); + } + } + } +} diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index c50ae94676ac..131e156748d0 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -29,7 +29,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; -import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.net.Uri; @@ -40,6 +39,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; +import android.util.TypedValue; import android.util.Xml; import android.widget.RemoteViews; @@ -56,7 +56,6 @@ import java.util.HashSet; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.appwidget.IAppWidgetHost; -import com.android.internal.util.XmlUtils; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -79,7 +78,7 @@ class AppWidgetService extends IAppWidgetService.Stub static class Provider { int uid; AppWidgetProviderInfo info; - ArrayList<AppWidgetId> instances = new ArrayList(); + ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); PendingIntent broadcast; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it @@ -90,7 +89,7 @@ class AppWidgetService extends IAppWidgetService.Stub int uid; int hostId; String packageName; - ArrayList<AppWidgetId> instances = new ArrayList(); + ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); IAppWidgetHost callbacks; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it @@ -107,10 +106,10 @@ class AppWidgetService extends IAppWidgetService.Stub Context mContext; PackageManager mPackageManager; AlarmManager mAlarmManager; - ArrayList<Provider> mInstalledProviders = new ArrayList(); + ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>(); int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; - ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList(); - ArrayList<Host> mHosts = new ArrayList(); + final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>(); + ArrayList<Host> mHosts = new ArrayList<Host>(); boolean mSafeMode; AppWidgetService(Context context) { @@ -174,7 +173,7 @@ class AppWidgetService extends IAppWidgetService.Stub for (int i=0; i<N; i++) { AppWidgetId id = mAppWidgetIds.get(i); pw.print(" ["); pw.print(i); pw.print("] id="); - pw.println(id.appWidgetId);; + pw.println(id.appWidgetId); pw.print(" hostId="); pw.print(id.host.hostId); pw.print(' '); pw.print(id.host.packageName); pw.print('/'); @@ -384,7 +383,7 @@ class AppWidgetService extends IAppWidgetService.Stub public List<AppWidgetProviderInfo> getInstalledProviders() { synchronized (mAppWidgetIds) { final int N = mInstalledProviders.size(); - ArrayList<AppWidgetProviderInfo> result = new ArrayList(N); + ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N); for (int i=0; i<N; i++) { Provider p = mInstalledProviders.get(i); if (!p.zombie) { @@ -619,7 +618,6 @@ class AppWidgetService extends IAppWidgetService.Stub // rely on the fact that we've already set it and that // PendingIntent.getBroadcast will update the extras. boolean alreadyRegistered = p.broadcast != null; - int instancesSize = p.instances.size(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); intent.setComponent(p.info.provider); @@ -695,10 +693,16 @@ class AppWidgetService extends IAppWidgetService.Stub TypedArray sa = mContext.getResources().obtainAttributes(attrs, com.android.internal.R.styleable.AppWidgetProviderInfo); - info.minWidth = sa.getDimensionPixelSize( - com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0); - info.minHeight = sa.getDimensionPixelSize( - com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0); + + // These dimensions has to be resolved in the application's context. + // We simply send back the raw complex data, which will be + // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. + TypedValue value = sa.peekValue( + com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); + info.minWidth = value != null ? value.data : 0; + value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); + info.minHeight = value != null ? value.data : 0; + info.updatePeriodMillis = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); info.initialLayout = sa.getResourceId( @@ -773,10 +777,12 @@ class AppWidgetService extends IAppWidgetService.Stub if (real.exists()) { readStateFromFileLocked(real); if (temp.exists()) { + //noinspection ResultOfMethodCallIgnored temp.delete(); } } else if (temp.exists()) { readStateFromFileLocked(temp); + //noinspection ResultOfMethodCallIgnored temp.renameTo(real); } } @@ -792,18 +798,23 @@ class AppWidgetService extends IAppWidgetService.Stub // use the temporary one until it's fully written, create an empty file // for real, which will we'll shortly delete. try { + //noinspection ResultOfMethodCallIgnored real.createNewFile(); } catch (IOException e) { + // Ignore } } if (temp.exists()) { + //noinspection ResultOfMethodCallIgnored temp.delete(); } writeStateToFileLocked(temp); + //noinspection ResultOfMethodCallIgnored real.delete(); + //noinspection ResultOfMethodCallIgnored temp.renameTo(real); } @@ -866,8 +877,10 @@ class AppWidgetService extends IAppWidgetService.Stub stream.close(); } } catch (IOException ex) { + // Ignore } if (file.exists()) { + //noinspection ResultOfMethodCallIgnored file.delete(); } } @@ -885,7 +898,7 @@ class AppWidgetService extends IAppWidgetService.Stub int type; int providerIndex = 0; - HashMap<Integer,Provider> loadedProviders = new HashMap(); + HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>(); do { type = parser.next(); if (type == XmlPullParser.START_TAG) { @@ -986,6 +999,7 @@ class AppWidgetService extends IAppWidgetService.Stub stream.close(); } } catch (IOException e) { + // Ignore } if (success) { @@ -1081,7 +1095,7 @@ class AppWidgetService extends IAppWidgetService.Stub // TODO: If there's a better way of matching an intent filter against the // packages for a given package, use that. void updateProvidersForPackageLocked(String pkgName) { - HashSet<String> keep = new HashSet(); + HashSet<String> keep = new HashSet<String>(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1103,7 +1117,6 @@ class AppWidgetService extends IAppWidgetService.Stub if (parsed != null) { keep.add(ai.name); // Use the new AppWidgetProviderInfo. - AppWidgetProviderInfo oldInfo = p.info; p.info = parsed.info; // If it's enabled final int M = p.instances.size(); diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java index 459ae52a4bbf..81378dcdc51a 100644 --- a/services/java/com/android/server/AttributeCache.java +++ b/services/java/com/android/server/AttributeCache.java @@ -17,56 +17,36 @@ package com.android.server; -import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; -import android.content.SharedPreferences; -import android.content.Intent; -import android.content.BroadcastReceiver; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.provider.Settings; -import android.util.Config; -import android.util.Log; +import android.util.SparseArray; +import java.util.HashMap; import java.util.WeakHashMap; -public final class AttributeCache extends BroadcastReceiver { +/** + * TODO: This should be better integrated into the system so it doesn't need + * special calls from the activity manager to clear it. + */ +public final class AttributeCache { private static AttributeCache sInstance = null; private final Context mContext; - private final WeakHashMap<Key, Entry> mMap = - new WeakHashMap<Key, Entry>(); - private final WeakHashMap<String, Context> mContexts = - new WeakHashMap<String, Context>(); + private final WeakHashMap<String, Package> mPackages = + new WeakHashMap<String, Package>(); + private final Configuration mConfiguration = new Configuration(); - final static class Key { - public final String packageName; - public final int resId; - public final int[] styleable; - - public Key(String inPackageName, int inResId, int[] inStyleable) { - packageName = inPackageName; - resId = inResId; - styleable = inStyleable; - } + public final static class Package { + public final Context context; + private final SparseArray<HashMap<int[], Entry>> mMap + = new SparseArray<HashMap<int[], Entry>>(); - @Override public boolean equals(Object obj) { - try { - if (obj != null) { - Key other = (Key)obj; - return packageName.equals(other.packageName) - && resId == other.resId - && styleable == other.styleable; - } - } catch (ClassCastException e) { - } - return false; - } - - @Override public int hashCode() { - return packageName.hashCode() + resId; + public Package(Context c) { + context = c; } } @@ -94,36 +74,68 @@ public final class AttributeCache extends BroadcastReceiver { mContext = context; } - public Entry get(String packageName, int resId, int[] styleable) { + public void removePackage(String packageName) { synchronized (this) { - Key key = new Key(packageName, resId, styleable); - Entry ent = mMap.get(key); - if (ent != null) { - return ent; + mPackages.remove(packageName); + } + } + + public void updateConfiguration(Configuration config) { + synchronized (this) { + int changes = mConfiguration.updateFrom(config); + if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE | + ActivityInfo.CONFIG_KEYBOARD_HIDDEN | + ActivityInfo.CONFIG_ORIENTATION)) != 0) { + // The configurations being masked out are ones that commonly + // change so we don't want flushing the cache... all others + // will flush the cache. + mPackages.clear(); } - Context context = mContexts.get(packageName); - if (context == null) { + } + } + + public Entry get(String packageName, int resId, int[] styleable) { + synchronized (this) { + Package pkg = mPackages.get(packageName); + HashMap<int[], Entry> map = null; + Entry ent = null; + if (pkg != null) { + map = pkg.mMap.get(resId); + if (map != null) { + ent = map.get(styleable); + if (ent != null) { + return ent; + } + } + } else { + Context context; try { context = mContext.createPackageContext(packageName, 0); if (context == null) { return null; } - mContexts.put(packageName, context); } catch (PackageManager.NameNotFoundException e) { return null; } + pkg = new Package(context); + mPackages.put(packageName, pkg); } + + if (map == null) { + map = new HashMap<int[], Entry>(); + pkg.mMap.put(resId, map); + } + try { - ent = new Entry(context, - context.obtainStyledAttributes(resId, styleable)); - mMap.put(key, ent); + ent = new Entry(pkg.context, + pkg.context.obtainStyledAttributes(resId, styleable)); + map.put(styleable, ent); } catch (Resources.NotFoundException e) { return null; } + return ent; } } - @Override public void onReceive(Context context, Intent intent) { - } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 983329be5ea5..3b82284c0c4b 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -16,18 +16,26 @@ package com.android.server; -import android.backup.BackupService; -import android.backup.IBackupService; +import android.app.ActivityManagerNative; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IApplicationThread; +import android.app.IBackupAgent; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; +import android.content.pm.Signature; import android.net.Uri; +import android.provider.Settings; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -35,71 +43,212 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import android.backup.IBackupManager; +import android.backup.IRestoreObserver; +import android.backup.IRestoreSession; +import android.backup.RestoreSet; +import com.android.internal.backup.LocalTransport; +import com.android.internal.backup.IBackupTransport; + +import com.android.server.PackageManagerBackupAgent; +import com.android.server.PackageManagerBackupAgent.Metadata; + +import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.lang.String; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; private static final boolean DEBUG = true; - - private static final long COLLECTION_INTERVAL = 1000; - //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; + // How often we perform a backup pass. Privileged external callers can + // trigger an immediate pass. + private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR; + + // The amount of time between the initial provisioning of the device and + // the first backup pass. + private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; + + private static final String RUN_BACKUP_ACTION = "_backup_run_"; 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 int MSG_RUN_CLEAR = 4; + + // Timeout interval for deciding that a bind or clear-data has taken too long + static final long TIMEOUT_INTERVAL = 10 * 1000; + private Context mContext; private PackageManager mPackageManager; + private IActivityManager mActivityManager; + private PowerManager mPowerManager; + private AlarmManager mAlarmManager; + + private boolean mEnabled; // access to this is synchronized on 'this' + private boolean mProvisioned; + private PowerManager.WakeLock mWakelock; private final BackupHandler mBackupHandler = new BackupHandler(); + private PendingIntent mRunBackupIntent; + private BroadcastReceiver mRunBackupReceiver; + private IntentFilter mRunBackupFilter; // map UIDs to the set of backup client services within that UID's app set - private SparseArray<HashSet<ServiceInfo>> mBackupParticipants - = new SparseArray<HashSet<ServiceInfo>>(); + private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants + = new SparseArray<HashSet<ApplicationInfo>>(); // set of backup services that have pending changes private class BackupRequest { - public ServiceInfo service; + public ApplicationInfo appInfo; public boolean fullBackup; - - BackupRequest(ServiceInfo svc, boolean isFull) { - service = svc; + + BackupRequest(ApplicationInfo app, boolean isFull) { + appInfo = app; fullBackup = isFull; } + + public String toString() { + return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}"; + } } // Backups that we haven't started yet. - private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap(); - // Backups that we have started. These are separate to prevent starvation - // if an app keeps re-enqueuing itself. - private ArrayList<BackupRequest> mBackupQueue; + private HashMap<ApplicationInfo,BackupRequest> mPendingBackups + = new HashMap<ApplicationInfo,BackupRequest>(); + + // Pseudoname that we use for the Package Manager metadata "package" + private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; + + // locking around the pending-backup management private final Object mQueueLock = new Object(); - private File mStateDir; + // The thread performing the sequence of queued backups binds to each app's agent + // in succession. Bind notifications are asynchronously delivered through the + // Activity Manager; use this lock object to signal when a requested binding has + // completed. + private final Object mAgentConnectLock = new Object(); + private IBackupAgent mConnectedAgent; + private volatile boolean mConnecting; + + // A similar synchronicity mechanism around clearing apps' data for restore + private final Object mClearDataLock = new Object(); + private volatile boolean mClearingData; + + // Transport bookkeeping + private final HashMap<String,IBackupTransport> mTransports + = new HashMap<String,IBackupTransport>(); + private String mCurrentTransport; + private IBackupTransport mLocalTransport, mGoogleTransport; + private RestoreSession mActiveRestoreSession; + + 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; + } + } + + private class ClearParams { + public IBackupTransport transport; + public PackageInfo packageInfo; + + ClearParams(IBackupTransport _transport, PackageInfo _info) { + transport = _transport; + packageInfo = _info; + } + } + + // Where we keep our journal files and other bookkeeping + private File mBaseStateDir; private File mDataDir; - + private File mJournalDir; + private File mJournal; + private RandomAccessFile mJournalStream; + public BackupManagerService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mActivityManager = ActivityManagerNative.getDefault(); + + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); // Set up our bookkeeping - mStateDir = new File(Environment.getDataDirectory(), "backup"); - mStateDir.mkdirs(); + boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.BACKUP_ENABLED, 0) != 0; + // !!! TODO: mProvisioned needs to default to 0, not 1. + mProvisioned = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.BACKUP_PROVISIONED, 1) != 0; + mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); mDataDir = Environment.getDownloadCacheDirectory(); - - // Build our mapping of uid to backup client services + + mRunBackupReceiver = new RunBackupReceiver(); + mRunBackupFilter = new IntentFilter(); + mRunBackupFilter.addAction(RUN_BACKUP_ACTION); + context.registerReceiver(mRunBackupReceiver, mRunBackupFilter); + + Intent backupIntent = new Intent(RUN_BACKUP_ACTION); + // !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work + //backupIntent.setClass(context, mRunBackupReceiver.getClass()); + backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0); + + // Set up the backup-request journaling + 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 + // schedules a backup pass on the Package Manager metadata the first + // time anything needs to be backed up. synchronized (mBackupParticipants) { addPackageParticipantsLocked(null); } + // 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? + mLocalTransport = new LocalTransport(context); // This is actually pretty cheap + ComponentName localName = new ComponentName(context, LocalTransport.class); + registerTransport(localName.flattenToShortString(), mLocalTransport); + + mGoogleTransport = null; + mCurrentTransport = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT); + if ("".equals(mCurrentTransport)) { + mCurrentTransport = null; + } + if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport); + + // Attach to the Google backup transport. When this comes up, it will set + // itself as the current transport because we explicitly reset mCurrentTransport + // to null. + Intent intent = new Intent().setComponent(new ComponentName( + "com.google.android.backup", + "com.google.android.backup.BackupTransportService")); + context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE); + + // Now that we know about valid backup participants, parse any + // leftover journal files into the pending backup set + parseLeftoverJournals(); + // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); @@ -107,6 +256,77 @@ class BackupManagerService extends IBackupManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); + + // Power management + mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup"); + + // Start the backup passes going + setBackupEnabled(areEnabled); + } + + private class RunBackupReceiver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + if (RUN_BACKUP_ACTION.equals(intent.getAction())) { + if (DEBUG) Log.v(TAG, "Running a backup pass"); + + synchronized (mQueueLock) { + // acquire a wakelock and pass it to the backup thread. it will + // be released once backup concludes. + mWakelock.acquire(); + + Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); + mBackupHandler.sendMessage(msg); + } + } + } + } + + private void makeJournalLocked() { + try { + mJournal = File.createTempFile("journal", null, mJournalDir); + mJournalStream = new RandomAccessFile(mJournal, "rwd"); + } catch (IOException e) { + Log.e(TAG, "Unable to write backup journals"); + mJournal = null; + mJournalStream = null; + } + } + + private void parseLeftoverJournals() { + if (mJournal != null) { + File[] allJournals = mJournalDir.listFiles(); + for (File f : allJournals) { + if (f.compareTo(mJournal) != 0) { + // This isn't the current journal, so it must be a leftover. Read + // out the package names mentioned there and schedule them for + // backup. + try { + Log.i(TAG, "Found stale backup journal, scheduling:"); + RandomAccessFile in = new RandomAccessFile(f, "r"); + while (true) { + String packageName = in.readUTF(); + Log.i(TAG, " + " + packageName); + dataChanged(packageName); + } + } catch (EOFException e) { + // no more data; we're done + } catch (Exception e) { + // can't read it or other error; just skip it + } finally { + // close/delete the file + f.delete(); + } + } + } + } + } + + // Add a transport to our set of available backends + private void registerTransport(String name, IBackupTransport transport) { + synchronized (mTransports) { + if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport); + mTransports.put(name, transport); + } } // ----- Track installation/removal of packages ----- @@ -149,282 +369,1201 @@ class BackupManagerService extends IBackupManager.Stub { } }; + // ----- Track connection to GoogleBackupTransport service ----- + ServiceConnection mGoogleConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.v(TAG, "Connected to Google transport"); + mGoogleTransport = IBackupTransport.Stub.asInterface(service); + registerTransport(name.flattenToShortString(), mGoogleTransport); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Disconnected from Google transport"); + mGoogleTransport = null; + registerTransport(name.flattenToShortString(), null); + } + }; + // ----- Run the actual backup process asynchronously ----- - private class BackupHandler extends Handler implements ServiceConnection { + private class BackupHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case MSG_RUN_BACKUP: + { + IBackupTransport transport = getTransport(mCurrentTransport); + if (transport == null) { + Log.v(TAG, "Backup requested but no transport available"); + mWakelock.release(); + break; + } + // snapshot the pending-backup set and work on that + ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); + File oldJournal = mJournal; synchronized (mQueueLock) { - mBackupQueue = new ArrayList(); - for (BackupRequest b: mPendingBackups.values()) { - mBackupQueue.add(b); + // Do we have any work to do? + if (mPendingBackups.size() > 0) { + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); + } + Log.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); + + // Start a new backup-queue journal file too + if (mJournalStream != null) { + try { + mJournalStream.close(); + } catch (IOException e) { + // don't need to do anything + } + makeJournalLocked(); + } + + // At this point, we have started a new journal file, and the old + // file identity is being passed to the backup processing thread. + // When it completes successfully, that old journal file will be + // deleted. If we crash prior to that, the old journal is parsed + // at next boot and the journaled requests fulfilled. + (new PerformBackupThread(transport, queue, oldJournal)).start(); + } else { + Log.v(TAG, "Backup requested but nothing pending"); + mWakelock.release(); } - mPendingBackups = new HashMap<ComponentName,BackupRequest>(); - // !!! TODO: start a new backup-queue journal file too - // WARNING: If we crash after this line, anything in mPendingBackups will - // be lost. FIX THIS. } - startOneService(); break; } + + case MSG_RUN_FULL_BACKUP: + break; + + case MSG_RUN_RESTORE: + { + RestoreParams params = (RestoreParams)msg.obj; + Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); + (new PerformRestoreThread(params.transport, params.observer, + params.token)).start(); + break; + } + + case MSG_RUN_CLEAR: + { + ClearParams params = (ClearParams)msg.obj; + (new PerformClearThread(params.transport, params.packageInfo)).start(); + break; + } + } } - - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "onServiceConnected name=" + name + " service=" + service); - IBackupService bs = IBackupService.Stub.asInterface(service); - processOneBackup(name, bs); + } + + // Add the backup agents in the given package to our set of known backup participants. + // If 'packageName' is null, adds all backup agents in the whole system. + void addPackageParticipantsLocked(String packageName) { + // Look for apps that define the android:backupAgent attribute + if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName); + List<PackageInfo> targetApps = allAgentPackages(); + addPackageParticipantsLockedInner(packageName, targetApps); + } + + private void addPackageParticipantsLockedInner(String packageName, + List<PackageInfo> targetPkgs) { + if (DEBUG) { + Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:"); + for (PackageInfo p : targetPkgs) { + Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName + + " uid=" + p.applicationInfo.uid); + } } - public void onServiceDisconnected(ComponentName name) { - // TODO: handle backup being interrupted + 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(pkg.applicationInfo); + } } } - void startOneService() { - // Loop until we find someone to start or the queue empties out. - Intent intent = new Intent(BackupService.SERVICE_ACTION); - while (true) { - BackupRequest request; - synchronized (mQueueLock) { - int queueSize = mBackupQueue.size(); - if (queueSize == 0) { - mBackupQueue = null; - // TODO: Anything else to do here? - return; + // Remove the given package's entry from our known active set. If + // 'packageName' is null, *all* participating apps will be removed. + void removePackageParticipantsLocked(String packageName) { + if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName); + List<PackageInfo> allApps = null; + if (packageName != null) { + allApps = new ArrayList<PackageInfo>(); + try { + int flags = PackageManager.GET_SIGNATURES; + allApps.add(mPackageManager.getPackageInfo(packageName, flags)); + } catch (Exception e) { + // just skip it (???) + } + } else { + // all apps with agents + allApps = allAgentPackages(); + } + removePackageParticipantsLockedInner(packageName, allApps); + } + + private void removePackageParticipantsLockedInner(String packageName, + List<PackageInfo> agents) { + if (DEBUG) { + Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName + + ") removing " + agents.size() + " entries"); + for (PackageInfo p : agents) { + Log.v(TAG, " - " + p); + } + } + 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(pkg.packageName)) { + set.remove(entry); + break; + } + } + if (set.size() == 0) { + mBackupParticipants.delete(uid); + } } - request = mBackupQueue.get(0); - // Take it off the queue when we're done. } - - intent.setClassName(request.service.packageName, request.service.name); - Log.d(TAG, "binding to " + intent); + } + } + + // Returns the set of all applications that define an android:backupAgent attribute + private List<PackageInfo> allAgentPackages() { + // !!! TODO: cache this and regenerate only when necessary + 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 packages; + } + + // Reset the given package's known backup participants. Unlike add/remove, the update + // action cannot be passed a null package name. + void updatePackageParticipantsLocked(String packageName) { + if (packageName == null) { + Log.e(TAG, "updatePackageParticipants called with null package name"); + return; + } + if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName); + + // brute force but small code size + List<PackageInfo> allApps = allAgentPackages(); + removePackageParticipantsLockedInner(packageName, allApps); + addPackageParticipantsLockedInner(packageName, allApps); + } + + // Return the given transport + private IBackupTransport getTransport(String transportName) { + synchronized (mTransports) { + IBackupTransport transport = mTransports.get(transportName); + if (transport == null) { + Log.w(TAG, "Requested unavailable transport: " + transportName); + } + return transport; + } + } + + // fire off a backup agent, blocking until it attaches or times out + IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { + IBackupAgent agent = null; + synchronized(mAgentConnectLock) { + mConnecting = true; + mConnectedAgent = null; try { - if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) { - Log.d(TAG, "awaiting service object for " + intent); - // success - return; + if (mActivityManager.bindBackupAgent(app, mode)) { + Log.d(TAG, "awaiting agent for " + app); + + // success; wait for the agent to arrive + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mConnecting && mConnectedAgent == null + && (System.currentTimeMillis() < timeoutMark)) { + try { + mAgentConnectLock.wait(5000); + } catch (InterruptedException e) { + // just bail + return null; + } + } + + // if we timed out with no connect, abort and move on + if (mConnecting == true) { + Log.w(TAG, "Timeout waiting for agent " + app); + return null; + } + agent = mConnectedAgent; } - } catch (SecurityException ex) { - // Try for the next one. - Log.d(TAG, "error in bind", ex); + } catch (RemoteException e) { + // can't happen } } + return agent; } - void processOneBackup(ComponentName name, IBackupService bs) { + // clear an application's data, blocking until the operation completes or times out + void clearApplicationDataSynchronous(String packageName) { + // Don't wipe packages marked allowClearUserData=false try { - Log.d(TAG, "processOneBackup doBackup() on " + name); + PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { + if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping " + + packageName); + return; + } + } catch (NameNotFoundException e) { + Log.w(TAG, "Tried to clear data for " + packageName + " but not found"); + return; + } - BackupRequest request; - synchronized (mQueueLock) { - if (mBackupQueue == null) { - Log.d(TAG, "mBackupQueue is null. WHY?"); + ClearDataObserver observer = new ClearDataObserver(); + + synchronized(mClearDataLock) { + mClearingData = true; + mPackageManager.clearApplicationUserData(packageName, observer); + + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { + try { + mClearDataLock.wait(5000); + } catch (InterruptedException e) { + // won't happen, but still. + mClearingData = false; } - request = mBackupQueue.get(0); } + } + } - // !!! TODO right now these naming schemes limit applications to - // one backup service per package - File savedStateName = new File(mStateDir, request.service.packageName); - File backupDataName = new File(mDataDir, request.service.packageName + ".data"); - File newStateName = new File(mStateDir, request.service.packageName + ".new"); - - // In a full backup, we pass a null ParcelFileDescriptor as - // the saved-state "file" - ParcelFileDescriptor savedState = (request.fullBackup) ? null - : ParcelFileDescriptor.open(savedStateName, - ParcelFileDescriptor.MODE_READ_ONLY | - ParcelFileDescriptor.MODE_CREATE); + class ClearDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) + throws android.os.RemoteException { + synchronized(mClearDataLock) { + mClearingData = false; + mClearDataLock.notifyAll(); + } + } + } - backupDataName.delete(); - ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); + // ----- Back up a set of applications via a worker thread ----- - newStateName.delete(); - ParcelFileDescriptor newState = - ParcelFileDescriptor.open(newStateName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); + class PerformBackupThread extends Thread { + private static final String TAG = "PerformBackupThread"; + IBackupTransport mTransport; + ArrayList<BackupRequest> mQueue; + File mStateDir; + File mJournal; + + public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue, + File journal) { + mTransport = transport; + mQueue = queue; + mJournal = journal; - // Run the target's backup pass try { - // TODO: Make this oneway - bs.doBackup(savedState, backupData, newState); - } finally { - if (savedState != null) { - savedState.close(); + 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"); + + // Backups run at background priority + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + + // 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 { + if (!mTransport.finishBackup()) { + // STOPSHIP TODO: handle errors + Log.e(TAG, "Backup failure in finishBackup()"); } - backupData.close(); - newState.close(); + } catch (RemoteException e) { + Log.e(TAG, "Error in finishBackup()", e); } - // !!! TODO: Now propagate the newly-backed-up data to the transport - - // !!! 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 (FileNotFoundException fnf) { - Log.d(TAG, "File not found on backup: "); - fnf.printStackTrace(); - } catch (RemoteException e) { - Log.d(TAG, "Remote target " + name + " threw during backup:"); - e.printStackTrace(); - } catch (Exception e) { - Log.w(TAG, "Final exception guard in backup: "); - e.printStackTrace(); + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath()); + } + + // Only once we're entirely finished do we release the wakelock + mWakelock.release(); } - synchronized (mQueueLock) { - mBackupQueue.remove(0); + + private void doQueuedBackups(IBackupTransport transport) { + for (BackupRequest request : mQueue) { + Log.d(TAG, "starting agent for backup of " + request); + + IBackupAgent agent = null; + int mode = (request.fullBackup) + ? IApplicationThread.BACKUP_MODE_FULL + : IApplicationThread.BACKUP_MODE_INCREMENTAL; + try { + agent = bindToAgentSynchronous(request.appInfo, mode); + if (agent != null) { + processOneBackup(request, agent, transport); + } + + // unbind even on timeout, just in case + mActivityManager.unbindBackupAgent(request.appInfo); + } catch (SecurityException ex) { + // Try for the next one. + Log.d(TAG, "error in bind/backup", ex); + } catch (RemoteException e) { + Log.v(TAG, "bind/backup threw"); + e.printStackTrace(); + } + + } } - mContext.unbindService(mBackupHandler); - } - // Add the backup services in the given package to our set of known backup participants. - // If 'packageName' is null, adds all backup services in the system. - void addPackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - addPackageParticipantsLockedInner(packageName, services); - } + void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + final String packageName = request.appInfo.packageName; + Log.d(TAG, "processOneBackup doBackup() on " + packageName); - private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); - if (set == null) { - set = new HashSet<ServiceInfo>(); - mBackupParticipants.put(uid, set); + try { + // Look up the package info & signatures. This is first so that if it + // throws an exception, there's no file setup yet that would need to + // be unraveled. + PackageInfo packInfo; + if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { + // The metadata 'package' is synthetic + packInfo = new PackageInfo(); + packInfo.packageName = packageName; + } else { + packInfo = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); } - if (DEBUG) { - Log.v(TAG, "Adding " + services.size() + " backup participants:"); - for (ResolveInfo svc : services) { - Log.v(TAG, " " + svc + " : " + svc.filter); + + // !!! TODO: get the state file dir from the transport + File savedStateName = new File(mStateDir, packageName); + File backupDataName = new File(mDataDir, packageName + ".data"); + File newStateName = new File(mStateDir, packageName + ".new"); + + // In a full backup, we pass a null ParcelFileDescriptor as + // the saved-state "file" + ParcelFileDescriptor savedState = (request.fullBackup) ? null + : ParcelFileDescriptor.open(savedStateName, + ParcelFileDescriptor.MODE_READ_ONLY | + ParcelFileDescriptor.MODE_CREATE); + + backupDataName.delete(); + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + newStateName.delete(); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the target's backup pass + boolean success = false; + try { + agent.doBackup(savedState, backupData, newState); + success = true; + } finally { + if (savedState != null) { + savedState.close(); } + backupData.close(); + newState.close(); } - set.add(ri.serviceInfo); + // Now propagate the newly-backed-up data to the transport + if (success) { + if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); + backupData = + ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); + 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 (Exception e) { + Log.e(TAG, "Error backing up " + packageName, e); } } } - // Remove the given package's backup services from our known active set. If - // 'packageName' is null, *all* backup services will be removed. - void removePackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); + + // ----- Restore handling ----- + + 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? + if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + + " device=" + deviceSigs); + if ((storedSigs == null || storedSigs.length == 0) + && (deviceSigs == null || deviceSigs.length == 0)) { + return true; + } + if (storedSigs == null || deviceSigs == null) { + return false; + } + + // !!! TODO: this demands that every stored signature match one + // that is present on device, and does not demand the converse. + // Is this this right policy? + int nStored = storedSigs.length; + int nDevice = deviceSigs.length; + + for (int i=0; i < nStored; i++) { + boolean match = false; + for (int j=0; j < nDevice; j++) { + if (storedSigs[i].equals(deviceSigs[j])) { + match = true; + break; + } + } + if (!match) { + return false; + } + } + return true; } - private void removePackageParticipantsLockedInner(String packageName, - List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); - if (set != null) { - set.remove(ri.serviceInfo); - if (set.size() == 0) { - mBackupParticipants.put(uid, null); + class PerformRestoreThread extends Thread { + private IBackupTransport mTransport; + private IRestoreObserver mObserver; + private long mToken; + private RestoreSet mImage; + private File mStateDir; + + class RestoreRequest { + public PackageInfo app; + public int storedAppVersion; + + RestoreRequest(PackageInfo _app, int _version) { + app = _app; + storedAppVersion = _version; + } + } + + PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer, + long restoreSetToken) { + mTransport = transport; + Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver); + mObserver = observer; + mToken = restoreSetToken; + + 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 restore process mTransport=" + mTransport + + " mObserver=" + mObserver + " mToken=" + mToken); + /** + * Restore sequence: + * + * 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 + * 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 error = -1; // assume error + + // build the set of apps to restore + try { + RestoreSet[] images = mTransport.getAvailableRestoreSets(); + if (images == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting restore sets"); + return; + } + + 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); + + 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; + } + } + + if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error starting restore operation"); + return; + } + + 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())); + + // Verify that the backup set includes metadata. If not, we can't do + // signature/version verification etc, so we simply do not proceed with + // the restore operation. + if (!pmAgent.hasMetadata()) { + Log.i(TAG, "No restore metadata available, so not restoring settings"); + return; + } + + 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; + } + + if (mObserver != null) { + ++count; + try { + mObserver.onUpdate(count); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died in onUpdate"); + mObserver = null; + } + } + + 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 actual restore + clearApplicationDataSynchronous(packageName); + IBackupAgent agent = bindToAgentSynchronous( + packageInfo.applicationInfo, + IApplicationThread.BACKUP_MODE_RESTORE); + if (agent == null) { + Log.w(TAG, "Can't find backup agent for " + packageName); + continue; + } + + 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) { + Log.e(TAG, "Error finishing restore", e); + } + + Log.d(TAG, "finishing restore mObserver=" + mObserver); + + if (mObserver != null) { + try { + mObserver.restoreFinished(error); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died at restoreFinished"); + } + } + + // done; we can finally release the wakelock + mWakelock.release(); + } + } + + // 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; + + Log.d(TAG, "processOneRestore packageName=" + packageName); + + // !!! TODO: get the dirs from the transport + File backupDataName = new File(mDataDir, packageName + ".restore"); + backupDataName.delete(); + try { + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the transport's restore pass + // Run the target's backup pass + try { + if (!mTransport.getRestoreData(backupData)) { + // STOPSHIP TODO: Handle this error somehow? + Log.e(TAG, "Error getting restore data for " + packageName); + return; + } + } finally { + backupData.close(); + } + + // Okay, we have the data. Now have the agent do the restore. + File newStateName = new File(mStateDir, packageName + ".new"); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + backupData = ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_ONLY); + + try { + agent.doRestore(backupData, appVersionCode, newState); + } finally { + newState.close(); + backupData.close(); + } + + // if everything went okay, remember the recorded state now + File savedStateName = new File(mStateDir, packageName); + newStateName.renameTo(savedStateName); + } catch (Exception e) { + Log.e(TAG, "Error restoring data for " + packageName, e); } } } - // Reset the given package's known backup participants. Unlike add/remove, the update - // action cannot be passed a null package name. - void updatePackageParticipantsLocked(String packageName) { - if (packageName == null) { - Log.e(TAG, "updatePackageParticipants called with null package name"); - return; + class PerformClearThread extends Thread { + IBackupTransport mTransport; + PackageInfo mPackage; + + PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) { + mTransport = transport; + mPackage = packageInfo; } - // brute force but small code size - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); - addPackageParticipantsLockedInner(packageName, services); + @Override + public void run() { + try { + // Clear the on-device backup state to ensure a full backup next time + File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); + File stateFile = new File(stateDir, mPackage.packageName); + stateFile.delete(); + + // Tell the transport to remove all the persistent storage for the app + mTransport.clearBackupData(mPackage); + } catch (RemoteException e) { + // can't happen; the transport is local + } finally { + try { + mTransport.finishBackup(); + } catch (RemoteException e) { + // can't happen; the transport is local + } + + // Last but not least, release the cpu + mWakelock.release(); + } + } } + // ----- IBackupManager binder interface ----- - + public void dataChanged(String packageName) throws RemoteException { // Record that we need a backup pass for the caller. Since multiple callers // may share a uid, we need to note all candidates within that uid and schedule // a backup pass for each of them. - Log.d(TAG, "dataChanged packageName=" + packageName); - - HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid()); - Log.d(TAG, "targets=" + targets); + // If the caller does not hold the BACKUP permission, it can only request a + // backup of its own data. + HashSet<ApplicationInfo> targets; + if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + targets = mBackupParticipants.get(Binder.getCallingUid()); + } else { + // a caller with full permission can ask to back up any participating app + // !!! TODO: allow backup of ANY app? + targets = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + targets.addAll(s); + } + } + } if (targets != null) { synchronized (mQueueLock) { // Note that this client has made data changes that need to be backed up - for (ServiceInfo service : targets) { + for (ApplicationInfo app : targets) { // validate the caller-supplied package name against the known set of // packages associated with this uid - if (service.packageName.equals(packageName)) { + if (app.packageName.equals(packageName)) { // Add the caller to the set of pending backups. If there is // one already there, then overwrite it, but no harm done. - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); - // !!! TODO: write to the pending-backup journal file in case of crash + BackupRequest req = new BackupRequest(app, false); + if (mPendingBackups.put(app, req) == null) { + // Journal this request in case of crash. The put() + // operation returned null when this package was not already + // in the set; we want to avoid touching the disk redundantly. + writeToJournalLocked(packageName); + + if (DEBUG) { + int numKeys = mPendingBackups.size(); + Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); + for (BackupRequest b : mPendingBackups.values()) { + Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); + } + } + } } } + } + } else { + Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"); + } + } + + private void writeToJournalLocked(String str) { + if (mJournalStream != null) { + try { + mJournalStream.writeUTF(str); + } catch (IOException e) { + Log.e(TAG, "Error writing to backup journal"); + mJournalStream = null; + mJournal = null; + } + } + } + + // Clear the given package's backup data from the current transport + public void clearBackupData(String packageName) { + if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName); + PackageInfo info; + try { + info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); + return; + } + + // If the caller does not hold the BACKUP permission, it can only request a + // wipe of its own backed-up data. + HashSet<ApplicationInfo> apps; + if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + apps = mBackupParticipants.get(Binder.getCallingUid()); + } else { + // a caller with full permission can ask to back up any participating app + // !!! TODO: allow data-clear of ANY app? + if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps"); + apps = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + apps.addAll(s); + } + } + } + + // now find the given package in the set of candidate apps + for (ApplicationInfo app : apps) { + if (app.packageName.equals(packageName)) { + if (DEBUG) Log.v(TAG, "Found the app - running clear process"); + // found it; fire off the clear request + synchronized (mQueueLock) { + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, + new ClearParams(getTransport(mCurrentTransport), info)); + mBackupHandler.sendMessage(msg); + } + break; + } + } + } - Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants"); - // Schedule a backup pass in a few minutes. As backup-eligible data - // keeps changing, continue to defer the backup pass until things - // settle down, to avoid extra overhead. - mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); + // Run a backup pass immediately for any applications that have declared + // that they have pending updates. + public void backupNow() throws RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow"); + + if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); + synchronized (mQueueLock) { + try { + if (DEBUG) Log.v(TAG, "sending immediate backup broadcast"); + mRunBackupIntent.send(); + } catch (PendingIntent.CanceledException e) { + // should never happen + Log.e(TAG, "run-backup intent cancelled!"); } } } - // Schedule a backup pass for a given package, even if the caller is not part of - // that uid or package itself. - public void scheduleFullBackup(String packageName) throws RemoteException { - // !!! TODO: protect with a signature-or-system permission? - HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>(); + // Enable/disable the backup service + public void setBackupEnabled(boolean enable) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setBackupEnabled"); + + boolean wasEnabled = mEnabled; + synchronized (this) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); + mEnabled = enable; + } + synchronized (mQueueLock) { - int numKeys = mBackupParticipants.size(); - for (int index = 0; index < numKeys; index++) { - int uid = mBackupParticipants.keyAt(index); - HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid); - for (ServiceInfo service: servicesAtUid) { - if (service.packageName.equals(packageName)) { - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); + if (enable && !wasEnabled && mProvisioned) { + // if we've just been enabled, start scheduling backup passes + startBackupAlarmsLocked(BACKUP_INTERVAL); + } else if (!enable) { + // No longer enabled, so stop running backups + mAlarmManager.cancel(mRunBackupIntent); + } + } + } + + // Mark the backup service as having been provisioned + public void setBackupProvisioned(boolean available) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "setBackupProvisioned"); + + boolean wasProvisioned = mProvisioned; + synchronized (this) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0); + mProvisioned = available; + } + + synchronized (mQueueLock) { + if (available && !wasProvisioned && mEnabled) { + // we're now good to go, so start the backup alarms + startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); + } else if (!available) { + // No longer enabled, so stop running backups + Log.w(TAG, "Backup service no longer provisioned"); + mAlarmManager.cancel(mRunBackupIntent); + } + } + } + + private void startBackupAlarmsLocked(long delayBeforeFirstBackup) { + long when = System.currentTimeMillis() + delayBeforeFirstBackup; + mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, when, + BACKUP_INTERVAL, mRunBackupIntent); + } + + // Report whether the backup mechanism is currently enabled + public boolean isBackupEnabled() { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); + return mEnabled; // no need to synchronize just to read it + } + + // Report the name of the currently active transport + public String getCurrentTransport() { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "getCurrentTransport"); + Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); + return mCurrentTransport; + } + + // Report all known, available backup transports + public String[] listAllTransports() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); + + String[] list = null; + ArrayList<String> known = new ArrayList<String>(); + for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { + if (entry.getValue() != null) { + known.add(entry.getKey()); + } + } + + if (known.size() > 0) { + list = new String[known.size()]; + known.toArray(list); + } + return list; + } + + // Select which transport to use for the next backup operation. If the given + // name is not one of the available transports, no action is taken and the method + // returns null. + public String selectBackupTransport(String transport) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport"); + + synchronized (mTransports) { + String prevTransport = null; + if (mTransports.get(transport) != null) { + prevTransport = mCurrentTransport; + mCurrentTransport = transport; + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, transport); + Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport + + " returning " + prevTransport); + } else { + Log.w(TAG, "Attempt to select unavailable transport " + transport); + } + return prevTransport; + } + } + + // Callback: a requested backup agent has been instantiated. This should only + // be called from the Activity Manager. + public void agentConnected(String packageName, IBinder agentBinder) { + synchronized(mAgentConnectLock) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); + IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); + mConnectedAgent = agent; + mConnecting = false; + } else { + Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + + " claiming agent connected"); + } + mAgentConnectLock.notifyAll(); + } + } + + // Callback: a backup agent has failed to come up, or has unexpectedly quit. + // If the agent failed to come up in the first place, the agentBinder argument + // will be null. This should only be called from the Activity Manager. + public void agentDisconnected(String packageName) { + // TODO: handle backup being interrupted + synchronized(mAgentConnectLock) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + mConnectedAgent = null; + mConnecting = false; + } else { + Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid() + + " claiming agent disconnected"); + } + mAgentConnectLock.notifyAll(); + } + } + + // Hand off a restore session + public IRestoreSession beginRestoreSession(String transport) { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession"); + + synchronized(this) { + if (mActiveRestoreSession != null) { + Log.d(TAG, "Restore session requested but one already active"); + return null; + } + mActiveRestoreSession = new RestoreSession(transport); + } + return mActiveRestoreSession; + } + + // ----- Restore session ----- + + class RestoreSession extends IRestoreSession.Stub { + private static final String TAG = "RestoreSession"; + + private IBackupTransport mRestoreTransport = null; + RestoreSet[] mRestoreSets = null; + + RestoreSession(String transport) { + mRestoreTransport = getTransport(transport); + } + + // --- Binder interface --- + public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "getAvailableRestoreSets"); + + try { + synchronized(this) { + if (mRestoreSets == null) { + mRestoreSets = mRestoreTransport.getAvailableRestoreSets(); + } + return mRestoreSets; + } + } catch (RuntimeException e) { + Log.d(TAG, "getAvailableRestoreSets exception"); + e.printStackTrace(); + throw e; + } + } + + public int performRestore(long token, IRestoreObserver observer) + throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore"); + + Log.d(TAG, "performRestore token=" + token + " observer=" + observer); + + if (mRestoreSets != null) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token); + mBackupHandler.sendMessage(msg); + return 0; } } + } else { + if (DEBUG) Log.v(TAG, "No current restore set, not doing restore"); + } + return -1; + } + + public void endRestoreSession() throws android.os.RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, + "endRestoreSession"); + + Log.d(TAG, "endRestoreSession"); + + mRestoreTransport.finishRestore(); + mRestoreTransport = null; + synchronized(BackupManagerService.this) { + if (BackupManagerService.this.mActiveRestoreSession == this) { + BackupManagerService.this.mActiveRestoreSession = null; + } else { + Log.e(TAG, "ending non-current restore session"); + } } } } - + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { + long oldId = Binder.clearCallingIdentity(); + + pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") + + " / " + (!mProvisioned ? "not " : "") + "provisioned"); + pw.println("Available transports:"); + for (String t : listAllTransports()) { + String pad = (t.equals(mCurrentTransport)) ? " * " : " "; + pw.println(pad + t); + } int N = mBackupParticipants.size(); - pw.println("Participants:"); + pw.println("Participants: " + N); for (int i=0; i<N; i++) { int uid = mBackupParticipants.keyAt(i); pw.print(" uid: "); pw.println(uid); - HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i); - for (ServiceInfo s: services) { - pw.print(" "); - pw.println(s.toString()); + HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i); + for (ApplicationInfo app: participants) { + pw.println(" " + app.toString()); } } + pw.println("Pending: " + mPendingBackups.size()); + for (BackupRequest req : mPendingBackups.values()) { + pw.println(" " + req); + } + + Binder.restoreCallingIdentity(oldId); } } } diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java new file mode 100644 index 000000000000..28f09f53ff14 --- /dev/null +++ b/services/java/com/android/server/EntropyService.java @@ -0,0 +1,148 @@ +/* + * 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; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +import android.os.Binder; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.SystemProperties; +import android.util.Log; + +/** + * A service designed to load and periodically save "randomness" + * for the Linux kernel. + * + * <p>When a Linux system starts up, the entropy pool associated with + * {@code /dev/random} may be in a fairly predictable state. Applications which + * depend strongly on randomness may find {@code /dev/random} or + * {@code /dev/urandom} returning predictable data. In order to counteract + * this effect, it's helpful to carry the entropy pool information across + * shutdowns and startups. + * + * <p>This class was modeled after the script in + * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man + * 4 random</a>. + * + * <p>TODO: Investigate attempting to write entropy data at shutdown time + * instead of periodically. + */ +public class EntropyService extends Binder { + private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat"; + private static final String TAG = "EntropyService"; + private static final int ENTROPY_WHAT = 1; + private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs + private static final String RANDOM_DEV = "/dev/urandom"; + private static final long START_TIME = System.currentTimeMillis(); + private static final long START_NANOTIME = System.nanoTime(); + + /** + * Handler that periodically updates the entropy on disk. + */ + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what != ENTROPY_WHAT) { + Log.e(TAG, "Will not process invalid message"); + return; + } + writeEntropy(); + scheduleEntropyWriter(); + } + }; + + public EntropyService() { + loadInitialEntropy(); + addDeviceSpecificEntropy(); + writeEntropy(); + scheduleEntropyWriter(); + } + + private void scheduleEntropyWriter() { + mHandler.removeMessages(ENTROPY_WHAT); + mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD); + } + + private void loadInitialEntropy() { + try { + RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV); + } catch (IOException e) { + Log.w(TAG, "unable to load initial entropy (first boot?)", e); + } + } + + private void writeEntropy() { + try { + RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME); + } catch (IOException e) { + Log.w(TAG, "unable to write entropy", e); + } + } + + /** + * Add additional information to the kernel entropy pool. The + * information isn't necessarily "random", but that's ok. Even + * sending non-random information to {@code /dev/urandom} is useful + * because, while it doesn't increase the "quality" of the entropy pool, + * it mixes more bits into the pool, which gives us a higher degree + * of uncertainty in the generated randomness. Like nature, writes to + * the random device can only cause the quality of the entropy in the + * kernel to stay the same or increase. + * + * <p>For maximum effect, we try to target information which varies + * on a per-device basis, and is not easily observable to an + * attacker. + */ + private void addDeviceSpecificEntropy() { + PrintWriter out = null; + try { + out = new PrintWriter(new FileOutputStream(RANDOM_DEV)); + out.println("Copyright (C) 2009 The Android Open Source Project"); + out.println("All Your Randomness Are Belong To Us"); + out.println(START_TIME); + out.println(START_NANOTIME); + out.println(SystemProperties.get("ro.serialno")); + out.println(SystemProperties.get("ro.bootmode")); + out.println(SystemProperties.get("ro.baseband")); + out.println(SystemProperties.get("ro.carrier")); + out.println(SystemProperties.get("ro.bootloader")); + out.println(SystemProperties.get("ro.hardware")); + out.println(SystemProperties.get("ro.revision")); + out.println(System.currentTimeMillis()); + out.println(System.nanoTime()); + } catch (IOException e) { + Log.w(TAG, "Unable to add device specific data to the entropy pool", e); + } finally { + if (out != null) { + out.close(); + } + } + } + + private static String getSystemDir() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + systemDir.mkdirs(); + return systemDir.toString(); + } +} diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 3fc1e0ea5879..9b0a2d465dbe 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -35,6 +35,7 @@ import java.io.FileNotFoundException; */ class HeadsetObserver extends UEventObserver { private static final String TAG = HeadsetObserver.class.getSimpleName(); + private static final boolean LOG = false; private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w"; private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state"; @@ -61,7 +62,7 @@ class HeadsetObserver extends UEventObserver { @Override public void onUEvent(UEventObserver.UEvent event) { - Log.v(TAG, "Headset UEVENT: " + event.toString()); + if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString()); try { update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE"))); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index 72efca5eb4b9..d8c8c90fd254 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -163,8 +163,24 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return Collections.unmodifiableSet(mFilters); } - public List<R> queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List<R> queryIntentFromList(Intent intent, String resolvedType, + boolean defaultOnly, ArrayList<ArrayList<F>> listCut) { + ArrayList<R> resultList = new ArrayList<R>(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + final String scheme = intent.getScheme(); + int N = listCut.size(); + for (int i = 0; i < N; ++i) { + buildResolveList(intent, debug, defaultOnly, + resolvedType, scheme, listCut.get(i), resultList); + } + sortResults(resultList); + return resultList; + } + + public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 05888e03d4ea..fab97b1e1574 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -46,7 +46,6 @@ import android.location.Address; import android.location.IGeocodeProvider; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; -import android.location.ILocationCollector; import android.location.ILocationListener; import android.location.ILocationManager; import android.location.ILocationProvider; @@ -66,15 +65,12 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; -import android.util.Config; import android.util.Log; import android.util.PrintWriterPrinter; -import android.util.SparseIntArray; import com.android.internal.location.GpsLocationProvider; import com.android.internal.location.LocationProviderProxy; import com.android.internal.location.MockProvider; -import com.android.server.am.BatteryStatsService; /** * The service class that manages LocationProviders and issues location @@ -107,8 +103,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; private static final String INSTALL_LOCATION_PROVIDER = android.Manifest.permission.INSTALL_LOCATION_PROVIDER; - private static final String INSTALL_LOCATION_COLLECTOR = - android.Manifest.permission.INSTALL_LOCATION_COLLECTOR; // Set of providers that are explicitly enabled private final Set<String> mEnabledProviders = new HashSet<String>(); @@ -171,9 +165,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private HashMap<String,Location> mLastKnownLocation = new HashMap<String,Location>(); - // Location collector - private ILocationCollector mCollector; - private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; // for Settings change notification @@ -516,6 +507,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void removeProvider(LocationProviderProxy provider) { mProviders.remove(provider); + provider.unlinkProvider(); mProvidersByName.remove(provider.getName()); } @@ -630,16 +622,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void installLocationCollector(ILocationCollector collector) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission"); - } - - // FIXME - only support one collector - mCollector = collector; - } - public void installGeocodeProvider(IGeocodeProvider provider) { if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) != PackageManager.PERMISSION_GRANTED) { @@ -666,14 +648,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void checkPermissionsSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException( "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); @@ -682,14 +664,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private boolean isAllowedProviderSafe(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { return false; } if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) + && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) { return false; } @@ -788,8 +770,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); - deadReceivers.add(record.mReceiver); } + deadReceivers.add(record.mReceiver); } listeners++; } @@ -1093,7 +1075,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mGpsStatusProvider == null) { return false; } - if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); } @@ -1121,7 +1103,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // first check for permission to the provider checkPermissionsSafe(provider); // and check for ACCESS_LOCATION_EXTRA_COMMANDS - if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS) + if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission"); } @@ -1619,23 +1601,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { Location location = (Location) msg.obj; + String provider = location.getProvider(); - if (mCollector != null && - LocationManager.GPS_PROVIDER.equals(location.getProvider())) { - try { - mCollector.updateLocation(location); - } catch (RemoteException e) { - Log.w(TAG, "mCollector.updateLocation failed"); - mCollector = null; + // notify other providers of the new location + for (int i = mProviders.size() - 1; i >= 0; i--) { + LocationProviderProxy proxy = mProviders.get(i); + if (!provider.equals(proxy.getName())) { + proxy.updateLocation(location); } } - String provider = location.getProvider(); - if (!isAllowedBySettingsLocked(provider)) { - return; + if (isAllowedBySettingsLocked(provider)) { + handleLocationChangedLocked(location); } - - handleLocationChangedLocked(location); } } } catch (Exception e) { @@ -1935,7 +1913,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { pw.println("Current Location Manager state:"); pw.println(" sProvidersLoaded=" + sProvidersLoaded); - pw.println(" mCollector=" + mCollector); pw.println(" Listeners:"); int N = mReceivers.size(); for (int i=0; i<N; i++) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 6ed8b4c6accb..854138c74085 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -16,47 +16,52 @@ package com.android.server; +import com.android.server.status.IconData; +import com.android.server.status.NotificationData; +import com.android.server.status.StatusBarService; + import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentQueryMap; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.media.AudioManager; +import android.database.ContentObserver; import android.media.AsyncPlayer; -import android.media.RingtoneManager; +import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; import android.os.Binder; -import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Power; +import android.os.RemoteException; import android.os.Vibrator; import android.provider.Settings; -import android.util.Config; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.server.status.IconData; -import com.android.server.status.NotificationData; -import com.android.server.status.StatusBarService; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.io.IOException; class NotificationManagerService extends INotificationManager.Stub { @@ -88,6 +93,12 @@ class NotificationManagerService extends INotificationManager.Stub private NotificationRecord mVibrateNotification; private Vibrator mVibrator = new Vibrator(); + // adb + private int mBatteryPlugged; + private boolean mAdbEnabled = false; + private boolean mAdbNotificationShown = false; + private Notification mAdbNotification; + private ArrayList<NotificationRecord> mNotificationList; private ArrayList<ToastRecord> mToastQueue; @@ -98,7 +109,7 @@ class NotificationManagerService extends INotificationManager.Stub private boolean mBatteryLow; private boolean mBatteryFull; private NotificationRecord mLedNotification; - + private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on @@ -297,6 +308,9 @@ class NotificationManagerService extends INotificationManager.Stub mBatteryFull = batteryFull; updateLights(); } + + mBatteryPlugged = intent.getIntExtra("plugged", 0); + updateAdbNotification(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) { Uri uri = intent.getData(); @@ -312,6 +326,31 @@ class NotificationManagerService extends INotificationManager.Stub } }; + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ADB_ENABLED), false, this); + update(); + } + + @Override public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + mAdbEnabled = Settings.Secure.getInt(resolver, + Settings.Secure.ADB_ENABLED, 0) != 0; + updateAdbNotification(); + } + } + private final SettingsObserver mSettingsObserver; + NotificationManagerService(Context context, StatusBarService statusBar, HardwareService hardware) { @@ -333,6 +372,9 @@ class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); mContext.registerReceiver(mIntentReceiver, filter); + + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); } // Toasts @@ -594,6 +636,9 @@ class NotificationManagerService extends INotificationManager.Stub Binder.restoreCallingIdentity(identity); } } + + sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg); + } else { if (old != null && old.statusBarKey != null) { long identity = Binder.clearCallingIdentity(); @@ -676,6 +721,26 @@ class NotificationManagerService extends INotificationManager.Stub idOut[0] = id; } + private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification, + CharSequence packageName) { + AccessibilityManager manager = AccessibilityManager.getInstance(mContext); + if (!manager.isEnabled()) { + return; + } + + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + event.setPackageName(packageName); + event.setClassName(Notification.class.getName()); + event.setParcelableData(notification); + CharSequence tickerText = notification.tickerText; + if (!TextUtils.isEmpty(tickerText)) { + event.getText().add(tickerText); + } + + manager.sendAccessibilityEvent(event); + } + private void cancelNotificationLocked(NotificationRecord r) { // status bar if (r.notification.icon != 0) { @@ -869,6 +934,62 @@ class NotificationManagerService extends INotificationManager.Stub return -1; } + // This is here instead of StatusBarPolicy because it is an important + // security feature that we don't want people customizing the platform + // to accidentally lose. + private void updateAdbNotification() { + if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) { + if (!mAdbNotificationShown) { + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + Resources r = mContext.getResources(); + CharSequence title = r.getText( + com.android.internal.R.string.adb_active_notification_title); + CharSequence message = r.getText( + com.android.internal.R.string.adb_active_notification_message); + + if (mAdbNotification == null) { + mAdbNotification = new Notification(); + mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning; + mAdbNotification.when = 0; + mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; + mAdbNotification.tickerText = title; + mAdbNotification.defaults |= Notification.DEFAULT_SOUND; + } + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + // Note: we are hard-coding the component because this is + // an important security UI that we don't want anyone + // intercepting. + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + + mAdbNotification.setLatestEventInfo(mContext, title, message, pi); + + mAdbNotificationShown = true; + notificationManager.notify( + com.android.internal.R.string.adb_active_notification_title, + mAdbNotification); + } + } + + } else if (mAdbNotificationShown) { + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + mAdbNotificationShown = false; + notificationManager.cancel( + com.android.internal.R.string.adb_active_notification_title); + } + } + } + // ====================================================================== @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java new file mode 100644 index 000000000000..786f42305c96 --- /dev/null +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -0,0 +1,413 @@ +/* + * 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; + +import android.app.BackupAgent; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +/** + * We back up the signatures of each package so that during a system restore, + * we can verify that the app whose data we think we have matches the app + * actually resident on the device. + * + * Since the Package Manager isn't a proper "application" we just provide a + * direct IBackupAgent implementation and hand-construct it at need. + */ +public class PackageManagerBackupAgent extends BackupAgent { + private static final String TAG = "PMBA"; + private static final boolean DEBUG = true; + + // key under which we store global metadata (individual app metadata + // is stored using the package name as a key) + private static final String GLOBAL_METADATA_KEY = "@meta@"; + + private List<PackageInfo> mAllPackages; + private PackageManager mPackageManager; + // version & signature info of each app in a restore set + private HashMap<String, Metadata> mRestoredSignatures; + // The version info of each backed-up app as read from the state file + private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>(); + + private final HashSet<String> mExisting = new HashSet<String>(); + private int mStoredSdkVersion; + private String mStoredIncrementalVersion; + private boolean mHasMetadata; + + public class Metadata { + public int versionCode; + public Signature[] signatures; + + Metadata(int version, Signature[] sigs) { + versionCode = version; + signatures = sigs; + } + } + + // 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<PackageInfo> packages) { + mPackageManager = packageMgr; + mAllPackages = packages; + mRestoredSignatures = null; + mHasMetadata = false; + } + + public boolean hasMetadata() { + return mHasMetadata; + } + + public Metadata getRestoredMetadata(String packageName) { + if (mRestoredSignatures == null) { + Log.w(TAG, "getRestoredMetadata() before metadata read!"); + return null; + } + + return mRestoredSignatures.get(packageName); + } + + // The backed up data is the signature block for each app, keyed by + // the package name. + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + if (DEBUG) Log.v(TAG, "onBackup()"); + + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outWriter = new DataOutputStream(bufStream); + parseStateFile(oldState); + + // If the stored version string differs, we need to re-backup all + // of the metadata. We force this by removing everything from the + // "already backed up" map built by parseStateFile(). + if (mStoredIncrementalVersion == null + || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { + Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " + + Build.VERSION.INCREMENTAL + " - rewriting"); + mExisting.clear(); + } + + try { + /* + * Global metadata: + * + * int SDKversion -- the SDK version of the OS itself on the device + * that produced this backup set. Used to reject + * backups from later OSes onto earlier ones. + * String incremental -- the incremental release name of the OS stored in + * the backup set. + */ + if (!mExisting.contains(GLOBAL_METADATA_KEY)) { + if (DEBUG) Log.v(TAG, "Storing global metadata key"); + outWriter.writeInt(Build.VERSION.SDK_INT); + outWriter.writeUTF(Build.VERSION.INCREMENTAL); + byte[] metadata = bufStream.toByteArray(); + data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length); + data.writeEntityData(metadata, metadata.length); + } else { + if (DEBUG) Log.v(TAG, "Global metadata key already stored"); + // don't consider it to have been skipped/deleted + mExisting.remove(GLOBAL_METADATA_KEY); + } + + // 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 (PackageInfo pkg : mAllPackages) { + String packName = pkg.packageName; + if (packName.equals(GLOBAL_METADATA_KEY)) { + // We've already handled the metadata key; skip it here + continue; + } else { + PackageInfo info = null; + try { + info = mPackageManager.getPackageInfo(packName, + PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + // Weird; we just found it, and now are told it doesn't exist. + // Treat it as having been removed from the device. + mExisting.add(packName); + continue; + } + + boolean doBackup = false; + if (!mExisting.contains(packName)) { + // We haven't backed up this app before + doBackup = true; + } else { + // We *have* backed this one up before. Check whether the version + // of the backup matches the version of the current app; if they + // don't match, the app has been updated and we need to store its + // metadata again. In either case, take it out of mExisting so that + // we don't consider it deleted later. + if (info.versionCode != mStateVersions.get(packName).versionCode) { + doBackup = true; + } + mExisting.remove(packName); + } + + if (doBackup) { + // We need to store this app's metadata + /* + * Metadata for each package: + * + * int version -- [4] the package's versionCode + * byte[] signatures -- [len] flattened Signature[] of the package + */ + + // marshal the version code in a canonical form + bufStream.reset(); + outWriter.writeInt(info.versionCode); + byte[] versionBuf = bufStream.toByteArray(); + + byte[] sigs = flattenSignatureArray(info.signatures); + + // !!! TODO: take out this debugging + if (DEBUG) { + Log.v(TAG, "+ metadata for " + packName + + " version=" + info.versionCode + + " versionLen=" + versionBuf.length + + " sigsLen=" + sigs.length); + } + // Now we can write the backup entity for this package + data.writeEntityHeader(packName, versionBuf.length + sigs.length); + data.writeEntityData(versionBuf, versionBuf.length); + data.writeEntityData(sigs, sigs.length); + } + } + } + + // At this point, the only entries in 'existing' are apps that were + // mentioned in the saved state file, but appear to no longer be present + // on the device. Write a deletion entity for them. + for (String app : mExisting) { + // !!! TODO: take out this msg + if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); + try { + data.writeEntityHeader(app, -1); + } catch (IOException e) { + Log.e(TAG, "Unable to write package deletions!"); + return; + } + } + } catch (IOException e) { + // Real error writing data + Log.e(TAG, "Unable to write package backup data file!"); + return; + } + + // Finally, write the new state blob -- just the list of all apps we handled + writeStateFile(mAllPackages, newState); + } + + // "Restore" here is a misnomer. What we're really doing is reading back the + // set of app signatures associated with each backed-up app in this restore + // image. We'll use those later to determine what we can legitimately restore. + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); + HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); + if (DEBUG) Log.v(TAG, "onRestore()"); + int storedSystemVersion = -1; + + while (data.readNextHeader()) { + String key = data.getKey(); + int dataSize = data.getDataSize(); + + if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize); + + // generic setup to parse any entity data + byte[] dataBuf = new byte[dataSize]; + data.readEntityData(dataBuf, 0, dataSize); + ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); + DataInputStream in = new DataInputStream(baStream); + + if (key.equals(GLOBAL_METADATA_KEY)) { + int storedSdkVersion = in.readInt(); + if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion); + if (storedSystemVersion > Build.VERSION.SDK_INT) { + // returning before setting the sig map means we rejected the restore set + Log.w(TAG, "Restore set was from a later version of Android; not restoring"); + return; + } + mStoredSdkVersion = storedSdkVersion; + mStoredIncrementalVersion = in.readUTF(); + mHasMetadata = true; + // !!! TODO: remove this debugging output + if (DEBUG) { + Log.i(TAG, "Restore set version " + storedSystemVersion + + " is compatible with OS version " + Build.VERSION.SDK_INT + + " (" + mStoredIncrementalVersion + " vs " + + Build.VERSION.INCREMENTAL + ")"); + } + } else { + // it's a file metadata record + int versionCode = in.readInt(); + Signature[] sigs = unflattenSignatureArray(in); +// !!! TODO: take out this debugging + if (DEBUG) { + Log.i(TAG, " restored metadata for " + key + + " dataSize=" + dataSize + + " versionCode=" + versionCode + " sigs=" + sigs); + } + + ApplicationInfo app = new ApplicationInfo(); + app.packageName = key; + restoredApps.add(app); + sigMap.put(key, new Metadata(versionCode, sigs)); + } + } + + // On successful completion, cache the signature map for the Backup Manager to use + mRestoredSignatures = sigMap; + } + + + // Util: convert an array of Signatures into a flattened byte buffer. The + // flattened format contains enough info to reconstruct the signature array. + private byte[] flattenSignatureArray(Signature[] allSigs) { + ByteArrayOutputStream outBuf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(outBuf); + + // build the set of subsidiary buffers + try { + // first the # of signatures in the array + out.writeInt(allSigs.length); + + // then the signatures themselves, length + flattened buffer + for (Signature sig : allSigs) { + byte[] flat = sig.toByteArray(); + out.writeInt(flat.length); + out.write(flat); + } + } catch (IOException e) { + // very strange; we're writing to memory here. abort. + return null; + } + + return outBuf.toByteArray(); + } + + private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { + Signature[] sigs = null; + + try { + int num = in.readInt(); + Log.v(TAG, " ... unflatten read " + num); + sigs = new Signature[num]; + for (int i = 0; i < num; i++) { + int len = in.readInt(); + byte[] flatSig = new byte[len]; + in.read(flatSig); + sigs[i] = new Signature(flatSig); + } + } catch (EOFException e) { + // clean termination + if (sigs == null) { + Log.w(TAG, "Empty signature block found"); + } + } catch (IOException e) { + Log.d(TAG, "Unable to unflatten sigs"); + return null; + } + + return sigs; + } + + // Util: parse out an existing state file into a usable structure + private void parseStateFile(ParcelFileDescriptor stateFile) { + mExisting.clear(); + mStateVersions.clear(); + mStoredSdkVersion = 0; + mStoredIncrementalVersion = null; + + // The state file is just the list of app names we have stored signatures for + // with the exception of the metadata block, to which is also appended the + // version numbers corresponding with the last time we wrote this PM block. + // If they mismatch the current system, we'll re-store the metadata key. + FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); + DataInputStream in = new DataInputStream(instream); + + int bufSize = 256; + byte[] buf = new byte[bufSize]; + try { + String pkg = in.readUTF(); + if (pkg.equals(GLOBAL_METADATA_KEY)) { + mStoredSdkVersion = in.readInt(); + mStoredIncrementalVersion = in.readUTF(); + mExisting.add(GLOBAL_METADATA_KEY); + } else { + Log.e(TAG, "No global metadata in state file!"); + return; + } + + // The global metadata was first; now read all the apps + while (true) { + pkg = in.readUTF(); + int versionCode = in.readInt(); + mExisting.add(pkg); + mStateVersions.put(pkg, new Metadata(versionCode, null)); + } + } catch (EOFException eof) { + // safe; we're done + } catch (IOException e) { + // whoops, bad state file. abort. + Log.e(TAG, "Unable to read Package Manager state file: " + e); + } + } + + // Util: write out our new backup state file + private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { + FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); + DataOutputStream out = new DataOutputStream(outstream); + + try { + // by the time we get here we know we've stored the global metadata record + out.writeUTF(GLOBAL_METADATA_KEY); + out.writeInt(Build.VERSION.SDK_INT); + out.writeUTF(Build.VERSION.INCREMENTAL); + + // now write all the app names too + for (PackageInfo pkg : pkgs) { + out.writeUTF(pkg.packageName); + out.writeInt(pkg.versionCode); + } + } catch (IOException e) { + Log.e(TAG, "Unable to write package manager state file!"); + return; + } + } +} diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 079f363b5fc8..06435c87ff19 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -26,13 +26,12 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; import android.app.IActivityManager; -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -57,6 +56,8 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -147,6 +148,7 @@ class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; + final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -258,6 +260,7 @@ class PackageManagerService extends IPackageManager.Stub { final ResolveInfo mResolveInfo = new ResolveInfo(); ComponentName mResolveComponentName; PackageParser.Package mPlatformPackage; + private boolean mCompatibilityModeEnabled = true; public static final IPackageManager main(Context context, boolean factoryTest) { PackageManagerService m = new PackageManagerService(context, factoryTest); @@ -297,6 +300,7 @@ class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; + mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(); mSettings.addSharedUserLP("android.uid.system", @@ -366,6 +370,10 @@ class PackageManagerService extends IPackageManager.Stub { startTime); int scanMode = SCAN_MONITOR; + if (mNoDexOpt) { + Log.w(TAG, "Running ENG build: no pre-dexopt!"); + scanMode |= SCAN_NO_DEX; + } final HashSet<String> libFiles = new HashSet<String>(); @@ -508,7 +516,7 @@ class PackageManagerService extends IPackageManager.Stub { } // synchronized (mPackages) } // synchronized (mInstallLock) } - + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -884,7 +892,11 @@ class PackageManagerService extends IPackageManager.Stub { + ": " + p); if (p != null) { // Note: isEnabledLP() does not apply here - always return info - return PackageParser.generateApplicationInfo(p, flags); + ApplicationInfo appInfo = PackageParser.generateApplicationInfo(p, flags); + if (!mCompatibilityModeEnabled) { + appInfo.disableCompatibilityMode(); + } + return appInfo; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; @@ -922,7 +934,7 @@ class PackageManagerService extends IPackageManager.Stub { }); } - public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) { + public void freeStorage(final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); // Queue up an async operation since clearing cache may take a little while. @@ -936,11 +948,13 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Couldn't clear application caches"); } } - if(opFinishedIntent != null) { + if(pi != null) { try { // Callback via pending intent - opFinishedIntent.send((retCode >= 0) ? 1 : 0); - } catch (CanceledException e1) { + int code = (retCode >= 0) ? 1 : 0; + pi.sendIntent(null, code, null, + null, null); + } catch (SendIntentException e1) { Log.i(TAG, "Failed to send pending intent"); } } @@ -951,8 +965,8 @@ class PackageManagerService extends IPackageManager.Stub { public ActivityInfo getActivityInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); - if (Config.LOGV) Log.v( - TAG, "getActivityInfo " + component + ": " + a); + + if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLP(a.info, flags)) { return PackageParser.generateActivityInfo(a, flags); } @@ -1203,6 +1217,11 @@ class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) { List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags); + return chooseBestActivity(intent, resolvedType, flags, query); + } + + private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, + int flags, List<ResolveInfo> query) { if (query != null) { final int N = query.size(); if (N == 1) { @@ -1243,8 +1262,7 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List<PreferredActivity> prefs = - mSettings.mPreferredActivities.queryIntent(null, - intent, resolvedType, + mSettings.mPreferredActivities.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); if (prefs != null && prefs.size() > 0) { // First figure out how good the original match set is. @@ -1320,8 +1338,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mActivities. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mActivities.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent, + resolvedType, flags, pkg.activities); + } + return null; } } @@ -1488,9 +1515,30 @@ class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { + ComponentName comp = intent.getComponent(); + if (comp != null) { + List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + ActivityInfo ai = getReceiverInfo(comp, flags); + if (ai != null) { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + synchronized (mPackages) { - return (List<ResolveInfo>)mReceivers. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mReceivers.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent, + resolvedType, flags, pkg.receivers); + } + return null; } } @@ -1523,8 +1571,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mServices. - queryIntent(null, intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mServices.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>)mServices.queryIntentForPackage(intent, + resolvedType, flags, pkg.services); + } + return null; } } @@ -1837,7 +1894,56 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + public boolean performDexOpt(String packageName) { + if (!mNoDexOpt) { + return false; + } + PackageParser.Package p; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null || p.mDidDexOpt) { + return false; + } + } + synchronized (mInstallLock) { + return performDexOptLI(p, false) == DEX_OPT_PERFORMED; + } + } + + static final int DEX_OPT_SKIPPED = 0; + static final int DEX_OPT_PERFORMED = 1; + static final int DEX_OPT_FAILED = -1; + + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { + boolean performed = false; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) { + String path = pkg.mScanPath; + int ret = 0; + try { + if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !pkg.mForwardLocked); + pkg.mDidDexOpt = true; + performed = true; + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Apk not found for dexopt: " + path); + ret = -1; + } catch (IOException e) { + Log.w(TAG, "Exception reading apk: " + path, e); + ret = -1; + } + if (ret < 0) { + //error from installer + return DEX_OPT_FAILED; + } + } + + return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; + } + private PackageParser.Package scanPackageLI( File scanFile, File destCodeFile, File destResourceFile, PackageParser.Package pkg, int parseFlags, int scanMode) { @@ -2126,37 +2232,18 @@ class PackageManagerService extends IPackageManager.Stub { String path = scanFile.getPath(); if (scanFileNewer) { Log.i(TAG, path + " changed; unpacking"); - try { - cachePackageSharedLibsLI(pkg, dataPath, scanFile); - } catch (IOException e) { - Log.e(TAG, "Failure extracting shared libs", e); - if(mInstaller != null) { - mInstaller.remove(pkgName); - } else { - dataPath.delete(); - } - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + int err = cachePackageSharedLibsLI(pkg, dataPath, scanFile); + if (err != PackageManager.INSTALL_SUCCEEDED) { + mLastScanError = err; return null; } } - if ((scanMode&SCAN_NO_DEX) == 0 - && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - int ret = 0; - try { - if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - (scanMode&SCAN_FORWARD_LOCKED) == 0); - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; - } catch (IOException e) { - Log.w(TAG, "Exception reading apk: " + path, e); - ret = -1; - } - if (ret < 0) { - //error from installer + pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0; + pkg.mScanPath = path; + + if ((scanMode&SCAN_NO_DEX) == 0) { + if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } @@ -2419,14 +2506,15 @@ class PackageManagerService extends IPackageManager.Stub { return pkg; } - private void cachePackageSharedLibsLI(PackageParser.Package pkg, - File dataPath, File scanFile) throws IOException { + private int cachePackageSharedLibsLI(PackageParser.Package pkg, + File dataPath, File scanFile) { File sharedLibraryDir = new File(dataPath.getPath() + "/lib"); - final String sharedLibraryABI = "armeabi"; + final String sharedLibraryABI = Build.CPU_ABI; final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/"; final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib"; final String sharedLibrarySuffix = ".so"; - boolean createdSharedLib = false; + boolean hasNativeCode = false; + boolean installedNativeCode = false; try { ZipFile zipFile = new ZipFile(scanFile); Enumeration<ZipEntry> entries = @@ -2435,9 +2523,15 @@ class PackageManagerService extends IPackageManager.Stub { while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) { + if (!hasNativeCode && entry.getName().startsWith("lib")) { + hasNativeCode = true; + } continue; } String entryName = entry.getName(); + if (entryName.startsWith("lib/")) { + hasNativeCode = true; + } if (! (entryName.startsWith(apkSharedLibraryPrefix) && entryName.endsWith(sharedLibrarySuffix))) { continue; @@ -2448,6 +2542,9 @@ class PackageManagerService extends IPackageManager.Stub { || (!FileUtils.isFilenameSafe(new File(libFileName)))) { continue; } + + installedNativeCode = true; + String sharedLibraryFilePath = sharedLibraryDir.getPath() + File.separator + libFileName; File sharedLibraryFile = new File(sharedLibraryFilePath); @@ -2459,19 +2556,23 @@ class PackageManagerService extends IPackageManager.Stub { } if (mInstaller == null) { sharedLibraryDir.mkdir(); - createdSharedLib = true; } cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, sharedLibraryFile); } } } catch (IOException e) { - Log.e(TAG, "Failed to cache package shared libs", e); - if(createdSharedLib) { - sharedLibraryDir.delete(); - } - throw e; + Log.w(TAG, "Failed to cache package shared libs", e); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } + + if (hasNativeCode && !installedNativeCode) { + Log.w(TAG, "Install failed: .apk has native code but none for arch " + + Build.CPU_ABI); + return PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE; + } + + return PackageManager.INSTALL_SUCCEEDED; } private void cacheSharedLibLI(PackageParser.Package pkg, @@ -2801,6 +2902,21 @@ class PackageManagerService extends IPackageManager.Stub { // we can't add any new permissions to it. if (!gp.loadedPermissions.contains(perm)) { allowed = false; + // Except... if this is a permission that was added + // to the platform (note: need to only do this when + // updating the platform). + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip<NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (npi.name.equals(perm) + && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { + allowed = true; + Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg " + + pkg.packageName); + break; + } + } } } if (allowed) { @@ -2839,20 +2955,38 @@ class PackageManagerService extends IPackageManager.Stub { private final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; - return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, int flags) { + public List queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; - return super.queryIntent( - resolver, intent, resolvedType, + return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } + public List queryIntentForPackage(Intent intent, String resolvedType, int flags, + ArrayList<PackageParser.Activity> packageActivities) { + if (packageActivities == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; + int N = packageActivities.size(); + ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut = + new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N); + + ArrayList<PackageParser.ActivityIntentInfo> intentFilters; + for (int i = 0; i < N; ++i) { + intentFilters = packageActivities.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + listCut.add(intentFilters); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut); + } + public final void addActivity(PackageParser.Activity a, String type) { mActivities.put(a.component, a); if (SHOW_INFO || Config.LOGV) Log.v( @@ -2860,8 +2994,7 @@ class PackageManagerService extends IPackageManager.Stub { (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); int NI = a.intents.size(); - int j; - for (j=0; j<NI; j++) { + for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (SHOW_INFO || Config.LOGV) { Log.v(TAG, " IntentFilter:"); @@ -2881,8 +3014,7 @@ class PackageManagerService extends IPackageManager.Stub { (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); int NI = a.intents.size(); - int j; - for (j=0; j<NI; j++) { + for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (SHOW_INFO || Config.LOGV) { Log.v(TAG, " IntentFilter:"); @@ -2969,20 +3101,38 @@ class PackageManagerService extends IPackageManager.Stub { private final class ServiceIntentResolver extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> { - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, boolean defaultOnly) { + public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; - return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(ContentResolver resolver, Intent intent, - String resolvedType, int flags) { + public List queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; - return super.queryIntent( - resolver, intent, resolvedType, + return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } + public List queryIntentForPackage(Intent intent, String resolvedType, int flags, + ArrayList<PackageParser.Service> packageServices) { + if (packageServices == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; + int N = packageServices.size(); + ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = + new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); + + ArrayList<PackageParser.ServiceIntentInfo> intentFilters; + for (int i = 0; i < N; ++i) { + intentFilters = packageServices.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + listCut.add(intentFilters); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut); + } + public final void addService(PackageParser.Service s) { mServices.put(s.component, s); if (SHOW_INFO || Config.LOGV) Log.v( @@ -3142,6 +3292,7 @@ class PackageManagerService extends IPackageManager.Stub { if (extras != null) { intent.putExtras(extras); } + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); am.broadcastIntent( null, intent, null, null, 0, null, null, null, false, false); @@ -3545,7 +3696,9 @@ class PackageManagerService extends IPackageManager.Stub { } else { // Re installation failed. Restore old information // Remove new pkg information - removePackageLI(newPackage, true); + if (newPackage != null) { + removePackageLI(newPackage, true); + } // Add back the old system package scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, oldPkgSetting.resourcePath, @@ -4084,6 +4237,7 @@ class PackageManagerService extends IPackageManager.Stub { return false; } synchronized (mPackages) { + grantPermissionsLP(newPkg, true); mSettings.writeLP(); } return true; @@ -4445,6 +4599,42 @@ class PackageManagerService extends IPackageManager.Stub { } } + public void replacePreferredActivity(IntentFilter filter, int match, + ComponentName[] set, ComponentName activity) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + if (filter.countActions() != 1) { + throw new IllegalArgumentException( + "replacePreferredActivity expects filter to have only 1 action."); + } + if (filter.countCategories() != 1) { + throw new IllegalArgumentException( + "replacePreferredActivity expects filter to have only 1 category."); + } + if (filter.countDataAuthorities() != 0 + || filter.countDataPaths() != 0 + || filter.countDataSchemes() != 0 + || filter.countDataTypes() != 0) { + throw new IllegalArgumentException( + "replacePreferredActivity expects filter to have no data authorities, " + + "paths, schemes or types."); + } + synchronized (mPackages) { + Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); + String action = filter.getAction(0); + String category = filter.getCategory(0); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) { + it.remove(); + Log.i(TAG, "Removed preferred activity " + pa.mActivity + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + } + } + addPreferredActivity(filter, match, set, activity); + } + } + public void clearPackagePreferredActivities(String packageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -4609,6 +4799,14 @@ class PackageManagerService extends IPackageManager.Stub { public void systemReady() { mSystemReady = true; + + // Read the compatibilty setting when the system is ready. + mCompatibilityModeEnabled = android.provider.Settings.System.getInt( + mContext.getContentResolver(), + android.provider.Settings.System.COMPATIBILITY_MODE, 1) == 1; + if (DEBUG_SETTINGS) { + Log.d(TAG, "compatibility mode:" + mCompatibilityModeEnabled); + } } public boolean isSafeMode() { @@ -5607,24 +5805,15 @@ class PackageManagerService extends IPackageManager.Stub { // Check to see if its a disabled system app PackageSetting ps = mDisabledSysPackages.get(name); if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) { - // Could be a replaced system package - // Note that if the user replaced a system app, the user has to physically - // delete the new one in order to revert to the system app. So even - // if the user updated the system app via an update, the user still - // has to delete the one installed in the data partition in order to pick up the - // new system package. + // This is an updated system app with versions in both system + // and data partition. Just let the most recent version + // take precedence. return p; - } else if ((p.pkg != null) && (p.pkg.applicationInfo != null) && - ((p.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) { - // Check for non-system apps + } else if ((p.pkg != null) && (p.pkg.applicationInfo != null)) { + // Let the app continue with previous uid if code path changes. reportSettingsProblem(Log.WARN, "Package " + name + " codePath changed from " + p.codePath + " to " + codePath + "; Retaining data and using new code"); - } else { - reportSettingsProblem(Log.WARN, - "Package " + name + " codePath changed from " + p.codePath - + " to " + codePath + "; replacing with new"); - p = null; } } else if (p.sharedUser != sharedUser) { reportSettingsProblem(Log.WARN, @@ -5709,7 +5898,7 @@ class PackageManagerService extends IPackageManager.Stub { continue; } for (PackageSetting pkg:sus.packages) { - if (pkg.grantedPermissions.contains (eachPerm)) { + if (pkg.pkg.requestedPermissions.contains(eachPerm)) { used = true; break; } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index c5ea5fa9652c..79d78ad13a94 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -709,7 +709,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage p.awakeOnSet = true; } } else { - mPokeLocks.remove(token); + PokeLock rLock = mPokeLocks.remove(token); + if (rLock != null) { + token.unlinkToDeath(rLock, 0); + } } int oldPokey = mPokey; diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index 55adabbfa7a0..58f8980c004d 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -54,7 +54,10 @@ public class ProcessStats { PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime }; + /** Stores user time and system time in 100ths of a second. */ private final long[] mProcessStatsData = new long[2]; + /** Stores user time and system time in 100ths of a second. */ + private final long[] mSinglePidStatsData = new long[2]; private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { PROC_SPACE_TERM, @@ -418,7 +421,18 @@ public class ProcessStats { return pids; } - + + public long getCpuTimeForPid(int pid) { + final String statFile = "/proc/" + pid + "/stat"; + final long[] statsData = mSinglePidStatsData; + if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT, + null, statsData, null)) { + long time = statsData[0] + statsData[1]; + return time; + } + return 0; + } + final public int getLastUserTime() { return mRelUserTime; } diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java new file mode 100644 index 000000000000..4ac1c6e21b3f --- /dev/null +++ b/services/java/com/android/server/RandomBlock.java @@ -0,0 +1,100 @@ +/* + * 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; + +import android.util.Log; + +import java.io.Closeable; +import java.io.DataOutput; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; + +/** + * A 4k block of random {@code byte}s. + */ +class RandomBlock { + + private static final String TAG = "RandomBlock"; + private static final int BLOCK_SIZE = 4096; + private byte[] block = new byte[BLOCK_SIZE]; + + private RandomBlock() { } + + static RandomBlock fromFile(String filename) throws IOException { + Log.v(TAG, "reading from file " + filename); + InputStream stream = null; + try { + stream = new FileInputStream(filename); + return fromStream(stream); + } finally { + close(stream); + } + } + + private static RandomBlock fromStream(InputStream in) throws IOException { + RandomBlock retval = new RandomBlock(); + int total = 0; + while(total < BLOCK_SIZE) { + int result = in.read(retval.block, total, BLOCK_SIZE - total); + if (result == -1) { + throw new EOFException(); + } + total += result; + } + return retval; + } + + void toFile(String filename) throws IOException { + Log.v(TAG, "writing to file " + filename); + RandomAccessFile out = null; + try { + out = new RandomAccessFile(filename, "rws"); + toDataOut(out); + truncateIfPossible(out); + } finally { + close(out); + } + } + + private static void truncateIfPossible(RandomAccessFile f) { + try { + f.setLength(BLOCK_SIZE); + } catch (IOException e) { + // ignore this exception. Sometimes, the file we're trying to + // write is a character device, such as /dev/urandom, and + // these character devices do not support setting the length. + } + } + + private void toDataOut(DataOutput out) throws IOException { + out.write(block); + } + + private static void close(Closeable c) { + try { + if (c == null) { + return; + } + c.close(); + } catch (IOException e) { + Log.w(TAG, "IOException thrown while closing Closeable", e); + } + } +} diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index b2530383f347..ceef39f93ff9 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -19,7 +19,7 @@ package com.android.server; import android.content.Context; import android.hardware.ISensorService; import android.os.Binder; -import android.os.ParcelFileDescriptor; +import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.util.Config; @@ -101,7 +101,7 @@ class SensorService extends ISensorService.Stub { _sensors_control_init(); } - public ParcelFileDescriptor getDataChanel() throws RemoteException { + public Bundle getDataChannel() throws RemoteException { return _sensors_control_open(); } @@ -190,7 +190,7 @@ class SensorService extends ISensorService.Stub { ArrayList<Listener> mListeners = new ArrayList<Listener>(); private static native int _sensors_control_init(); - private static native ParcelFileDescriptor _sensors_control_open(); + private static native Bundle _sensors_control_open(); private static native boolean _sensors_control_activate(int sensor, boolean activate); private static native int _sensors_control_set_delay(int ms); private static native int _sensors_control_wake(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8b7260ba34c4..3e4d5f97f9b7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -97,6 +97,9 @@ class ServerThread extends Thread { // Critical services... try { + Log.i(TAG, "Starting Entropy Service."); + ServiceManager.addService("entropy", new EntropyService()); + Log.i(TAG, "Starting Power Manager."); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); @@ -228,6 +231,14 @@ class ServerThread extends Thread { } try { + Log.i(TAG, "Starting Accessibility Manager."); + ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, + new AccessibilityManagerService(context)); + } catch (Throwable e) { + Log.e(TAG, "Failure starting Accessibility Manager", e); + } + + try { Log.i(TAG, "Starting Notification Manager."); ServiceManager.addService(Context.NOTIFICATION_SERVICE, new NotificationManagerService(context, statusBar, hardware)); diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index fa5442197689..9f2856c694bb 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -39,48 +40,71 @@ import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.DefaultPhoneNotifier; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneStateIntentReceiver; import com.android.internal.telephony.TelephonyIntents; import com.android.server.am.BatteryStatsService; - /** - * Since phone process can be restarted, this class provides a centralized - * place that applications can register and be called back from. + * Since phone process can be restarted, this class provides a centralized place + * that applications can register and be called back from. */ class TelephonyRegistry extends ITelephonyRegistry.Stub { private static final String TAG = "TelephonyRegistry"; private static class Record { String pkgForDebug; + IBinder binder; + IPhoneStateListener callback; + int events; } private final Context mContext; + private final ArrayList<Record> mRecords = new ArrayList(); + private final IBatteryStats mBatteryStats; private int mCallState = TelephonyManager.CALL_STATE_IDLE; + private String mCallIncomingNumber = ""; + private ServiceState mServiceState = new ServiceState(); - private int mSignalStrength = -1; + + private SignalStrength mSignalStrength = new SignalStrength(); + private boolean mMessageWaiting = false; + private boolean mCallForwarding = false; + private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + private int mDataConnectionState = TelephonyManager.DATA_CONNECTED; + private boolean mDataConnectionPossible = false; + private String mDataConnectionReason = ""; + private String mDataConnectionApn = ""; + private String mDataConnectionInterfaceName = ""; + private Bundle mCellLocation = new Bundle(); - // we keep a copy of all of the sate so we can send it out when folks register for it + static final int PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | + PhoneStateListener.LISTEN_CALL_STATE | + PhoneStateListener.LISTEN_DATA_ACTIVITY | + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | + PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; + + // we keep a copy of all of the state so we can send it out when folks + // register for it // - // In these calls we call with the lock held. This is safe becasuse remote - // calls go through a oneway interface and local calls going through a handler before - // they get to app code. + // In these calls we call with the lock held. This is safe becasuse remote + // calls go through a oneway interface and local calls going through a + // handler before they get to app code. TelephonyRegistry(Context context) { CellLocation.getEmpty().fillInNotifierBundle(mCellLocation); @@ -90,14 +114,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { - //Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events)); + // Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + + // Integer.toHexString(events)); if (events != 0) { - // check permissions - if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - - } + /* Checks permission and throws Security exception */ + checkListenerPermission(events); synchronized (mRecords) { // register @@ -105,7 +126,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { find_and_add: { IBinder b = callback.asBinder(); final int N = mRecords.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { r = mRecords.get(i); if (b == r.binder) { break find_and_add; @@ -125,7 +146,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { - r.callback.onSignalStrengthChanged(mSignalStrength); + int gsmSignalStrength = mSignalStrength.getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } catch (RemoteException ex) { remove(r.binder); } @@ -168,6 +191,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + try { + r.callback.onSignalStrengthsChanged(mSignalStrength); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -177,8 +207,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void remove(IBinder binder) { synchronized (mRecords) { - final int N = mRecords.size(); - for (int i=0; i<N; i++) { + final int recordCount = mRecords.size(); + for (int i = 0; i < recordCount; i++) { if (mRecords.get(i).binder == binder) { mRecords.remove(i); return; @@ -188,14 +218,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallState(int state, String incomingNumber) { - if (!checkPhoneStatePermission("notifyCallState()")) { + if (!checkNotifyPermission("notifyCallState()")) { return; } synchronized (mRecords) { mCallState = state; mCallIncomingNumber = incomingNumber; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { @@ -210,13 +239,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyServiceState(ServiceState state) { - if (!checkPhoneStatePermission("notifyServiceState()")) { + if (!checkNotifyPermission("notifyServiceState()")){ return; - } + } synchronized (mRecords) { mServiceState = state; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { sendServiceState(r, state); @@ -226,35 +254,38 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastServiceStateChanged(state); } - public void notifySignalStrength(int signalStrengthASU) { - if (!checkPhoneStatePermission("notifySignalStrength()")) { + public void notifySignalStrength(SignalStrength signalStrength) { + if (!checkNotifyPermission("notifySignalStrength()")) { return; - } + } synchronized (mRecords) { - mSignalStrength = signalStrengthASU; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + mSignalStrength = signalStrength; + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); + if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + sendSignalStrength(r, signalStrength); + } if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { - r.callback.onSignalStrengthChanged(signalStrengthASU); + int gsmSignalStrength = signalStrength.getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } catch (RemoteException ex) { remove(r.binder); } } } } - broadcastSignalStrengthChanged(signalStrengthASU); + broadcastSignalStrengthChanged(signalStrength); } public void notifyMessageWaitingChanged(boolean mwi) { - if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) { + if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { return; - } + } synchronized (mRecords) { mMessageWaiting = mwi; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { @@ -268,13 +299,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallForwardingChanged(boolean cfi) { - if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) { + if (!checkNotifyPermission("notifyCallForwardingChanged()")) { return; - } + } synchronized (mRecords) { mCallForwarding = cfi; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { try { @@ -288,13 +318,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataActivity(int state) { - if (!checkPhoneStatePermission("notifyDataActivity()")) { + if (!checkNotifyPermission("notifyDataActivity()" )) { return; - } + } synchronized (mRecords) { mDataActivity = state; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { try { @@ -307,19 +336,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyDataConnection(int state, boolean isDataConnectivityPissible, + public void notifyDataConnection(int state, boolean isDataConnectivityPossible, String reason, String apn, String interfaceName) { - if (!checkPhoneStatePermission("notifyDataConnection()")) { + if (!checkNotifyPermission("notifyDataConnection()" )) { return; - } + } synchronized (mRecords) { mDataConnectionState = state; - mDataConnectionPossible = isDataConnectivityPissible; + mDataConnectionPossible = isDataConnectivityPossible; mDataConnectionReason = reason; mDataConnectionApn = apn; mDataConnectionInterfaceName = interfaceName; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { try { @@ -330,17 +358,17 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } } - broadcastDataConnectionStateChanged(state, isDataConnectivityPissible, - reason, apn, interfaceName); + broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, + interfaceName); } public void notifyDataConnectionFailed(String reason) { - if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) { + if (!checkNotifyPermission("notifyDataConnectionFailed()")) { return; - } + } /* * This is commented out because there is on onDataConnectionFailed callback - * on PhoneStateListener. There should be. + * on PhoneStateListener. There should be synchronized (mRecords) { mDataConnectionFailedReason = reason; final int N = mRecords.size(); @@ -356,13 +384,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCellLocation(Bundle cellLocation) { - if (!checkPhoneStatePermission("notifyCellLocation()")) { + if (!checkNotifyPermission("notifyCellLocation()")) { return; - } + } synchronized (mRecords) { mCellLocation = cellLocation; - final int N = mRecords.size(); - for (int i=N-1; i>=0; i--) { + for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { sendCellLocation(r, cellLocation); @@ -371,11 +398,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - // - // the new callback broadcasting - // - // copy the service state object so they can't mess it up in the local calls - // + /** + * Copy the service state object so they can't mess it up in the local calls + */ public void sendServiceState(Record r, ServiceState state) { try { r.callback.onServiceStateChanged(new ServiceState(state)); @@ -384,7 +409,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void sendCellLocation(Record r, Bundle cellLocation) { + private void sendCellLocation(Record r, Bundle cellLocation) { try { r.callback.onCellLocationChanged(new Bundle(cellLocation)); } catch (RemoteException ex) { @@ -392,18 +417,24 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + private void sendSignalStrength(Record r, SignalStrength signalStrength) { + try { + r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); + } catch (RemoteException ex) { + remove(r.binder); + } + } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump telephony.registry from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } synchronized (mRecords) { - final int N = mRecords.size(); + final int recordCount = mRecords.size(); pw.println("last known state:"); pw.println(" mCallState=" + mCallState); pw.println(" mCallIncomingNumber=" + mCallIncomingNumber); @@ -418,20 +449,28 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println(" mDataConnectionApn=" + mDataConnectionApn); pw.println(" mDataConnectionInterfaceName=" + mDataConnectionInterfaceName); pw.println(" mCellLocation=" + mCellLocation); - pw.println("registrations: count=" + N); - for (int i=0; i<N; i++) { + pw.println("registrations: count=" + recordCount); + for (int i = 0; i < recordCount; i++) { Record r = mRecords.get(i); pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events)); } } } - // // the legacy intent broadcasting // 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); @@ -439,17 +478,20 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcast(intent); } - private void broadcastSignalStrengthChanged(int asu) { + private void broadcastSignalStrengthChanged(SignalStrength signalStrength) { long ident = Binder.clearCallingIdentity(); try { - mBatteryStats.notePhoneSignalStrength(asu); + mBatteryStats.notePhoneSignalStrength(signalStrength); } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } - + Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); - intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu); + Bundle data = new Bundle(); + signalStrength.fillInNotifierBundle(data); + intent.putExtras(data); mContext.sendStickyBroadcast(intent); } @@ -462,13 +504,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mBatteryStats.notePhoneOn(); } } catch (RemoteException e) { + /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } - + Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - intent.putExtra(Phone.STATE_KEY, - DefaultPhoneNotifier.convertCallState(state).toString()); + intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); if (!TextUtils.isEmpty(incomingNumber)) { intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); } @@ -498,16 +540,28 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(Phone.FAILURE_REASON_KEY, reason); mContext.sendStickyBroadcast(intent); } - - private boolean checkPhoneStatePermission(String method) { + + private boolean checkNotifyPermission(String method) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { return true; } String msg = "Modify Phone State Permission Denial: " + method + " from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid(); + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); Log.w(TAG, msg); return false; } + + private void checkListenerPermission(int events) { + if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION, null); + + } + + if ((events & PHONE_STATE_PERMISSION_MASK) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PHONE_STATE, null); + } + } } diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java index 5532894b0f0d..d921bafe7101 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 fef35984ff5a..68bf4fbb0653 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/WifiService.java b/services/java/com/android/server/WifiService.java index 348f0a19c717..a940af385f29 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -38,6 +38,7 @@ import android.net.wifi.WifiNative; import android.net.wifi.WifiStateTracker; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.SupplicantState; import android.net.NetworkStateTracker; import android.net.DhcpInfo; import android.os.Binder; @@ -49,6 +50,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; import android.text.TextUtils; @@ -64,6 +66,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import com.android.internal.app.IBatteryStats; +import android.backup.IBackupManager; import com.android.server.am.BatteryStatsService; /** @@ -96,8 +99,8 @@ public class WifiService extends IWifiManager.Stub { private int mScanLocksAcquired; private int mScanLocksReleased; - private final List<WifiMulticaster> mMulticasters = - new ArrayList<WifiMulticaster>(); + private final List<Multicaster> mMulticasters = + new ArrayList<Multicaster>(); private int mMulticastEnabled; private int mMulticastDisabled; @@ -588,6 +591,12 @@ public class WifiService extends IWifiManager.Stub { } + private void enforceMulticastChangePermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, + "WifiService"); + } + /** * see {@link WifiManager#getWifiState()} * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -1054,6 +1063,94 @@ public class WifiService extends IWifiManager.Stub { break setVariables; } + if ((config.eap != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.eapVarName, + config.eap)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set eap: "+ + config.eap); + } + break setVariables; + } + + if ((config.identity != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.identityVarName, + config.identity)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set identity: "+ + config.identity); + } + break setVariables; + } + + if ((config.anonymousIdentity != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.anonymousIdentityVarName, + config.anonymousIdentity)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set anonymousIdentity: "+ + config.anonymousIdentity); + } + break setVariables; + } + + if ((config.password != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.passwordVarName, + config.password)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set password: "+ + config.password); + } + break setVariables; + } + + if ((config.clientCert != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.clientCertVarName, + config.clientCert)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set clientCert: "+ + config.clientCert); + } + break setVariables; + } + + if ((config.caCert != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.caCertVarName, + config.caCert)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set caCert: "+ + config.caCert); + } + break setVariables; + } + + if ((config.privateKey != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.privateKeyVarName, + config.privateKey)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set privateKey: "+ + config.privateKey); + } + break setVariables; + } + + if ((config.privateKeyPasswd != null) && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.privateKeyPasswdVarName, + config.privateKeyPasswd)) { + if (DBG) { + Log.d(TAG, config.SSID + ": failed to set privateKeyPasswd: "+ + config.privateKeyPasswd); + } + break setVariables; + } + return netId; } @@ -1353,6 +1450,16 @@ public class WifiService extends IWifiManager.Stub { } } } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (Exception e) { + // Try again later + } + } return result; } @@ -1449,10 +1556,12 @@ public class WifiService extends IWifiManager.Stub { Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); if (action.equals(Intent.ACTION_SCREEN_ON)) { + Log.d(TAG, "ACTION_SCREEN_ON"); mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + Log.d(TAG, "ACTION_SCREEN_OFF"); mScreenOff = true; /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off @@ -1461,12 +1570,21 @@ public class WifiService extends IWifiManager.Stub { * or plugged in to AC). */ if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { - long triggerTime = System.currentTimeMillis() + idleMillis; - mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + WifiInfo info = mWifiStateTracker.requestConnectionInfo(); + if (info.getSupplicantState() != SupplicantState.COMPLETED) { + // do not keep Wifi awake when screen is off if Wifi is not associated + mDeviceIdle = true; + updateWifiState(); + } else { + long triggerTime = System.currentTimeMillis() + idleMillis; + Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + } } /* we can return now -- there's nothing to do until we get the idle intent back */ return; } else if (action.equals(ACTION_DEVICE_IDLE)) { + Log.d(TAG, "got ACTION_DEVICE_IDLE"); mDeviceIdle = true; } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { /* @@ -1477,9 +1595,11 @@ public class WifiService extends IWifiManager.Stub { * the already-set timer. */ int pluggedType = intent.getIntExtra("plugged", 0); + Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType); if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) && !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) { long triggerTime = System.currentTimeMillis() + idleMillis; + Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms"); mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); mPluggedType = pluggedType; return; @@ -1732,7 +1852,7 @@ public class WifiService extends IWifiManager.Stub { } } - private class WifiLock extends WifiDeathRecipient { + private class WifiLock extends DeathRecipient { WifiLock(int lockMode, String tag, IBinder binder) { super(lockMode, tag, binder); } @@ -1780,7 +1900,9 @@ public class WifiService extends IWifiManager.Stub { private WifiLock removeLock(IBinder binder) { int index = findLockByBinder(binder); if (index >= 0) { - return mList.remove(index); + WifiLock ret = mList.remove(index); + ret.unlinkDeathRecipient(); + return ret; } else { return null; } @@ -1875,13 +1997,13 @@ public class WifiService extends IWifiManager.Stub { return hadLock; } - private abstract class WifiDeathRecipient + private abstract class DeathRecipient implements IBinder.DeathRecipient { String mTag; int mMode; IBinder mBinder; - WifiDeathRecipient(int mode, String tag, IBinder binder) { + DeathRecipient(int mode, String tag, IBinder binder) { super(); mTag = tag; mMode = mode; @@ -1892,15 +2014,19 @@ public class WifiService extends IWifiManager.Stub { binderDied(); } } + + void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } } - private class WifiMulticaster extends WifiDeathRecipient { - WifiMulticaster(String tag, IBinder binder) { + private class Multicaster extends DeathRecipient { + Multicaster(String tag, IBinder binder) { super(Binder.getCallingUid(), tag, binder); } public void binderDied() { - Log.e(TAG, "WifiMulticaster binderDied"); + Log.e(TAG, "Multicaster binderDied"); synchronized (mMulticasters) { int i = mMulticasters.indexOf(this); if (i != -1) { @@ -1910,7 +2036,7 @@ public class WifiService extends IWifiManager.Stub { } public String toString() { - return "WifiMulticaster{" + mTag + " binder=" + mBinder + "}"; + return "Multicaster{" + mTag + " binder=" + mBinder + "}"; } public int getUid() { @@ -1918,12 +2044,12 @@ public class WifiService extends IWifiManager.Stub { } } - public void enableWifiMulticast(IBinder binder, String tag) { - enforceChangePermission(); + public void acquireMulticastLock(IBinder binder, String tag) { + enforceMulticastChangePermission(); synchronized (mMulticasters) { mMulticastEnabled++; - mMulticasters.add(new WifiMulticaster(tag, binder)); + mMulticasters.add(new Multicaster(tag, binder)); // Note that we could call stopPacketFiltering only when // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each @@ -1941,15 +2067,15 @@ public class WifiService extends IWifiManager.Stub { } } - public void disableWifiMulticast() { - enforceChangePermission(); + public void releaseMulticastLock() { + enforceMulticastChangePermission(); int uid = Binder.getCallingUid(); synchronized (mMulticasters) { mMulticastDisabled++; int size = mMulticasters.size(); for (int i = size - 1; i >= 0; i--) { - WifiMulticaster m = mMulticasters.get(i); + Multicaster m = mMulticasters.get(i); if ((m != null) && (m.getUid() == uid)) { removeMulticasterLocked(i, uid); } @@ -1959,7 +2085,10 @@ public class WifiService extends IWifiManager.Stub { private void removeMulticasterLocked(int i, int uid) { - mMulticasters.remove(i); + Multicaster removed = mMulticasters.remove(i); + if (removed != null) { + removed.unlinkDeathRecipient(); + } if (mMulticasters.size() == 0) { WifiNative.startPacketFiltering(); } @@ -1973,7 +2102,7 @@ public class WifiService extends IWifiManager.Stub { } } - public boolean isWifiMulticastEnabled() { + public boolean isMulticastEnabled() { enforceAccessPermission(); synchronized (mMulticasters) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 3fa5bafc0101..2dd70efcec1b 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; @@ -133,16 +134,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo static final boolean DEBUG_STARTING_WINDOW = false; static final boolean DEBUG_REORDER = false; static final boolean SHOW_TRANSACTIONS = false; - + static final boolean PROFILE_ORIENTATION = false; static final boolean BLUR = true; static final boolean localLOGV = DEBUG; - + static final int LOG_WM_NO_SURFACE_MEMORY = 31000; - + /** How long to wait for first key repeat, in milliseconds */ static final int KEY_REPEAT_FIRST_DELAY = 750; - + /** How long to wait for subsequent key repeats, in milliseconds */ static final int KEY_REPEAT_DELAY = 50; @@ -150,16 +151,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * for multiple windows of the same type and Z-ordering adjustment * with TYPE_LAYER_OFFSET. */ static final int TYPE_LAYER_MULTIPLIER = 10000; - + /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above * or below others in the same layer. */ static final int TYPE_LAYER_OFFSET = 1000; - + /** How much to increment the layer for each window, to reserve room * for effect surfaces between them. */ static final int WINDOW_LAYER_MULTIPLIER = 5; - + /** The maximum length we will accept for a loaded animation duration: * this is 10 seconds. */ @@ -173,21 +174,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** Adjustment to time to perform a dim, to make it more dramatic. */ static final int DIM_DURATION_MULTIPLIER = 6; + + static final int INJECT_FAILED = 0; + static final int INJECT_SUCCEEDED = 1; + static final int INJECT_NO_PERMISSION = -1; static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; static final int UPDATE_FOCUS_PLACING_SURFACES = 2; static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; - + /** The minimum time between dispatching touch events. */ int mMinWaitTimeBetweenTouchEvents = 1000 / 35; // Last touch event time long mLastTouchEventTime = 0; - + // Last touch event type int mLastTouchEventType = OTHER_EVENT; - + // Time to wait before calling useractivity again. This saves CPU usage // when we get a flood of touch events. static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000; @@ -195,10 +200,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Last time we call user activity long mLastUserActivityCallTime = 0; - // Last time we updated battery stats + // Last time we updated battery stats long mLastBatteryStatsCallTime = 0; - + private static final String SYSTEM_SECURE = "ro.secure"; + private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; /** * Condition waited on by {@link #reenableKeyguard} to know the call to @@ -224,20 +230,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Context mContext; final boolean mHaveInputMethods; - + final boolean mLimitedAlphaCompositing; - + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); final IActivityManager mActivityManager; - + final IBatteryStats mBatteryStats; - + /** * All currently active sessions with clients. */ final HashSet<Session> mSessions = new HashSet<Session>(); - + /** * Mapping from an IWindow IBinder to the server's Window object. * This is also used as the lock for all of our state. @@ -255,7 +261,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * over them. */ final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>(); - + /** * Window tokens that are in the process of exiting, but still * on screen for animations. @@ -314,9 +320,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * list or contain windows that need to be force removed. */ ArrayList<WindowState> mForceRemoves; - + IInputMethodManager mInputMethodManager; - + SurfaceSession mFxSession; Surface mDimSurface; boolean mDimShown; @@ -326,9 +332,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long mLastDimAnimTime; Surface mBlurSurface; boolean mBlurShown; - + int mTransactionSequence = 0; - + final float[] mTmpFloats = new float[9]; boolean mSafeMode; @@ -340,7 +346,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int mLastRotationFlags; ArrayList<IRotationWatcher> mRotationWatchers = new ArrayList<IRotationWatcher>(); - + boolean mLayoutNeeded = true; boolean mAnimationPending = false; boolean mDisplayFrozen = false; @@ -352,7 +358,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // perform a rotation animation when turning off shows the lock screen which // changes the orientation. PowerManager.WakeLock mScreenFrozenLock; - + // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to // perform or TRANSIT_NONE if we are not waiting. If we are waiting, @@ -365,40 +371,40 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mSkipAppTransitionAnimation = false; final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>(); final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>(); - + //flag to detect fat touch events boolean mFatTouch = false; Display mDisplay; - + H mH = new H(); WindowState mCurrentFocus = null; WindowState mLastFocus = null; - + // This just indicates the window the input method is on top of, not // necessarily the window its input is going to. WindowState mInputMethodTarget = null; WindowState mUpcomingInputMethodTarget = null; boolean mInputMethodTargetWaitingAnim; int mInputMethodAnimLayerAdjustment; - + WindowState mInputMethodWindow = null; final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); AppWindowToken mFocusedApp = null; PowerManagerService mPowerManager; - + float mWindowAnimationScale = 1.0f; float mTransitionAnimationScale = 1.0f; - + final KeyWaiter mKeyWaiter = new KeyWaiter(); final KeyQ mQueue; final InputDispatcherThread mInputThread; // Who is holding the screen on. Session mHoldingScreenOn; - + /** * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). @@ -408,14 +414,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private ViewServer mViewServer; 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); thr.start(); - + synchronized (thr) { while (thr.mService == null) { try { @@ -424,17 +431,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + return thr.mService; } - + static class WMThread extends Thread { WindowManagerService mService; - + private final Context mContext; private final PowerManagerService mPM; private final boolean mHaveInputMethods; - + public WMThread(Context context, PowerManagerService pm, boolean haveInputMethods) { super("WindowManager"); @@ -442,19 +449,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mPM = pm; mHaveInputMethods = haveInputMethods; } - + public void run() { Looper.prepare(); WindowManagerService s = new WindowManagerService(mContext, mPM, mHaveInputMethods); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); - + synchronized (this) { mService = s; notifyAll(); } - + Looper.loop(); } } @@ -465,7 +472,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private final Context mContext; private final PowerManagerService mPM; boolean mRunning = false; - + public PolicyThread(WindowManagerPolicy policy, WindowManagerService service, Context context, PowerManagerService pm) { @@ -475,7 +482,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mContext = context; mPM = pm; } - + public void run() { Looper.prepare(); //Looper.myLooper().setMessageLogging(new LogPrinter( @@ -483,12 +490,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); mPolicy.init(mContext, mService, mPM); - + synchronized (this) { mRunning = true; notifyAll(); } - + Looper.loop(); } } @@ -499,7 +506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mHaveInputMethods = haveInputMethods; mLimitedAlphaCompositing = context.getResources().getBoolean( com.android.internal.R.bool.config_sf_limitedAlpha); - + mPowerManager = pm; mPowerManager.setPolicy(mPolicy); PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -515,14 +522,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(), Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); - + mQueue = new KeyQ(); mInputThread = new InputDispatcherThread(); - + PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); thr.start(); - + synchronized (thr) { while (!thr.mRunning) { try { @@ -531,9 +538,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + mInputThread.start(); - + // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); } @@ -586,12 +593,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return -1; } - + private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final ArrayList localmWindows = mWindows; - + final int N = localmWindows.size(); final WindowState attached = win.mAttachedWindow; int i; @@ -616,12 +623,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { int newIdx = findIdxBasedOnAppTokens(win); if(newIdx != -1) { - //there is a window above this one associated with the same - //apptoken note that the window could be a floating window - //that was created later or a window at the top of the list of + //there is a window above this one associated with the same + //apptoken note that the window could be a floating window + //that was created later or a window at the top of the list of //windows associated with this token. localmWindows.add(newIdx+1, win); - } + } } } } else { @@ -647,7 +654,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // we need to look some more. if (pos != null) { // Move behind any windows attached to this one. - WindowToken atoken = + WindowToken atoken = mTokenMap.get(((WindowState)pos).mClient.asBinder()); if (atoken != null) { final int NC = atoken.windows.size(); @@ -770,12 +777,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); } } - + static boolean canBeImeTarget(WindowState w) { final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); @@ -784,7 +791,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + int findDesiredInputMethodWindowIndexLocked(boolean willMove) { final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); @@ -793,12 +800,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo while (i > 0) { i--; w = (WindowState)localmWindows.get(i); - + //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x" // + Integer.toHexString(w.mAttrs.flags)); if (canBeImeTarget(w)) { //Log.i(TAG, "Putting input method here!"); - + // Yet more tricksyness! If this window is a "starting" // window, we do actually want to be on top of it, but // it is not -really- where input will go. So if the caller @@ -816,16 +823,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo break; } } - + mUpcomingInputMethodTarget = w; - + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target=" + w + " willMove=" + willMove); - + if (willMove && w != null) { final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.mAppToken != null) { - + // Now some fun for dealing with window animations that // modify the Z order. We need to look at all windows below // the current target that are in this app, finding the highest @@ -851,14 +858,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pos--; } } - + if (highestTarget != null) { - if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + mNextAppTransition + " " + highestTarget + " animating=" + highestTarget.isAnimating() + " layer=" + highestTarget.mAnimLayer + " new layer=" + w.mAnimLayer); - + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { // If we are currently setting up for an animation, // hold everything until we can find out what will happen. @@ -877,7 +884,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + //Log.i(TAG, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { @@ -904,7 +911,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return -1; } - + void addInputMethodWindowToListLocked(WindowState win) { int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { @@ -917,7 +924,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(pos); } - + void setInputMethodAnimLayerAdjustment(int adj) { if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj); mInputMethodAnimLayerAdjustment = adj; @@ -944,7 +951,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " anim layer: " + imw.mAnimLayer); } } - + private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { @@ -963,7 +970,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return interestingPos; } - + private void reAddWindowToListInOrderLocked(WindowState win) { addWindowToListInOrderLocked(win, false); // This is a hack to get all of the child windows added as well @@ -975,7 +982,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo reAddWindowLocked(wpos, win); } } - + void logWindowList(String prefix) { int N = mWindows.size(); while (N > 0) { @@ -983,10 +990,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); } } - + void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; - + final int N = dialogs.size(); if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { @@ -996,7 +1003,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Window list w/pos=" + pos); logWindowList(" "); } - + if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; if (pos < mWindows.size()) { @@ -1027,25 +1034,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) { final WindowState imWin = mInputMethodWindow; final int DN = mInputMethodDialogs.size(); if (imWin == null && DN == 0) { return false; } - + int imPos = findDesiredInputMethodWindowIndexLocked(true); if (imPos >= 0) { // In this case, the input method windows are to be placed // immediately above the window they are targeting. - + // First check to see if the input method windows are already // located here, and contiguous. final int N = mWindows.size(); WindowState firstImWin = imPos < N ? (WindowState)mWindows.get(imPos) : null; - + // Figure out the actual input method window that should be // at the bottom of their stack. WindowState baseImWin = imWin != null @@ -1054,7 +1061,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState cw = (WindowState)baseImWin.mChildWindows.get(0); if (cw.mSubLayer < 0) baseImWin = cw; } - + if (firstImWin == baseImWin) { // The windows haven't moved... but are they still contiguous? // First find the top IM window. @@ -1078,7 +1085,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } } - + if (imWin != null) { if (DEBUG_INPUT_METHOD) { Log.v(TAG, "Moving IM from " + imPos); @@ -1099,11 +1106,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { moveInputMethodDialogsLocked(imPos); } - + } else { // In this case, the input method windows go in a fixed layer, // because they aren't currently associated with a focus window. - + if (imWin != null) { if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); @@ -1117,20 +1124,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { moveInputMethodDialogsLocked(-1);; } - + } - + if (needAssignLayers) { assignLayersLocked(); } - + return true; } - + void adjustInputMethodDialogsLocked() { moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } - + public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets) { @@ -1138,11 +1145,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (res != WindowManagerImpl.ADD_OKAY) { return res; } - + boolean reportNewConfig = false; WindowState attachedWindow = null; WindowState win = null; - + synchronized(mWindowMap) { // Instantiating a Display requires talking with the simulator, // so don't do it until we know the system is mostly up and @@ -1153,14 +1160,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.setDisplay(mDisplay); reportNewConfig = true; } - + if (mWindowMap.containsKey(client.asBinder())) { Log.w(TAG, "Window " + client + " is already added"); return WindowManagerImpl.ADD_DUPLICATE_ADD; } if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { - attachedWindow = windowForClientLocked(null, attrs.token); + attachedWindow = windowForClientLocked(null, attrs.token); if (attachedWindow == null) { Log.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); @@ -1227,7 +1234,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mPolicy.adjustWindowParamsLw(win.mAttrs); - + res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; @@ -1236,9 +1243,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // From now on, no exceptions or errors allowed! res = WindowManagerImpl.ADD_OKAY; - + final long origId = Binder.clearCallingIdentity(); - + if (addToken) { mTokenMap.put(attrs.token, token); mTokenList.add(token); @@ -1252,7 +1259,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } boolean imMayMove = true; - + if (attrs.type == TYPE_INPUT_METHOD) { mInputMethodWindow = win; addInputMethodWindowToListLocked(win); @@ -1265,18 +1272,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { addWindowToListInOrderLocked(win, true); } - + win.mEnterAnimationPending = true; - + mPolicy.getContentInsetHintLw(attrs, outContentInsets); - + if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; } if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; } - + boolean focusChanged = false; if (win.canReceiveKeys()) { if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) @@ -1284,15 +1291,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo imMayMove = false; } } - + if (imMayMove) { - moveInputMethodWindowsIfNeededLocked(false); + moveInputMethodWindowsIfNeededLocked(false); } - + assignLayersLocked(); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. - + //dump(); if (focusChanged) { @@ -1300,7 +1307,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.handleNewWindowLocked(mCurrentFocus); } } - if (localLOGV) Log.v( TAG, "New client " + client.asBinder() + ": window=" + win); @@ -1317,16 +1323,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Update Orientation after adding a window, only if the window needs to be // displayed right away if (win.isVisibleOrAdding()) { - if (updateOrientationFromAppTokens(null, null) != null) { + if (updateOrientationFromAppTokensUnchecked(null, null) != null) { sendNewConfiguration(); } } } Binder.restoreCallingIdentity(origId); - + return res; } - + public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client); @@ -1336,7 +1342,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo removeWindowLocked(session, win); } } - + public void removeWindowLocked(Session session, WindowState win) { if (localLOGV || DEBUG_FOCUS) Log.v( @@ -1346,7 +1352,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", surface=" + win.mSurface); final long origId = Binder.clearCallingIdentity(); - + if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Remove " + win + ": mSurface=" + win.mSurface + " mExiting=" + win.mExiting @@ -1366,7 +1372,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // If we are not currently running the exit animation, we // need to see about starting one. if (wasVisible=win.isWinVisibleLw()) { - + int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -1403,17 +1409,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } - + private void removeWindowInnerLocked(Session session, WindowState win) { mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); - + win.mRemoved = true; - + if (mInputMethodTarget == win) { moveInputMethodWindowsIfNeededLocked(false); } - + mPolicy.removeWindowLw(win); win.removeLocked(); @@ -1425,7 +1431,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.remove(win); } - + final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; token.windows.remove(win); @@ -1462,7 +1468,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessage(m); } } - + if (!mInLayout) { assignLayersLocked(); mLayoutNeeded = true; @@ -1493,7 +1499,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void setInsetsWindow(Session session, IWindow client, - int touchableInsets, Rect contentInsets, + int touchableInsets, Rect contentInsets, Rect visibleInsets) { long origId = Binder.clearCallingIdentity(); try { @@ -1512,7 +1518,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void getWindowDisplayFrame(Session session, IWindow client, Rect outDisplayFrame) { synchronized(mWindowMap) { @@ -1534,7 +1540,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean inTouchMode; Configuration newConfig = null; long origId = Binder.clearCallingIdentity(); - + synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client); if (win == null) { @@ -1546,7 +1552,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); } - + int attrChanges = 0; int flagChanges = 0; if (attrs != null) { @@ -1578,11 +1584,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean imMayMove = (flagChanges&( WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0; - + boolean focusMayChange = win.mViewVisibility != viewVisibility || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) || (!win.mRelayoutCalled); - + win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; @@ -1670,17 +1676,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); } - + // updateFocusedWindowLocked() already assigned layers so we only need to // reassign them at this point if the IM window state gets shuffled boolean assignLayers = false; - + if (imMayMove) { if (moveInputMethodWindowsIfNeededLocked(false)) { assignLayers = true; } } - + mLayoutNeeded = true; win.mGivenInsetsPending = insetsPending; if (assignLayers) { @@ -1696,7 +1702,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo outVisibleInsets.set(win.mVisibleInsets); if (localLOGV) Log.v( TAG, "Relayout given client " + client.asBinder() - + ", requestedWidth=" + requestedWidth + + ", requestedWidth=" + requestedWidth + ", requestedHeight=" + requestedHeight + ", viewVisibility=" + viewVisibility + "\nRelayout returning frame=" + outFrame @@ -1711,9 +1717,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (newConfig != null) { sendNewConfiguration(); } - + Binder.restoreCallingIdentity(origId); - + return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); } @@ -1750,7 +1756,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return null; } - + private void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { @@ -1768,7 +1774,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // an animation of the same type, then just leave that one alone. return true; } - + // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation @@ -1833,7 +1839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return null; } - + private boolean applyAnimationLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, int transit, boolean enter) { // Only apply an animation if the display isn't frozen. If it is @@ -1932,7 +1938,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (Binder.getCallingPid() == Process.myPid()) { return true; } - + if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { return true; @@ -1944,7 +1950,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, msg); return false; } - + AppWindowToken findAppWindowToken(IBinder token) { WindowToken wtoken = mTokenMap.get(token); if (wtoken == null) { @@ -1952,13 +1958,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return wtoken.appWindowToken; } - + public void addWindowToken(IBinder token, int type) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addWindowToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { WindowToken wtoken = mTokenMap.get(token); if (wtoken != null) { @@ -1970,11 +1976,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mTokenList.add(wtoken); } } - + public void removeWindowToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeWindowToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -1985,17 +1991,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean delayed = false; if (!wtoken.hidden) { wtoken.hidden = true; - + final int N = wtoken.windows.size(); boolean changed = false; - + for (int i=0; i<N; i++) { WindowState win = wtoken.windows.get(i); if (win.isAnimating()) { delayed = true; } - + if (win.isVisibleNow()) { applyAnimationLocked(win, WindowManagerPolicy.TRANSIT_EXIT, false); @@ -2010,12 +2016,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo performLayoutAndPlaceSurfacesLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); } - + if (delayed) { mExitingTokens.add(wtoken); } } - + } else { Log.w(TAG, "Attempted to remove non-existing token: " + token); } @@ -2027,9 +2033,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int groupId, int requestedOrientation, boolean fullscreen) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken != null) { @@ -2044,19 +2050,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV) Log.v(TAG, "Adding new app token: " + wtoken); mTokenMap.put(token.asBinder(), wtoken); mTokenList.add(wtoken); - + // Application tokens start out hidden. wtoken.hidden = true; wtoken.hiddenRequested = true; - + //dump(); } } - + public void setAppGroupId(IBinder token, int groupId) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingIcon()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2068,7 +2074,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.groupId = groupId; } } - + public int getOrientationFromWindowsLocked() { int pos = mWindows.size() - 1; while (pos >= 0) { @@ -2092,7 +2098,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + public int getOrientationFromAppTokensLocked() { int pos = mAppTokens.size() - 1; int curGroup = 0; @@ -2134,7 +2140,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // to use the orientation behind it, then just take whatever // orientation it has and ignores whatever is under it. lastFullscreen = wtoken.appFullscreen; - if (lastFullscreen + if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { return or; } @@ -2151,11 +2157,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + public Configuration updateOrientationFromAppTokens( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "updateOrientationFromAppTokens()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + Configuration config; long ident = Binder.clearCallingIdentity(); + config = updateOrientationFromAppTokensUnchecked(currentConfig, + freezeThisOneIfNeeded); + Binder.restoreCallingIdentity(ident); + return config; + } + + Configuration updateOrientationFromAppTokensUnchecked( + Configuration currentConfig, IBinder freezeThisOneIfNeeded) { + Configuration config; synchronized(mWindowMap) { config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded); } @@ -2163,14 +2183,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } - Binder.restoreCallingIdentity(ident); return config; } - + /* * The orientation is computed from non-application windows first. If none of * the non-application windows specify orientation, the orientation is computed from - * application tokens. + * application tokens. * @see android.view.IWindowManager#updateOrientationFromAppTokens( * android.os.IBinder) */ @@ -2180,7 +2199,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long ident = Binder.clearCallingIdentity(); try { int req = computeForcedAppOrientationLocked(); - + if (req != mForcedAppOrientation) { changed = true; mForcedAppOrientation = req; @@ -2188,7 +2207,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //action like disabling/enabling sensors etc., mPolicy.setCurrentOrientationLw(req); } - + if (changed) { changed = setRotationUncheckedLocked( WindowManagerPolicy.USE_LAST_ROTATION, @@ -2220,10 +2239,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } finally { Binder.restoreCallingIdentity(ident); } - + return null; } - + int computeForcedAppOrientationLocked() { int req = getOrientationFromWindowsLocked(); if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { @@ -2231,39 +2250,39 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return req; } - + public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token); return; } - + wtoken.requestedOrientation = requestedOrientation; } } - + public int getAppOrientation(IApplicationToken token) { synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + return wtoken.requestedOrientation; } } - + public void setFocusedApp(IBinder token, boolean moveFocusNow) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setFocusedApp()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2296,9 +2315,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void prepareAppTransition(int transit) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "prepareAppTransition()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Prepare app transition: transit=" + transit @@ -2306,6 +2325,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mDisplayFrozen) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; + } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN + && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) { + // Opening a new task always supersedes a close for the anim. + mNextAppTransition = transit; + } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN + && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) { + // Opening a new activity always supersedes a close for the anim. + mNextAppTransition = transit; } mAppTransitionReady = false; mAppTransitionTimeout = false; @@ -2321,13 +2348,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public int getPendingAppTransition() { return mNextAppTransition; } - + public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - + synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition); @@ -2345,14 +2372,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo IBinder transferFrom, boolean createIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppStartingIcon()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Log.v( TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg + " transferFrom=" + transferFrom); - + AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null) { Log.w(TAG, "Attempted to set icon of non-existing app token: " + token); @@ -2365,11 +2392,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mDisplayFrozen) { return; } - + if (wtoken.startingData != null) { return; } - + if (transferFrom != null) { AppWindowToken ttoken = findAppWindowToken(transferFrom); if (ttoken != null) { @@ -2385,7 +2412,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo "Moving existing starting from " + ttoken + " to " + wtoken); final long origId = Binder.clearCallingIdentity(); - + // Transfer the starting window over to the new // token. wtoken.startingData = ttoken.startingData; @@ -2403,7 +2430,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ttoken.allAppWindows.remove(startingWindow); addWindowToListInOrderLocked(startingWindow, true); wtoken.allAppWindows.add(startingWindow); - + // Propagate other interesting state between the // tokens. If the old token is displayed, we should // immediately force the new one to be displayed. If @@ -2433,7 +2460,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.updateLayers(); ttoken.updateLayers(); } - + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); @@ -2463,7 +2490,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!createIfNeeded) { return; } - + mStartingIconInTransition = true; wtoken.startingData = new StartingData( pkg, theme, nonLocalizedLabel, @@ -2479,7 +2506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void setAppWillBeHidden(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppWillBeHidden()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; @@ -2493,7 +2520,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.willBeHidden = true; } } - + boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout) { boolean delayed = false; @@ -2502,7 +2529,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.clientHidden = !visible; wtoken.sendAppVisibilityToClients(); } - + wtoken.willBeHidden = false; if (wtoken.hidden == visible) { final int N = wtoken.allAppWindows.size(); @@ -2510,9 +2537,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); - + boolean runningAppAnimation = false; - + if (transit != WindowManagerPolicy.TRANSIT_NONE) { if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; @@ -2523,7 +2550,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo delayed = runningAppAnimation = true; } } - + for (int i=0; i<N; i++) { WindowState win = wtoken.allAppWindows.get(i); if (win == wtoken.startingWindow) { @@ -2533,7 +2560,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.isAnimating()) { delayed = true; } - + //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { @@ -2568,11 +2595,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo swin.mPolicyVisibilityAfterAnim = false; } } - + if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken + ": hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested); - + if (changed && performLayout) { mLayoutNeeded = true; updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); @@ -2583,14 +2610,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (wtoken.animation != null) { delayed = true; } - + return delayed; } public void setAppVisibility(IBinder token, boolean visible) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppVisibility()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; @@ -2610,7 +2637,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested, e); } - + // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { @@ -2619,7 +2646,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } wtoken.hiddenRequested = !visible; - + if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Setting dummy animation on: " + wtoken); wtoken.setDummyAnimation(); @@ -2631,7 +2658,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo wtoken.allDrawn = false; wtoken.startingDisplayed = false; wtoken.startingMoved = false; - + if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need @@ -2647,7 +2674,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return; } - + final long origId = Binder.clearCallingIdentity(); setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true); wtoken.updateReportedVisibilityLocked(); @@ -2688,7 +2715,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + public void startAppFreezingScreenLocked(AppWindowToken wtoken, int configChanges) { if (DEBUG_ORIENTATION) { @@ -2716,11 +2743,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + public void startAppFreezingScreen(IBinder token, int configChanges) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppFreezingScreen()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2728,7 +2755,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_ORIENTATION) Log.v(TAG, "Skipping set freeze of " + token); return; } - + AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null || wtoken.appToken == null) { Log.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken); @@ -2739,11 +2766,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void stopAppFreezingScreen(IBinder token, boolean force) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppFreezingScreen()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2758,11 +2785,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(origId); } } - + public void removeAppToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken = null; @@ -2807,7 +2834,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { Log.w(TAG, "Attempted to remove non-existing app token: " + token); } - + if (!delayed && wtoken != null) { wtoken.updateReportedVisibilityLocked(); } @@ -2841,13 +2868,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, " #" + i + ": " + mAppTokens.get(i).token); } } - + void dumpWindowsLocked() { for (int i=mWindows.size()-1; i>=0; i--) { Log.v(TAG, " #" + i + ": " + mWindows.get(i)); } } - + private int findWindowOffsetLocked(int tokenPos) { final int NW = mWindows.size(); @@ -2918,7 +2945,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return index; } - + private final int reAddAppWindowsLocked(int index, WindowToken token) { final int NW = token.windows.size(); for (int i=0; i<NW; i++) { @@ -2930,7 +2957,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppToken(int index, IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppToken()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { @@ -2945,7 +2972,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAppTokens.add(index, wtoken); if (DEBUG_REORDER) Log.v(TAG, "Moved " + token + " to " + index + ":"); if (DEBUG_REORDER) dumpAppTokensLocked(); - + final long origId = Binder.clearCallingIdentity(); if (DEBUG_REORDER) Log.v(TAG, "Removing windows in " + token + ":"); if (DEBUG_REORDER) dumpWindowsLocked(); @@ -3012,7 +3039,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppTokensToTop(List<IBinder> tokens) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppTokensToTop()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -3033,7 +3060,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void moveAppTokensToBottom(List<IBinder> tokens) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "moveAppTokensToBottom()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } final long origId = Binder.clearCallingIdentity(); @@ -3056,7 +3083,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // ------------------------------------------------------------- // Misc IWindowSession methods // ------------------------------------------------------------- - + public void disableKeyguard(IBinder token, String tag) { if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { @@ -3110,17 +3137,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public boolean inKeyguardRestrictedInputMode() { return mPolicy.inKeyguardRestrictedKeyInputMode(); } - + static float fixScale(float scale) { if (scale < 0) scale = 0; else if (scale > 20) scale = 20; return Math.abs(scale); } - + public void setAnimationScale(int which, float scale) { if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, "setAnimationScale()")) { - return; + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); } if (scale < 0) scale = 0; @@ -3130,15 +3157,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo case 0: mWindowAnimationScale = fixScale(scale); break; case 1: mTransitionAnimationScale = fixScale(scale); break; } - + // Persist setting mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget(); } - + public void setAnimationScales(float[] scales) { if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, "setAnimationScale()")) { - return; + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); } if (scales != null) { @@ -3149,11 +3176,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mTransitionAnimationScale = fixScale(scales[1]); } } - + // Persist setting mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget(); } - + public float getAnimationScale(int which) { switch (which) { case 0: return mWindowAnimationScale; @@ -3161,63 +3188,63 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return 0; } - + public float[] getAnimationScales() { return new float[] { mWindowAnimationScale, mTransitionAnimationScale }; } - + public int getSwitchState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getSwitchState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getSwitchState(sw); } - + public int getSwitchStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getSwitchStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getSwitchState(devid, sw); } - + public int getScancodeState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getScancodeState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getScancodeState(sw); } - + public int getScancodeStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getScancodeStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getScancodeState(devid, sw); } - + public int getKeycodeState(int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getKeycodeState()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getKeycodeState(sw); } - + public int getKeycodeStateForDevice(int devid, int sw) { if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE, "getKeycodeStateForDevice()")) { - return -1; + throw new SecurityException("Requires READ_INPUT_STATE permission"); } return KeyInputQueue.getKeycodeState(devid, sw); } - + public boolean hasKeys(int[] keycodes, boolean[] keyExists) { return KeyInputQueue.hasKeys(keycodes, keyExists); } - + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { @@ -3225,10 +3252,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mSystemBooted = true; } - + performEnableScreen(); } - + public void enableScreenIfNeededLocked() { if (mDisplayEnabled) { return; @@ -3238,7 +3265,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN)); } - + public void performEnableScreen() { synchronized(mWindowMap) { if (mDisplayEnabled) { @@ -3247,7 +3274,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mSystemBooted) { return; } - + // Don't enable the screen until all existing windows // have been drawn. final int N = mWindows.size(); @@ -3257,7 +3284,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } } - + mDisplayEnabled = true; if (false) { Log.i(TAG, "ENABLING SCREEN!"); @@ -3280,41 +3307,41 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.e(TAG, "Boot completed: SurfaceFlinger is dead!"); } } - + mPolicy.enableScreenAfterBoot(); - + // Make sure the last requested orientation has been applied. setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); } - + public void setInTouchMode(boolean mode) { synchronized(mWindowMap) { mInTouchMode = mode; } } - public void setRotation(int rotation, + public void setRotation(int rotation, boolean alwaysSendConfiguration, int animFlags) { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "setRotation()")) { - return; + throw new SecurityException("Requires SET_ORIENTATION permission"); } setRotationUnchecked(rotation, alwaysSendConfiguration, animFlags); } - + public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration, int animFlags) { if(DEBUG_ORIENTATION) Log.v(TAG, "alwaysSendConfiguration set to "+alwaysSendConfiguration); - + long origId = Binder.clearCallingIdentity(); boolean changed; synchronized(mWindowMap) { changed = setRotationUncheckedLocked(rotation, animFlags); } - + if (changed) { sendNewConfiguration(); synchronized(mWindowMap) { @@ -3325,10 +3352,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //update configuration ignoring orientation change sendNewConfiguration(); } - + Binder.restoreCallingIdentity(origId); } - + public boolean setRotationUncheckedLocked(int rotation, int animFlags) { boolean changed; if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) { @@ -3342,9 +3369,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mRotation, mDisplayEnabled); if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation); changed = mDisplayEnabled && mRotation != rotation; - + if (changed) { - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Log.v(TAG, "Rotation changed to " + rotation + " from " + mRotation + " (forceApp=" + mForcedAppOrientation @@ -3373,10 +3400,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } //end if changed - + return changed; } - + public int getRotation() { return mRotation; } @@ -3388,14 +3415,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (mWindowMap) { for (int i=0; i<mRotationWatchers.size(); i++) { if (watcherBinder == mRotationWatchers.get(i).asBinder()) { - mRotationWatchers.remove(i); + IRotationWatcher removed = mRotationWatchers.remove(i); + if (removed != null) { + removed.asBinder().unlinkToDeath(this, 0); + } i--; } } } } }; - + synchronized (mWindowMap) { try { watcher.asBinder().linkToDeath(dr, 0); @@ -3403,7 +3433,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { // Client died, no cleanup needed. } - + return mRotation; } } @@ -3419,7 +3449,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT */ public boolean startViewServer(int port) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3436,7 +3466,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo try { return mViewServer.start(); } catch (IOException e) { - Log.w(TAG, "View server did not start"); + Log.w(TAG, "View server did not start"); } } return false; @@ -3451,6 +3481,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } + private boolean isSystemSecure() { + return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) && + "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + } + /** * Stops the view server if it exists. * @@ -3460,7 +3495,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer */ public boolean stopViewServer() { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3482,7 +3517,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @see com.android.server.ViewServer */ public boolean isViewServerRunning() { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3503,7 +3538,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * @return False if an error occured, true otherwise. */ boolean viewServerListWindows(Socket client) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3570,7 +3605,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * not indicate whether the command itself was successful. */ boolean viewServerWindowCommand(Socket client, String command, String parameters) { - if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) { + if (isSystemSecure()) { return false; } @@ -3660,13 +3695,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { } } - + public Configuration computeNewConfiguration() { synchronized (mWindowMap) { return computeNewConfigurationLocked(); } } - + Configuration computeNewConfigurationLocked() { Configuration config = new Configuration(); if (!computeNewConfigurationLocked(config)) { @@ -3687,7 +3722,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return config; } - + boolean computeNewConfigurationLocked(Configuration config) { if (mDisplay == null) { return false; @@ -3702,12 +3737,46 @@ 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); return true; } - + // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- @@ -3771,15 +3840,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { + private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG, "dispatchPointer " + ev); Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, true, false); - + ev, true, false, pid, uid); + int action = ev.getAction(); - + if (action == MotionEvent.ACTION_UP) { // let go of our target mKeyWaiter.mMotionTarget = null; @@ -3801,20 +3870,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return true; + return INJECT_SUCCEEDED; } - + WindowState target = (WindowState)targetObj; - + final long eventTime = ev.getEventTime(); - + //Log.i(TAG, "Sending " + ev + " to " + target); if (uid != 0 && uid != target.mSession.mUid) { @@ -3828,11 +3897,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_NO_PERMISSION; } } - - if ((target.mAttrs.flags & + + if ((target.mAttrs.flags & WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { //target wants to ignore fat touch events boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev); @@ -3859,7 +3928,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if(mFatTouch) { //two cases here //an invalid down followed by 0 or moves(valid or invalid) - //a valid down, invalid move, more moves. want to ignore till up + //a valid down, invalid move, more moves. want to ignore till up returnFlag = true; } else if(cheekPress) { //valid down followed by invalid moves @@ -3878,7 +3947,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } } //end if target @@ -3944,7 +4013,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.bindTargetWindowLocked(target); } } - + // finally offset the event to the target's coordinate system and // dispatch the event. try { @@ -3952,7 +4021,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Delivering pointer " + qev + " to " + target); } target.mClient.dispatchPointer(ev, eventTime); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during motion dispatch: " + target); mKeyWaiter.mMotionTarget = null; @@ -3963,36 +4032,36 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - return false; + return INJECT_FAILED; } - + /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { + private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { if (DEBUG_INPUT) Log.v( TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">"); - + Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, false, false); + ev, false, false, pid, uid); if (focusObj == null) { Log.w(TAG, "No focus window, dropping trackball: " + ev); if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_FAILED; } if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); - return true; + return INJECT_SUCCEEDED; } - + WindowState focus = (WindowState)focusObj; - + if (uid != 0 && uid != focus.mSession.mUid) { if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) @@ -4004,12 +4073,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(qev); } ev.recycle(); - return false; + return INJECT_NO_PERMISSION; } } - + final long eventTime = ev.getEventTime(); - + synchronized(mWindowMap) { if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) { mKeyWaiter.bindTargetWindowLocked(focus, @@ -4021,10 +4090,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.bindTargetWindowLocked(focus); } } - + try { focus.mClient.dispatchTrackball(ev, eventTime); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { @@ -4034,28 +4103,28 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - - return false; + + return INJECT_FAILED; } - + /** * @return Returns true if event was dispatched, false if it was dropped for any reason */ - private boolean dispatchKey(KeyEvent event, int pid, int uid) { + private int dispatchKey(KeyEvent event, int pid, int uid) { if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event); Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null, - null, false, false); + null, false, false, pid, uid); if (focusObj == null) { Log.w(TAG, "No focus window, dropping: " + event); - return false; + return INJECT_FAILED; } if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { - return true; + return INJECT_SUCCEEDED; } - + WindowState focus = (WindowState)focusObj; - + if (DEBUG_INPUT) Log.v( TAG, "Dispatching to " + focus + ": " + event); @@ -4066,10 +4135,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Permission denied: injecting key event from pid " + pid + " uid " + uid + " to window " + focus + " owned by uid " + focus.mSession.mUid); - return false; + return INJECT_NO_PERMISSION; } } - + synchronized(mWindowMap) { mKeyWaiter.bindTargetWindowLocked(focus); } @@ -4077,14 +4146,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // NOSHIP extra state logging mKeyWaiter.recordDispatchState(event, focus); // END NOSHIP - + try { if (DEBUG_INPUT || DEBUG_FOCUS) { Log.v(TAG, "Delivering key " + event.getKeyCode() + " to " + focus); } focus.mClient.dispatchKey(event); - return true; + return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Log.i(TAG, "WINDOW DIED during key dispatch: " + focus); try { @@ -4094,14 +4163,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // removed. } } - - return false; + + return INJECT_FAILED; } - + public void pauseKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "pauseKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { @@ -4115,7 +4184,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void resumeKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "resumeKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { @@ -4129,18 +4198,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void setEventDispatching(boolean enabled) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "resumeKeyDispatching()")) { - return; + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized (mWindowMap) { mKeyWaiter.setEventDispatchingLocked(enabled); } } - + /** * Injects a keystroke event into the UI. - * - * @param ev A motion event describing the keystroke action. (Be sure to use + * + * @param ev A motion event describing the keystroke action. (Be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason @@ -4162,47 +4231,80 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState, deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM); - boolean result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchKey(newEvent, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); } - return result; + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } /** * Inject a pointer (touch) event into the UI. - * - * @param ev A motion event describing the pointer (touch) action. (As noted in - * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use + * + * @param ev A motion event describing the pointer (touch) action. (As noted in + * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason */ public boolean injectPointerEvent(MotionEvent ev, boolean sync) { - boolean result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchPointer(null, ev, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); + } + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; } - return result; + return false; } - + /** * Inject a trackball (navigation device) event into the UI. - * - * @param ev A motion event describing the trackball action. (As noted in - * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use + * + * @param ev A motion event describing the trackball action. (As noted in + * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. * @return Returns true if event was dispatched, false if it was dropped for any reason */ public boolean injectTrackballEvent(MotionEvent ev, boolean sync) { - boolean result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid()); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + final int result = dispatchTrackball(null, ev, pid, uid); if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true); + mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); } - return result; + Binder.restoreCallingIdentity(ident); + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENT permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } - + private WindowState getFocusedWindow() { synchronized (mWindowMap) { return getFocusedWindowLocked(); @@ -4212,7 +4314,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private WindowState getFocusedWindowLocked() { return mCurrentFocus; } - + /** * This class holds the state for dispatching key events. This state * is protected by the KeyWaiter instance, NOT by the window lock. You @@ -4234,7 +4336,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean wasFrozen; private boolean focusPaused; private WindowState curFocus; - + DispatchState(KeyEvent theEvent, WindowState theFocus) { focus = theFocus; event = theEvent; @@ -4256,7 +4358,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo focusPaused = theFocus.mToken.paused; } } - + public String toString() { return "{{" + event + " to " + focus + " @ " + time + " lw=" + lastWin + " lb=" + lastBinder @@ -4275,10 +4377,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static final int RETURN_NOTHING = 0; public static final int RETURN_PENDING_POINTER = 1; public static final int RETURN_PENDING_TRACKBALL = 2; - + final Object SKIP_TARGET_TOKEN = new Object(); final Object CONSUMED_EVENT_TOKEN = new Object(); - + private WindowState mLastWin = null; private IBinder mLastBinder = null; private boolean mFinished = true; @@ -4286,10 +4388,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean mEventDispatching = true; private long mTimeToSwitch = 0; /* package */ boolean mWasFrozen = false; - + // Target of Motion events WindowState mMotionTarget; - + // Windows above the target who would like to receive an "outside" // touch event for any down events outside of them. WindowState mOutsideTouchTargets; @@ -4301,7 +4403,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo */ Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev, MotionEvent nextMotion, boolean isPointerEvent, - boolean failIfTimeout) { + boolean failIfTimeout, int callingPid, int callingUid) { long startTime = SystemClock.uptimeMillis(); long keyDispatchingTimeout = 5 * 1000; long waitedFor = 0; @@ -4319,7 +4421,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ", mLastWin=" + mLastWin); if (targetIsNew) { Object target = findTargetWindow(nextKey, qev, nextMotion, - isPointerEvent); + isPointerEvent, callingPid, callingUid); if (target == SKIP_TARGET_TOKEN) { // The user has pressed a special key, and we are // dropping all pending events before it. @@ -4334,9 +4436,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } targetWin = (WindowState)target; } - + AppWindowToken targetApp = null; - + // Now: is it okay to send the next event to this window? synchronized (this) { // First: did we come here based on the last window not @@ -4345,7 +4447,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!targetIsNew && mLastWin == null) { continue; } - + // We never dispatch events if not finished with the // last one, or the display is frozen. if (mFinished && !mDisplayFrozen) { @@ -4364,7 +4466,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (targetIsNew && !targetWin.mToken.paused) { return targetWin; } - + // If we didn't find a target window, and there is no // focused app window, then just eat the events. } else if (mFocusedApp == null) { @@ -4374,7 +4476,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return null; } } - + if (DEBUG_INPUT) Log.v( TAG, "Waiting for last key in " + mLastBinder + " target=" + targetWin @@ -4385,10 +4487,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + (targetWin != null ? targetWin.mToken.paused : false) + " mFocusedApp=" + mFocusedApp + " mCurrentFocus=" + mCurrentFocus); - + targetApp = targetWin != null ? targetWin.mAppToken : mFocusedApp; - + long curTimeout = keyDispatchingTimeout; if (mTimeToSwitch != 0) { long now = SystemClock.uptimeMillis(); @@ -4404,7 +4506,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo curTimeout = switchTimeout; } } - + try { // after that continue // processing keys, so we don't get stuck. @@ -4468,7 +4570,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (this) { if (abort && (mLastWin == targetWin || targetWin == null)) { mFinished = true; - if (mLastWin != null) { + if (mLastWin != null) { if (DEBUG_INPUT) Log.v(TAG, "Window " + mLastWin + " timed out on key input"); @@ -4493,41 +4595,56 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev, - MotionEvent nextMotion, boolean isPointerEvent) { + MotionEvent nextMotion, boolean isPointerEvent, + int callingPid, int callingUid) { mOutsideTouchTargets = null; - + if (nextKey != null) { // Find the target window for a normal key event. final int keycode = nextKey.getKeyCode(); final int repeatCount = nextKey.getRepeatCount(); final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP; boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode); + if (!dispatch) { - mPolicy.interceptKeyTi(null, keycode, - nextKey.getMetaState(), down, repeatCount); + if (callingUid == 0 || + mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, + callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED) { + mPolicy.interceptKeyTi(null, keycode, + nextKey.getMetaState(), down, repeatCount); + } Log.w(TAG, "Event timeout during app switch: dropping " + nextKey); return SKIP_TARGET_TOKEN; } - + // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")"); - + WindowState focus = null; synchronized(mWindowMap) { focus = getFocusedWindowLocked(); } - + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - - if (mPolicy.interceptKeyTi(focus, - keycode, nextKey.getMetaState(), down, repeatCount)) { - return CONSUMED_EVENT_TOKEN; + + if (callingUid == 0 || + (focus != null && callingUid == focus.mSession.mUid) || + mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, + callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED) { + if (mPolicy.interceptKeyTi(focus, + keycode, nextKey.getMetaState(), down, repeatCount)) { + return CONSUMED_EVENT_TOKEN; + } } - + return focus; - + } else if (!isPointerEvent) { boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1); if (!dispatch) { @@ -4535,20 +4652,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + nextMotion); return SKIP_TARGET_TOKEN; } - + WindowState focus = null; synchronized(mWindowMap) { focus = getFocusedWindowLocked(); } - + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); return focus; } - + if (nextMotion == null) { return SKIP_TARGET_TOKEN; } - + boolean dispatch = mKeyWaiter.checkShouldDispatchKey( KeyEvent.KEYCODE_UNKNOWN); if (!dispatch) { @@ -4556,18 +4673,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + nextMotion); return SKIP_TARGET_TOKEN; } - + // Find the target window for a pointer event. int action = nextMotion.getAction(); final float xf = nextMotion.getX(); final float yf = nextMotion.getY(); final long eventTime = nextMotion.getEventTime(); - + final boolean screenWasOff = qev != null && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; - + WindowState target = null; - + synchronized(mWindowMap) { synchronized (this) { if (action == MotionEvent.ACTION_DOWN) { @@ -4580,12 +4697,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + mMotionTarget); mMotionTarget = null; } - + // ACTION_DOWN is special, because we need to lock next events to // the window we'll land onto. final int x = (int)xf; final int y = (int)yf; - + final ArrayList windows = mWindows; final int N = windows.size(); WindowState topErrWindow = null; @@ -4646,7 +4763,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + if ((flags & WindowManager.LayoutParams .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { child.mNextOutsideTouch = mOutsideTouchTargets; @@ -4663,18 +4780,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mMotionTarget = null; } } - + target = mMotionTarget; } } - + wakeupIfNeeded(target, eventType(nextMotion)); - + // Pointer events are a little different -- if there isn't a // target found for any event, then just drop it. return target != null ? target : SKIP_TARGET_TOKEN; } - + boolean checkShouldDispatchKey(int keycode) { synchronized (this) { if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) { @@ -4688,14 +4805,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return true; } } - + void bindTargetWindowLocked(WindowState win, int pendingWhat, QueuedEvent pendingMotion) { synchronized (this) { bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion); } } - + void bindTargetWindowLocked(WindowState win) { synchronized (this) { bindTargetWindowLockedLocked(win, RETURN_NOTHING, null); @@ -4713,7 +4830,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo releasePendingPointerLocked(s); s.mPendingPointerMove = pendingMotion; s.mPendingPointerWindow = win; - if (DEBUG_INPUT) Log.v(TAG, + if (DEBUG_INPUT) Log.v(TAG, "bindTargetToWindow " + s.mPendingPointerMove); } else if (pendingWhat == RETURN_PENDING_TRACKBALL) { releasePendingTrackballLocked(s); @@ -4722,7 +4839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void releasePendingPointerLocked(Session s) { if (DEBUG_INPUT) Log.v(TAG, "releasePendingPointer " + s.mPendingPointerMove); @@ -4731,14 +4848,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo s.mPendingPointerMove = null; } } - + void releasePendingTrackballLocked(Session s) { if (s.mPendingTrackballMove != null) { mQueue.recycleEvent(s.mPendingTrackballMove); s.mPendingTrackballMove = null; } } - + MotionEvent finishedKey(Session session, IWindow client, boolean force, int returnWhat) { if (DEBUG_INPUT) Log.v( @@ -4767,7 +4884,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo session.mPendingTrackballMove = null; session.mPendingTrackballWindow = null; } - + if (mLastBinder == client.asBinder()) { if (DEBUG_INPUT) Log.v( TAG, "finishedKey: last paused=" @@ -4783,7 +4900,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + if (qev != null) { MotionEvent res = (MotionEvent)qev.event; if (DEBUG_INPUT) Log.v(TAG, @@ -4803,7 +4920,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + void handleNewWindowLocked(WindowState newWindow) { if (!newWindow.canReceiveKeys()) { return; @@ -4904,7 +5021,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + void appSwitchComing() { synchronized (this) { // Don't wait for more than .5 seconds for app to finish @@ -4917,13 +5034,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo notifyAll(); } } - + private final void doFinishedKeyLocked(boolean doRecycle) { if (mLastWin != null) { releasePendingPointerLocked(mLastWin.mSession); releasePendingTrackballLocked(mLastWin.mSession); } - + if (mLastWin == null || !mLastWin.mToken.paused || !mLastWin.isVisibleLw()) { // If the current window has been paused, we aren't -really- @@ -4939,7 +5056,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private class KeyQ extends KeyInputQueue implements KeyInputQueue.FilterCallback { PowerManager.WakeLock mHoldingScreen; - + KeyQ() { super(mContext); PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); @@ -4953,7 +5070,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mPolicy.preprocessInputEventTq(event)) { return true; } - + switch (event.type) { case RawInputEvent.EV_KEY: { // XXX begin hack @@ -4973,11 +5090,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } // XXX end hack - + boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); int actions = mPolicy.interceptKeyTq(event, !screenIsOff); - + if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) { mPowerManager.goToSleep(event.when); } @@ -4992,7 +5109,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mPowerManager.userActivity(event.when, false, LocalPowerManager.BUTTON_EVENT, false); } - + if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) { if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) { filterQueue(this); @@ -5003,7 +5120,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return false; } } - + case RawInputEvent.EV_REL: { boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); @@ -5020,7 +5137,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + case RawInputEvent.EV_ABS: { boolean screenIsOff = !mPowerManager.screenIsOn(); boolean screenIsDim = !mPowerManager.screenIsBright(); @@ -5037,7 +5154,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + default: return true; } @@ -5057,7 +5174,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return FILTER_KEEP; } } - + /** * Must be called with the main window manager lock held. */ @@ -5078,11 +5195,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mSafeMode = mPolicy.detectSafeMode(); return mSafeMode; } - + public void systemReady() { mPolicy.systemReady(); } - + private final class InputDispatcherThread extends Thread { // Time to wait when there is nothing to do: 9999 seconds. static final int LONG_WAIT=9999*1000; @@ -5090,7 +5207,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public InputDispatcherThread() { super("InputDispatcher"); } - + @Override public void run() { while (true) { @@ -5101,11 +5218,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + private void process() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); - + // The last key event we saw KeyEvent lastKey = null; @@ -5113,12 +5230,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo long lastKeyTime = SystemClock.uptimeMillis(); long nextKeyTime = lastKeyTime+LONG_WAIT; - // How many successive repeats we generated + // How many successive repeats we generated int keyRepeatCount = 0; // Need to report that configuration has changed? boolean configChanged = false; - + while (true) { long curTime = SystemClock.uptimeMillis(); @@ -5199,14 +5316,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mQueue.recycleEvent(ev); break; } - + } else if (configChanged) { configChanged = false; sendNewConfiguration(); - + } else if (lastKey != null) { curTime = SystemClock.uptimeMillis(); - + // Timeout occurred while key was down. If it is at or // past the key repeat time, dispatch the repeat. if (DEBUG_INPUT) Log.v( @@ -5215,7 +5332,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (curTime < nextKeyTime) { continue; } - + lastKeyTime = nextKeyTime; nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY; keyRepeatCount++; @@ -5223,14 +5340,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Key repeat: count=" + keyRepeatCount + ", next @ " + nextKeyTime); dispatchKey(KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount), 0, 0); - + } else { curTime = SystemClock.uptimeMillis(); - + lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } - + } catch (Exception e) { Log.e(TAG, "Input thread received uncaught exception: " + e, e); @@ -5253,14 +5370,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo SurfaceSession mSurfaceSession; int mNumWindow = 0; boolean mClientDead = false; - + /** * Current pointer move event being dispatched to client window... must * hold key lock to access. */ QueuedEvent mPendingPointerMove; WindowState mPendingPointerWindow; - + /** * Current trackball move event being dispatched to client window... must * hold key lock to access. @@ -5280,7 +5397,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo sb.append(mUid); sb.append("}"); mStringName = sb.toString(); - + synchronized (mWindowMap) { if (mInputMethodManager == null && mHaveInputMethods) { IBinder b = ServiceManager.getService( @@ -5311,7 +5428,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(ident); } } - + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -5336,6 +5453,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { } synchronized(mWindowMap) { + mClient.asBinder().unlinkToDeath(this, 0); mClientDead = true; killSessionLocked(); } @@ -5345,11 +5463,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int viewVisibility, Rect outContentInsets) { return addWindow(this, window, attrs, viewVisibility, outContentInsets); } - + public void remove(IWindow window) { removeWindow(this, window); } - + public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, @@ -5358,21 +5476,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo requestedWidth, requestedHeight, viewFlags, insetsPending, outFrame, outContentInsets, outVisibleInsets, outSurface); } - + public void setTransparentRegion(IWindow window, Region region) { setTransparentRegionWindow(this, window, region); } - + public void setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets) { setInsetsWindow(this, window, touchableInsets, contentInsets, visibleInsets); } - + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { getWindowDisplayFrame(this, window, outDisplayFrame); } - + public void finishDrawing(IWindow window) { if (localLOGV) Log.v( TAG, "IWindow finishDrawing called for " + window); @@ -5392,7 +5510,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_PENDING_POINTER); } - + public MotionEvent getPendingTrackballMove(IWindow window) { if (localLOGV) Log.v( TAG, "IWindow getPendingMotionEvent called for " + window); @@ -5424,7 +5542,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void windowAddedLocked() { if (mSurfaceSession == null) { if (localLOGV) Log.v( @@ -5439,7 +5557,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mNumWindow--; killSessionLocked(); } - + void killSessionLocked() { if (mNumWindow <= 0 && mClientDead) { mSessions.remove(this); @@ -5458,7 +5576,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); pw.print(" mClientDead="); pw.print(mClientDead); @@ -5519,11 +5637,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mHaveFrame; WindowState mNextOutsideTouch; - + // Actual frame shown on-screen (may be modified by animation) final Rect mShownFrame = new Rect(); final Rect mLastShownFrame = new Rect(); - + /** * Insets that determine the actually visible area */ @@ -5543,19 +5661,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * given internal insets before laying out other windows based on it. */ boolean mGivenInsetsPending; - + /** * These are the content insets that were given during layout for * this window, to be applied to windows behind it. */ final Rect mGivenContentInsets = new Rect(); - + /** * These are the visible insets that were given during layout for * this window, to be applied to windows behind it. */ final Rect mGivenVisibleInsets = new Rect(); - + /** * Flag indicating whether the touchable region should be adjusted by * the visible insets; if false the area outside the visible insets is @@ -5563,7 +5681,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo * tests. */ int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; - + // Current transformation being applied. float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; @@ -5602,7 +5720,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // where we don't yet have a surface, but should have one soon, so // we can give the window focus before waiting for the relayout. boolean mRelayoutCalled; - + // This is set after the Surface has been created but before the // window has been drawn. During this time the surface is hidden. boolean mDrawPending; @@ -5617,7 +5735,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // to delay showing the surface until all windows in a token are ready // to be shown. boolean mReadyToShow; - + // Set when the window has been shown in the screen the first time. boolean mHasDrawn; @@ -5626,17 +5744,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Currently on the mDestroySurface list? boolean mDestroying; - + // Completely remove from window manager after exit animation? boolean mRemoveOnExit; // Set when the orientation is changing and this window has not yet // been updated for the new orientation. boolean mOrientationChanging; - + // Is this window now (or just being) removed? boolean mRemoved; - + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -5662,7 +5780,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return; } mDeathRecipient = deathRecipient; - + if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple @@ -5738,7 +5856,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth; h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight; } - + final Rect container = mContainingFrame; container.set(pf); @@ -5747,12 +5865,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Rect content = mContentFrame; content.set(cf); - + final Rect visible = mVisibleFrame; visible.set(vf); - + final Rect frame = mFrame; - + //System.out.println("In: w=" + w + " h=" + h + " container=" + // container + " x=" + mAttrs.x + " y=" + mAttrs.y); @@ -5764,7 +5882,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Now make sure the window fits in the overall display. Gravity.applyDisplay(mAttrs.gravity, df, frame); - + // Make sure the content and visible frames are inside of the // final window frame. if (content.left < frame.left) content.left = frame.left; @@ -5775,19 +5893,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (visible.top < frame.top) visible.top = frame.top; if (visible.right > frame.right) visible.right = frame.right; if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; - + final Rect contentInsets = mContentInsets; contentInsets.left = content.left-frame.left; contentInsets.top = content.top-frame.top; contentInsets.right = frame.right-content.right; contentInsets.bottom = frame.bottom-content.bottom; - + final Rect visibleInsets = mVisibleInsets; visibleInsets.left = visible.left-frame.left; visibleInsets.top = visible.top-frame.top; visibleInsets.right = frame.right-visible.right; visibleInsets.bottom = frame.bottom-visible.bottom; - + if (localLOGV) { //if ("com.google.android.youtube".equals(mAttrs.packageName) // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { @@ -5800,7 +5918,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //} } } - + public Rect getFrameLw() { return mFrame; } @@ -5828,11 +5946,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public Rect getGivenContentInsetsLw() { return mGivenContentInsets; } - + public Rect getGivenVisibleInsetsLw() { return mGivenVisibleInsets; } - + public WindowManager.LayoutParams getAttrs() { return mAttrs; } @@ -5840,7 +5958,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public int getSurfaceLayer() { return mLayer; } - + public IApplicationToken getAppToken() { return mAppToken != null ? mAppToken.appToken : null; } @@ -5874,7 +5992,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; } } - + Surface createSurfaceLocked() { if (mSurface == null) { mDrawPending = true; @@ -5914,7 +6032,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo try { mSurface = new Surface( - mSession.mSurfaceSession, mSession.mPid, + mSession.mSurfaceSession, mSession.mPid, 0, w, h, mAttrs.format, flags); } catch (Surface.OutOfResourcesException e) { Log.w(TAG, "OutOfResourcesException creating surface"); @@ -5924,7 +6042,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.e(TAG, "Exception creating surface", e); return null; } - + if (localLOGV) Log.v( TAG, "Got surface: " + mSurface + ", set left=" + mFrame.left + " top=" + mFrame.top @@ -5961,7 +6079,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return mSurface; } - + void destroySurfaceLocked() { // Window is no longer on-screen, so can no longer receive // key events... if we were waiting for it to finish @@ -5974,7 +6092,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mAppToken != null && this == mAppToken.startingWindow) { mAppToken.startingDisplayed = false; } - + if (localLOGV) Log.v( TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession); @@ -6064,7 +6182,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo enableScreenIfNeededLocked(); applyEnterAnimationLocked(this); - + int i = mChildWindows.size(); while (i > 0) { i--; @@ -6074,7 +6192,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo c.performShowLocked(); } } - + if (mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) { mAppToken.firstWindowDrawn = true; @@ -6090,13 +6208,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return true; } - + // This must be called while inside a transaction. Returns true if // there is more animation to run. boolean stepAnimationLocked(long currentTime, int dw, int dh) { if (!mDisplayFrozen) { // We will run animations as long as the display isn't frozen. - + if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; mHasLocalTransformation = true; @@ -6154,7 +6272,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mLocalAnimating = true; mAnimation = null; } - + if (!mAnimating && !mLocalAnimating) { return false; } @@ -6163,7 +6281,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Animation done in " + this + ": exiting=" + mExiting + ", reportedVisible=" + (mAppToken != null ? mAppToken.reportedVisible : false)); - + mAnimating = false; mLocalAnimating = false; mAnimation = null; @@ -6187,7 +6305,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mFinishedStarting.add(mAppToken); mH.sendEmptyMessage(H.FINISHED_STARTING); } - + finishExit(); if (mAppToken != null) { @@ -6203,16 +6321,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ": exiting=" + mExiting + " remove=" + mRemoveOnExit + " windowAnimating=" + isWindowAnimating()); - + final int N = mChildWindows.size(); for (int i=0; i<N; i++) { ((WindowState)mChildWindows.get(i)).finishExit(); } - + if (!mExiting) { return; } - + if (isWindowAnimating()) { return; } @@ -6239,7 +6357,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mRemoveOnExit = false; } } - + boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { if (dsdx < .99999f || dsdx > 1.00001f) return false; if (dtdy < .99999f || dtdy > 1.00001f) return false; @@ -6247,7 +6365,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (dsdy < -.000001f || dsdy > .000001f) return false; return true; } - + void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = @@ -6258,7 +6376,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ? mAppToken.transformation : null; if (selfTransformation || attachedTransformation != null || appTransformation != null) { - // cache often used attributes locally + // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; final Matrix tmpMatrix = mTmpMatrix; @@ -6280,7 +6398,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Here we must not transform the position of the surface // since it is already included in the transformation. //Log.i(TAG, "Transform: " + matrix); - + tmpMatrix.getValues(tmpFloats); mDsDx = tmpFloats[Matrix.MSCALE_X]; mDtDx = tmpFloats[Matrix.MSKEW_X]; @@ -6315,14 +6433,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { //Log.i(TAG, "Not applying alpha transform"); } - + if (localLOGV) Log.v( TAG, "Continuing animation in " + this + ": " + mShownFrame + ", alpha=" + mTransformation.getAlpha()); return; } - + mShownFrame.set(mFrame); mShownAlpha = mAlpha; mDsDx = 1; @@ -6330,7 +6448,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDsDy = 0; mDtDy = 1; } - + /** * Is this window visible? It is not visible if there is no * surface, or we are in the process of running an exit animation @@ -6393,7 +6511,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo && (!mAttachedHidden || mAnimating); } } - + /** * Like isOnScreen(), but we don't return true if the window is part * of a transition that has not yet been started. @@ -6412,7 +6530,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final AppWindowToken atoken = mAppToken; return mAnimation != null || (attached != null && attached.mAnimation != null) - || (atoken != null && + || (atoken != null && (atoken.animation != null || atoken.inPendingTransaction)); } @@ -6454,7 +6572,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + boolean isFullscreenOpaque(int screenWidth, int screenHeight) { if (mAttrs.format != PixelFormat.OPAQUE || mSurface == null || mAnimation != null || mDrawPending || mCommitDrawPending) { @@ -6546,7 +6664,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo void dump(PrintWriter pw, String prefix) { StringBuilder sb = new StringBuilder(64); - + pw.print(prefix); pw.print("mSession="); pw.print(mSession); pw.print(" mClient="); pw.println(mClient.asBinder()); pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); @@ -6662,7 +6780,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}"; } } - + // ------------------------------------------------------------- // Window Token State // ------------------------------------------------------------- @@ -6673,17 +6791,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; - + // Set if this token was explicitly added by a client, so should // not be removed when all windows are removed. final boolean explicit; - + // For printing. String stringName; - + // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; - + // All of the windows associated with this token. final ArrayList<WindowState> windows = new ArrayList<WindowState>(); @@ -6734,7 +6852,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int groupId = -1; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - + // These are used for determining when all windows associated with // an activity have been drawn, so they can be made visible together // at the same time. @@ -6743,20 +6861,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int numDrawnWindows; boolean inPendingTransaction; boolean allDrawn; - + // Is this token going to be hidden in a little while? If so, it // won't be taken into account for setting the screen orientation. boolean willBeHidden; - + // Is this window's surface needed? This is almost like hidden, except // it will sometimes be true a little earlier: when the token has // been shown, but is still waiting for its app transition to execute // before making its windows shown. boolean hiddenRequested; - + // Have we told the window clients to hide themselves? boolean clientHidden; - + // Last visibility state we reported to the app token. boolean reportedVisible; @@ -6765,16 +6883,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Have we been asked to have this token keep the screen frozen? boolean freezingScreen; - + boolean animating; Animation animation; boolean hasTransformation; final Transformation transformation = new Transformation(); - + // Offset to the window of all layers in the token, for use by // AppWindowToken animations. int animLayerAdjustment; - + // Information about an application starting window if displayed. StartingData startingData; WindowState startingWindow; @@ -6789,7 +6907,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo appWindowToken = this; appToken = _token; } - + public void setAnimation(Animation anim) { if (localLOGV) Log.v( TAG, "Setting animation in " + this + ": " + anim); @@ -6804,13 +6922,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else if (zorder == Animation.ZORDER_BOTTOM) { adj = -TYPE_LAYER_OFFSET; } - + if (animLayerAdjustment != adj) { animLayerAdjustment = adj; updateLayers(); } } - + public void setDummyAnimation() { if (animation == null) { if (localLOGV) Log.v( @@ -6825,7 +6943,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo animating = true; } } - + void updateLayers() { final int N = allAppWindows.size(); final int adj = animLayerAdjustment; @@ -6839,7 +6957,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void sendAppVisibilityToClients() { final int N = allAppWindows.size(); for (int i=0; i<N; i++) { @@ -6856,7 +6974,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + void showAllWindowsLocked() { final int NW = allAppWindows.size(); for (int i=0; i<NW; i++) { @@ -6866,12 +6984,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.performShowLocked(); } } - + // This must be called while inside a transaction. boolean stepAnimationLocked(long currentTime, int dw, int dh) { if (!mDisplayFrozen) { // We will run animations as long as the display isn't frozen. - + if (animation == sDummyAnimation) { // This guy is going to animate, but not yet. For now count // it is not animating for purposes of scheduling transactions; @@ -6879,7 +6997,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // a real animation and the next call will execute normally. return false; } - + if ((allDrawn || animating || startingDisplayed) && animation != null) { if (!animating) { if (DEBUG_ANIM) Log.v( @@ -6915,7 +7033,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } hasTransformation = false; - + if (!animating) { return false; } @@ -6925,7 +7043,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { moveInputMethodWindowsIfNeededLocked(true); } - + if (DEBUG_ANIM) Log.v( TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible); @@ -6935,13 +7053,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo animLayerAdjustment = 0; updateLayers(); } - + final int N = windows.size(); for (int i=0; i<N; i++) { ((WindowState)windows.get(i)).finishExit(); } updateReportedVisibilityLocked(); - + return false; } @@ -6949,11 +7067,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (appToken == null) { return; } - + int numInteresting = 0; int numVisible = 0; boolean nowGone = true; - + if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this); final int N = allAppWindows.size(); for (int i=0; i<N; i++) { @@ -6987,7 +7105,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo nowGone = false; } } - + boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); @@ -7004,7 +7122,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessage(m); } } - + void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); if (appToken != null) { @@ -7069,7 +7187,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return stringName; } } - + public static WindowManager.LayoutParams findAnimations( ArrayList<AppWindowToken> order, ArrayList<AppWindowToken> tokenList1, @@ -7077,7 +7195,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // We need to figure out which animation to use... WindowManager.LayoutParams animParams = null; int animSrc = 0; - + //Log.i(TAG, "Looking for animations..."); for (int i=order.size()-1; i>=0; i--) { AppWindowToken wtoken = order.get(i); @@ -7106,10 +7224,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + return animParams; } - + // ------------------------------------------------------------- // DummyAnimation // ------------------------------------------------------------- @@ -7123,7 +7241,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } static final Animation sDummyAnimation = new DummyAnimation(); - + // ------------------------------------------------------------- // Async Handler // ------------------------------------------------------------- @@ -7134,7 +7252,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final CharSequence nonLocalizedLabel; final int labelRes; final int icon; - + StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, int _labelRes, int _icon) { pkg = _pkg; @@ -7161,19 +7279,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public static final int ENABLE_SCREEN = 16; public static final int APP_FREEZE_TIMEOUT = 17; public static final int COMPUTE_AND_SEND_NEW_CONFIGURATION = 18; - + private Session mLastReportedHold; - + public H() { } - + @Override public void handleMessage(Message msg) { switch (msg.what) { case REPORT_FOCUS_CHANGE: { WindowState lastFocus; WindowState newFocus; - + synchronized(mWindowMap) { lastFocus = mLastFocus; newFocus = mCurrentFocus; @@ -7217,7 +7335,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo case REPORT_LOSING_FOCUS: { ArrayList<WindowState> losers; - + synchronized(mWindowMap) { losers = mLosingFocus; mLosingFocus = new ArrayList<WindowState>(); @@ -7249,10 +7367,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Animation has been canceled... do nothing. return; } - + if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Add starting " + wtoken + ": pkg=" + sd.pkg); - + View view = null; try { view = mPolicy.addStartingWindow( @@ -7379,7 +7497,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException ex) { } } break; - + case WINDOW_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Log.w(TAG, "Window freeze timeout expired."); @@ -7396,7 +7514,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case HOLD_SCREEN_CHANGED: { Session oldHold; Session newHold; @@ -7405,7 +7523,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo newHold = (Session)msg.obj; mLastReportedHold = newHold; } - + if (oldHold != newHold) { try { if (oldHold != null) { @@ -7423,7 +7541,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { @@ -7436,7 +7554,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case PERSIST_ANIMATION_SCALE: { Settings.System.putFloat(mContext.getContentResolver(), Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); @@ -7444,7 +7562,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); break; } - + case FORCE_GC: { synchronized(mWindowMap) { if (mAnimationPending) { @@ -7464,12 +7582,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Runtime.getRuntime().gc(); break; } - + case ENABLE_SCREEN: { performEnableScreen(); break; } - + case APP_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Log.w(TAG, "App freeze timeout expired."); @@ -7485,14 +7603,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } break; } - + case COMPUTE_AND_SEND_NEW_CONFIGURATION: { - if (updateOrientationFromAppTokens(null, null) != null) { + if (updateOrientationFromAppTokensUnchecked(null, null) != null) { sendNewConfiguration(); } break; } - + } } } @@ -7526,7 +7644,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return false; } - + // ------------------------------------------------------------- // Internals // ------------------------------------------------------------- @@ -7534,7 +7652,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final WindowState windowForClientLocked(Session session, IWindow client) { return windowForClientLocked(session, client.asBinder()); } - + final WindowState windowForClientLocked(Session session, IBinder client) { WindowState win = mWindowMap.get(client); if (localLOGV) Log.v( @@ -7559,7 +7677,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int curBaseLayer = 0; int curLayer = 0; int i; - + for (i=0; i<N; i++) { WindowState w = (WindowState)mWindows.get(i); if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) { @@ -7615,11 +7733,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + mInLayout = true; try { performLayoutAndPlaceSurfacesLockedInner(recoveringMemory); - + int i = mPendingRemove.size()-1; if (i >= 0) { while (i >= 0) { @@ -7655,7 +7773,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int i; // FIRST LOOP: Perform a layout, if needed. - + while (mLayoutNeeded) { mPolicy.beginLayoutLw(dw, dh); @@ -7690,7 +7808,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + // Now perform layout of attached windows, which usually // depend on the position of the window they are attached to. // XXX does not deal with windows that are attached to windows @@ -7721,7 +7839,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + private final void performLayoutAndPlaceSurfacesLockedInner( boolean recoveringMemory) { final long currentTime = SystemClock.uptimeMillis(); @@ -7732,13 +7850,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int i; // FIRST LOOP: Perform a layout, if needed. - performLayoutLockedInner(); - + if (mFxSession == null) { mFxSession = new SurfaceSession(); } - + if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION"); // Initialize state of exiting tokens. @@ -7752,7 +7869,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } // SECOND LOOP: Execute animations and update visibility of windows. - boolean orientationChangeComplete = true; Session holdScreen = null; float screenBrightness = -1; @@ -8063,7 +8179,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo !w.mLastContentInsets.equals(w.mContentInsets); w.mVisibleInsetsChanged = !w.mLastVisibleInsets.equals(w.mVisibleInsets); - if (!w.mLastFrame.equals(w.mFrame) + if (!w.mLastFrame.equals(w.mFrame) || w.mContentInsetsChanged || w.mVisibleInsetsChanged) { w.mLastFrame.set(w.mFrame); @@ -8085,7 +8201,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.mAppToken.allDrawn = false; } } - if (DEBUG_ORIENTATION) Log.v(TAG, + if (DEBUG_ORIENTATION) Log.v(TAG, "Resizing window " + w + " to " + w.mFrame); mResizingWindows.add(w); } else if (w.mOrientationChanging) { @@ -8275,7 +8391,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + ": CREATE"); try { - mDimSurface = new Surface(mFxSession, 0, + mDimSurface = new Surface(mFxSession, 0, -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); @@ -8330,7 +8446,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + ": CREATE"); try { - mBlurSurface = new Surface(mFxSession, 0, + mBlurSurface = new Surface(mFxSession, 0, -1, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_BLUR); @@ -8384,7 +8500,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { more = false; } - + // Do we need to continue animating? if (more) { if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " @@ -8410,7 +8526,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (!blurring && mBlurShown) { if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + ": HIDE"); @@ -8428,7 +8544,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } Surface.closeTransaction(); - + if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG, "With display frozen, orientationChangeComplete=" + orientationChangeComplete); @@ -8441,7 +8557,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo stopFreezingDisplayLocked(); } } - + i = mResizingWindows.size(); if (i > 0) { do { @@ -8459,7 +8575,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } while (i > 0); mResizingWindows.clear(); } - + // Destroy the surface of any windows that are no longer visible. i = mDestroySurface.size(); if (i > 0) { @@ -8518,13 +8634,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay); } } - + /** * Have the surface flinger show a surface, robustly dealing with * error conditions. In particular, if there is not enough memory * to show the surface, then we will try to get rid of other surfaces * in order to succeed. - * + * * @return Returns true if the surface was successfully shown. */ boolean showSurfaceRobustlyLocked(WindowState win) { @@ -8536,22 +8652,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RuntimeException e) { Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win); } - + reclaimSomeSurfaceMemoryLocked(win, "show"); - + return false; } - + void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) { final Surface surface = win.mSurface; - + EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(), win.mSession.mPid, operation); - + if (mForceRemoves == null) { mForceRemoves = new ArrayList<WindowState>(); } - + long callingIdentity = Binder.clearCallingIdentity(); try { // There was some problem... first, do a sanity check of the @@ -8585,7 +8701,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + boolean killedApps = false; if (!leakedSurface) { Log.w(TAG, "No leaked surfaces; killing applicatons!"); @@ -8609,7 +8725,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } - + if (leakedSurface || killedApps) { // We managed to reclaim some memory, so get rid of the trouble // surface and ask the app to request another one. @@ -8618,7 +8734,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo surface.clear(); win.mSurface = null; } - + try { win.mClient.dispatchGetNewSurface(); } catch (RemoteException e) { @@ -8628,7 +8744,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Binder.restoreCallingIdentity(callingIdentity); } } - + private boolean updateFocusedWindowLocked(int mode) { WindowState newFocus = computeFocusedWindowLocked(); if (mCurrentFocus != newFocus) { @@ -8641,7 +8757,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); - + final WindowState imWindow = mInputMethodWindow; if (newFocus != imWindow && oldFocus != imWindow) { if (moveInputMethodWindowsIfNeededLocked( @@ -8657,7 +8773,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo assignLayersLocked(); } } - + if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) { mKeyWaiter.handleNewWindowLocked(newFocus); } @@ -8685,13 +8801,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", canReceive=" + win.canReceiveKeys()); AppWindowToken thisApp = win.mAppToken; - + // If this window's application has been removed, just skip it. if (thisApp != null && thisApp.removed) { i--; continue; } - + // If there is a focused app, don't allow focus to go to any // windows below it. If this is an application window, step // through the app tokens until we find its app. @@ -8748,9 +8864,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } return; } - + mScreenFrozenLock.acquire(); - + long now = SystemClock.uptimeMillis(); //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now); if (mFreezeGcPending != 0) { @@ -8763,32 +8879,32 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { mFreezeGcPending = now; } - + mDisplayFrozen = true; if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; mAppTransitionReady = true; } - + if (PROFILE_ORIENTATION) { File file = new File("/data/system/frozen"); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } Surface.freezeDisplay(0); } - + private void stopFreezingDisplayLocked() { if (!mDisplayFrozen) { return; } - + mDisplayFrozen = false; mH.removeMessages(H.APP_FREEZE_TIMEOUT); if (PROFILE_ORIENTATION) { Debug.stopMethodTracing(); } Surface.unfreezeDisplay(0); - + // Reset the key delivery timeout on unfreeze, too. We force a wakeup here // too because regular key delivery processing should resume immediately. synchronized (mKeyWaiter) { @@ -8804,10 +8920,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mH.removeMessages(H.FORCE_GC); mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC), 2000); - + mScreenFrozenLock.release(); } - + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission("android.permission.DUMP") @@ -8817,7 +8933,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ", uid=" + Binder.getCallingUid()); return; } - + synchronized(mWindowMap) { pw.println("Current Window Manager state:"); for (int i=mWindows.size()-1; i>=0; i--) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9471eff95f3f..2fe4dd4c3109 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -17,7 +17,7 @@ package com.android.server.am; import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.RuntimeInit; +import com.android.server.AttributeCache; import com.android.server.IntentResolver; import com.android.server.ProcessMap; import com.android.server.ProcessStats; @@ -30,22 +30,25 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.AlertDialog; +import android.app.ApplicationErrorReport; import android.app.Dialog; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; -import android.app.IIntentReceiver; -import android.app.IIntentSender; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; import android.app.Instrumentation; import android.app.PendingIntent; import android.app.ResultInfo; +import android.backup.IBackupManager; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -78,6 +81,9 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Checkin; import android.provider.Settings; +import android.server.data.CrashData; +import android.server.data.StackTraceElementData; +import android.server.data.ThrowableData; import android.text.TextUtils; import android.util.Config; import android.util.EventLog; @@ -92,10 +98,13 @@ import android.view.WindowManagerPolicy; import dalvik.system.Zygote; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.PrintWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; @@ -117,11 +126,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_OOM_ADJ = localLOGV || false; static final boolean DEBUG_TRANSITION = localLOGV || false; static final boolean DEBUG_BROADCAST = localLOGV || false; + static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false; static final boolean DEBUG_SERVICE = localLOGV || false; static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean DEBUG_RESULTS = localLOGV || false; + static final boolean DEBUG_BACKUP = localLOGV || true; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -191,6 +202,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Maximum number of recent tasks that we can remember. static final int MAX_RECENT_TASKS = 20; + // Amount of time after a call to stopAppSwitches() during which we will + // prevent further untrusted switches from happening. + static final long APP_SWITCH_DELAY_TIME = 5*1000; + // How long until we reset a task when the user returns to it. Currently // 30 minutes. static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30; @@ -273,6 +288,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // because the user interacts with it so much. final int HOME_APP_ADJ; + // This is a process currently hosting a backup operation. Killing it + // is not entirely fatal but is generally a bad idea. + final int BACKUP_APP_ADJ; + // This is a process holding a secondary server -- killing it will not // have much of an impact as far as the user is concerned. Value set in // system/rootdir/init.rc on startup. @@ -301,6 +320,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final int EMPTY_APP_MEM; final int HIDDEN_APP_MEM; final int HOME_APP_MEM; + final int BACKUP_APP_MEM; final int SECONDARY_SERVER_MEM; final int VISIBLE_APP_MEM; final int FOREGROUND_APP_MEM; @@ -328,6 +348,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final ArrayList mHistory = new ArrayList(); /** + * Description of a request to start a new activity, which has been held + * due to app switches being disabled. + */ + class PendingActivityLaunch { + HistoryRecord r; + HistoryRecord sourceRecord; + Uri[] grantedUriPermissions; + int grantedMode; + boolean onlyIfNeeded; + } + + final ArrayList<PendingActivityLaunch> mPendingActivityLaunches + = new ArrayList<PendingActivityLaunch>(); + + /** * List of all active broadcasts that are to be executed immediately * (without waiting for another broadcast to finish). Currently this only * contains broadcasts to registered receivers, to avoid spinning up @@ -605,6 +640,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<ServiceRecord>(); /** + * Backup/restore process management + */ + String mBackupAppName = null; + BackupRecord mBackupTarget = null; + + /** * List of PendingThumbnailsRecord objects of clients who are still * waiting to receive all of the thumbnails for a task. */ @@ -704,6 +745,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int mFactoryTest; + boolean mCheckedForSetup; + + /** + * The time at which we will allow normal application switches again, + * after a call to {@link #stopAppSwitches()}. + */ + long mAppSwitchesAllowedTime; + + /** + * This is set to true after the first switch after mAppSwitchesAllowedTime + * is set; any switches after that will clear the time. + */ + boolean mDidAppSwitch; + /** * Set while we are wanting to sleep, to prevent any * activities from being started/resumed. @@ -757,6 +812,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ int[] mProcDeaths = new int[20]; + /** + * This is set if we had to do a delayed dexopt of an app before launching + * it, to increasing the ANR timeouts in that case. + */ + boolean mDidDexOpt; + String mDebugApp = null; boolean mWaitForDebugger = false; boolean mDebugTransient = false; @@ -852,6 +913,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int SERVICE_ERROR_MSG = 18; static final int RESUME_TOP_ACTIVITY_MSG = 19; static final int PROC_START_TIMEOUT_MSG = 20; + static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21; AlertDialog mUidAlert; @@ -910,6 +972,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen d.show(); proc.anrDialog = d; } + + ensureScreenEnabled(); } break; case SHOW_FACTORY_ERROR_MSG: { Dialog d = new FactoryErrorDialog( @@ -952,6 +1016,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT); + return; + } broadcastTimeout(); } break; case PAUSE_TIMEOUT_MSG: { @@ -962,9 +1032,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityPaused(token, null, true); } break; case IDLE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); + return; + } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. + IBinder token = (IBinder)msg.obj; Log.w(TAG, "Activity idle timeout for " + token); activityIdleInternal(token, true); } break; @@ -980,6 +1057,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityIdle(token); } break; case SERVICE_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT); + return; + } serviceTimeout((ProcessRecord)msg.obj); } break; case UPDATE_TIME_ZONE: { @@ -1016,6 +1100,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case LAUNCH_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); + return; + } synchronized (ActivityManagerService.this) { if (mLaunchingActivity.isHeld()) { Log.w(TAG, "Launch timeout has expired, giving up wake lock!"); @@ -1036,11 +1126,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } case PROC_START_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT); + return; + } ProcessRecord app = (ProcessRecord)msg.obj; synchronized (ActivityManagerService.this) { processStartTimedOutLocked(app); } } + case DO_PENDING_ACTIVITY_LAUNCHES_MSG: { + synchronized (ActivityManagerService.this) { + doPendingActivityLaunchesLocked(true); + } + } } } }; @@ -1301,6 +1403,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ")); SECONDARY_SERVER_ADJ = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ")); + BACKUP_APP_ADJ = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ")); HOME_APP_ADJ = Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ")); HIDDEN_APP_MIN_ADJ = @@ -1316,6 +1420,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE; SECONDARY_SERVER_MEM = Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + BACKUP_APP_MEM = + Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE; HOME_APP_MEM = Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE; HIDDEN_APP_MEM = @@ -1382,7 +1488,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (mProcessStatsThread) { final long now = SystemClock.uptimeMillis(); boolean haveNewCpuStats = false; - + if (MONITOR_CPU_USAGE && mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) { mLastCpuTime = now; @@ -1414,7 +1520,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()) { @@ -1426,12 +1533,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(); @@ -1495,6 +1608,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return null; } + private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) { + int i = mHistory.size()-1; + while (i >= 0) { + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (!r.finishing && !r.delayedResume && r != notTop) { + return r; + } + i--; + } + return null; + } + /** * This is a simplified version of topRunningActivityLocked that provides a number of * optional skip-over modes. It is intended for use with the ActivityWatcher hook only. @@ -1531,6 +1656,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return proc; } + private void ensurePackageDexOpt(String packageName) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + if (pm.performDexOpt(packageName)) { + mDidDexOpt = true; + } + } catch (RemoteException e) { + } + } + private boolean isNextTransitionForward() { int transit = mWindowManager.getPendingAppTransition(); return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN @@ -1590,6 +1725,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.isHomeActivity) { mHomeProcess = app; } + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); @@ -1640,6 +1776,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.stopped = true; } + // Launch the new version setup screen if needed. We do this -after- + // launching the initial activity (that is, home), so that it can have + // a chance to initialize itself while in the background, making the + // switch back to it faster and look better. + startSetupActivityLocked(); + return true; } @@ -1995,6 +2137,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (prev != null) { prev.resumeKeyDispatchingLocked(); } + + if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) { + long diff = 0; + synchronized (mProcessStatsThread) { + diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume; + } + if (diff > 0) { + BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics(); + synchronized (bsi) { + BatteryStatsImpl.Uid.Proc ps = + bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, + prev.info.packageName); + if (ps != null) { + ps.addForegroundTimeLocked(diff); + } + } + } + } + prev.cpuTimeAtResume = 0; // reset it } /** @@ -2027,6 +2188,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.resumeKeyDispatchingLocked(); ensureActivitiesVisibleLocked(null, 0); mWindowManager.executeAppTransition(); + + // Mark the point when the activity is resuming + // TODO: To be more accurate, the mark should be before the onCreate, + // not after the onResume. But for subsequent starts, onResume is fine. + if (next.app != null) { + synchronized (mProcessStatsThread) { + next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid); + } + } else { + next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process + } } /** @@ -2191,6 +2363,96 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + private boolean startHomeActivityLocked() { + if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL + && mTopAction == null) { + // We are running in factory test mode, but unable to find + // the factory test app, so just sit around displaying the + // error message and don't try to start anything. + return false; + } + Intent intent = new Intent( + mTopAction, + mTopData != null ? Uri.parse(mTopData) : null); + intent.setComponent(mTopComponent); + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + intent.addCategory(Intent.CATEGORY_HOME); + } + ActivityInfo aInfo = + intent.resolveActivityInfo(mContext.getPackageManager(), + STOCK_PM_FLAGS); + if (aInfo != null) { + intent.setComponent(new ComponentName( + aInfo.applicationInfo.packageName, aInfo.name)); + // Don't do this if the home app is currently being + // instrumented. + ProcessRecord app = getProcessRecordLocked(aInfo.processName, + aInfo.applicationInfo.uid); + if (app == null || app.instrumentationClass == null) { + intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivityLocked(null, intent, null, null, 0, aInfo, + null, null, 0, 0, 0, false, false); + } + } + + + return true; + } + + /** + * Starts the "new version setup screen" if appropriate. + */ + private void startSetupActivityLocked() { + // Only do this once per boot. + if (mCheckedForSetup) { + return; + } + + // We will show this screen if the current one is a different + // version than the last one shown, and we are not running in + // low-level factory test mode. + final ContentResolver resolver = mContext.getContentResolver(); + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL && + Settings.Secure.getInt(resolver, + Settings.Secure.DEVICE_PROVISIONED, 0) != 0) { + mCheckedForSetup = true; + + // See if we should be showing the platform update setup UI. + Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP); + List<ResolveInfo> ris = mSelf.mContext.getPackageManager() + .queryIntentActivities(intent, PackageManager.GET_META_DATA); + + // We don't allow third party apps to replace this. + ResolveInfo ri = null; + for (int i=0; ris != null && i<ris.size(); i++) { + if ((ris.get(i).activityInfo.applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0) { + ri = ris.get(i); + break; + } + } + + if (ri != null) { + String vers = ri.activityInfo.metaData != null + ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION) + : null; + if (vers == null && ri.activityInfo.applicationInfo.metaData != null) { + vers = ri.activityInfo.applicationInfo.metaData.getString( + Intent.METADATA_SETUP_VERSION); + } + String lastVers = Settings.Secure.getString( + resolver, Settings.Secure.LAST_SETUP_SHOWN); + if (vers != null && !vers.equals(lastVers)) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setComponent(new ComponentName( + ri.activityInfo.packageName, ri.activityInfo.name)); + startActivityLocked(null, intent, null, null, 0, ri.activityInfo, + null, null, 0, 0, 0, false, false); + } + } + } + } + /** * Ensure that the top activity in the stack is resumed. * @@ -2212,39 +2474,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (next == null) { // There are no more activities! Let's just start up the // Launcher... - if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL - && mTopAction == null) { - // We are running in factory test mode, but unable to find - // the factory test app, so just sit around displaying the - // error message and don't try to start anything. - return false; - } - Intent intent = new Intent( - mTopAction, - mTopData != null ? Uri.parse(mTopData) : null); - intent.setComponent(mTopComponent); - if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - intent.addCategory(Intent.CATEGORY_HOME); - } - ActivityInfo aInfo = - intent.resolveActivityInfo(mContext.getPackageManager(), - STOCK_PM_FLAGS); - if (aInfo != null) { - intent.setComponent(new ComponentName( - aInfo.applicationInfo.packageName, aInfo.name)); - // Don't do this if the home app is currently being - // instrumented. - ProcessRecord app = getProcessRecordLocked(aInfo.processName, - aInfo.applicationInfo.uid); - if (app == null || app.instrumentationClass == null) { - intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivityLocked(null, intent, null, null, 0, aInfo, - null, null, 0, 0, 0, false, false); - } - } - return true; + return startHomeActivityLocked(); } + next.delayedResume = false; + // If the top activity is the resumed one, nothing to do. if (mResumedActivity == next && next.state == ActivityState.RESUMED) { // Make sure we have executed any pending transitions, since there @@ -2471,7 +2705,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - private final void startActivityLocked(HistoryRecord r, boolean newTask) { + private final void startActivityLocked(HistoryRecord r, boolean newTask, + boolean doResume) { final int NH = mHistory.size(); int addPos = -1; @@ -2558,7 +2793,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if ((r.intent.getFlags() &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { resetTaskIfNeededLocked(r, r); - doShow = topRunningActivityLocked(null) == r; + doShow = topRunningNonDelayedActivityLocked(null) == r; } } if (SHOW_APP_STARTING_ICON && doShow) { @@ -2588,13 +2823,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.validateAppTokens(mHistory); } - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } } /** * Perform clear operation as requested by - * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: assuming the top task on the - * stack is the one that the new activity is being launched in, look for + * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the + * stack to the given task, then look for * an instance of that activity in the stack and, if found, finish all * activities on top of it and return the instance. * @@ -2602,9 +2839,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @return Returns the old activity that should be continue to be used, * or null if none was found. */ - private final HistoryRecord performClearTopTaskLocked(int taskId, + private final HistoryRecord performClearTaskLocked(int taskId, HistoryRecord newR, boolean doClear) { int i = mHistory.size(); + + // First find the requested task. + while (i > 0) { + i--; + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (r.task.taskId == taskId) { + i++; + break; + } + } + + // Now clear it. while (i > 0) { i--; HistoryRecord r = (HistoryRecord)mHistory.get(i); @@ -2636,7 +2885,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // instance of the activity so a new fresh one can be started. if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE) { if (!ret.finishing) { - int index = indexOfTokenLocked(ret, false); + int index = indexOfTokenLocked(ret); if (index >= 0) { finishActivityLocked(ret, 0, Activity.RESULT_CANCELED, null, "clear"); @@ -2729,7 +2978,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord sourceRecord = null; HistoryRecord resultRecord = null; if (resultTo != null) { - int index = indexOfTokenLocked(resultTo, false); + int index = indexOfTokenLocked(resultTo); if (DEBUG_RESULTS) Log.v( TAG, "Sending result to " + resultTo + " (index " + index + ")"); if (index >= 0) { @@ -2840,15 +3089,75 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent, resolvedType, aInfo, mConfiguration, resultRecord, resultWho, requestCode, componentSpecified); - HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) - != 0 ? r : null; - + if (mResumedActivity == null + || mResumedActivity.info.applicationInfo.uid != callingUid) { + if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { + PendingActivityLaunch pal = new PendingActivityLaunch(); + pal.r = r; + pal.sourceRecord = sourceRecord; + pal.grantedUriPermissions = grantedUriPermissions; + pal.grantedMode = grantedMode; + pal.onlyIfNeeded = onlyIfNeeded; + mPendingActivityLaunches.add(pal); + return START_SWITCHES_CANCELED; + } + } + + if (mDidAppSwitch) { + // This is the second allowed switch since we stopped switches, + // so now just generally allow switches. Use case: user presses + // home (switches disabled, switch to home, mDidAppSwitch now true); + // user taps a home icon (coming from home so allowed, we hit here + // and now allow anyone to switch again). + mAppSwitchesAllowedTime = 0; + } else { + mDidAppSwitch = true; + } + + doPendingActivityLaunchesLocked(false); + + return startActivityUncheckedLocked(r, sourceRecord, + grantedUriPermissions, grantedMode, onlyIfNeeded, true); + } + + private final void doPendingActivityLaunchesLocked(boolean doResume) { + final int N = mPendingActivityLaunches.size(); + if (N <= 0) { + return; + } + for (int i=0; i<N; i++) { + PendingActivityLaunch pal = mPendingActivityLaunches.get(i); + startActivityUncheckedLocked(pal.r, pal.sourceRecord, + pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded, + doResume && i == (N-1)); + } + mPendingActivityLaunches.clear(); + } + + private final int startActivityUncheckedLocked(HistoryRecord r, + HistoryRecord sourceRecord, Uri[] grantedUriPermissions, + int grantedMode, boolean onlyIfNeeded, boolean doResume) { + final Intent intent = r.intent; + final int callingUid = r.launchedFromUid; + + int launchFlags = intent.getFlags(); + // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Log.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); + // If the caller has asked not to resume at this point, we make note + // of this in the record so that we can skip it when trying to find + // the top running activity. + if (!doResume) { + r.delayedResume = true; + } + + HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) + != 0 ? r : null; + // If the onlyIfNeeded flag is set, then we can do this if the activity // being launched is the same as the one making the call... or, as // a special case, if we do not know the caller then we count the @@ -2856,7 +3165,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (onlyIfNeeded) { HistoryRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - checkedCaller = topRunningActivityLocked(notTop); + checkedCaller = topRunningNonDelayedActivityLocked(notTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { // Caller is not the same as launcher, so always needed. @@ -2894,7 +3203,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } - if (resultRecord != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // For whatever reason this activity is being launched into a new // task... yet the caller has requested a result back. Well, that // is pretty messed up, so instead immediately send back a cancel @@ -2902,10 +3211,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // dependency on its originator. Log.w(TAG, "Activity is launching as a new task, so cancelling activity result."); sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, + r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); r.resultTo = null; - resultRecord = null; } boolean addingToTask = false; @@ -2916,7 +3224,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If bring to front is requested, and no result is requested, and // we can find a task that was started with this same // component, then instead of launching bring that one to the front. - if (resultRecord == null) { + if (r.resultTo == null) { // See if there is a task to bring to the front. If this is // a SINGLE_INSTANCE activity, there can be one and only one // instance of it in the history, and it is always in its own @@ -2938,7 +3246,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to have the same behavior as if a new instance was // being started, which means not bringing it to the front // if the caller is not itself in the front. - HistoryRecord curTop = topRunningActivityLocked(notTop); + HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop); if (curTop.task != taskTop.task) { r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); boolean callerAtFront = sourceRecord == null @@ -2959,7 +3267,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // the client said not to do anything if that // is the case, so this is it! And for paranoia, make // sure we have correctly resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_RETURN_INTENT_TO_CALLER; } if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 @@ -2969,7 +3279,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // from the task up to the one being started. In most // cases this means we are resetting the task to its // initial state. - HistoryRecord top = performClearTopTaskLocked( + HistoryRecord top = performClearTaskLocked( taskTop.task.taskId, r, true); if (top != null) { if (top.frontOfTask) { @@ -3035,7 +3345,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We didn't do anything... but it was needed (a.k.a., client // don't use that intent!) And for paranoia, make // sure we have correctly resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_TASK_TO_FRONT; } } @@ -3052,8 +3364,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the activity being launched is the same as the one currently // at the top, then we need to check if it should only be launched // once. - HistoryRecord top = topRunningActivityLocked(notTop); - if (top != null && resultRecord == null) { + HistoryRecord top = topRunningNonDelayedActivityLocked(notTop); + if (top != null && r.resultTo == null) { if (top.realActivity.equals(r.realActivity)) { if (top.app != null && top.app.thread != null) { if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 @@ -3062,7 +3374,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen logStartActivity(LOG_AM_NEW_INTENT, top, top.task); // For paranoia, make sure we have correctly // resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } if (onlyIfNeeded) { // We don't need to start a new activity, and // the client said not to do anything if that @@ -3077,9 +3391,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } else { - if (resultRecord != null) { + if (r.resultTo != null) { sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, + r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); } return START_CLASS_NOT_FOUND; @@ -3088,7 +3402,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean newTask = false; // Should this be considered a new task? - if (resultRecord == null && !addingToTask + if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // todo: should do better management of integers. mCurTask++; @@ -3108,14 +3422,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // In this case, we are adding the activity to an existing // task, but the caller has asked to clear that task if the // activity is already running. - HistoryRecord top = performClearTopTaskLocked( + HistoryRecord top = performClearTaskLocked( sourceRecord.task.taskId, r, true); if (top != null) { logStartActivity(LOG_AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); // For paranoia, make sure we have correctly // resumed the top activity. - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_DELIVERED_TO_TOP; } } else if (!addingToTask && @@ -3128,7 +3444,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord top = moveActivityToFrontLocked(where); logStartActivity(LOG_AM_NEW_INTENT, r, top.task); deliverNewIntentLocked(top, r.intent); - resumeTopActivityLocked(null); + if (doResume) { + resumeTopActivityLocked(null); + } return START_DELIVERED_TO_TOP; } } @@ -3157,7 +3475,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_CREATE_TASK, r.task.taskId); } logStartActivity(LOG_AM_CREATE_ACTIVITY, r, r.task); - startActivityLocked(r, newTask); + startActivityLocked(r, newTask, doResume); return START_SUCCESS; } @@ -3226,7 +3544,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized (this) { - int index = indexOfTokenLocked(callingActivity, false); + int index = indexOfTokenLocked(callingActivity); if (index < 0) { return false; } @@ -3376,7 +3694,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -3398,7 +3716,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public int getRequestedOrientation(IBinder token) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -3454,7 +3772,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen TAG, "Finishing activity: token=" + token + ", result=" + resultCode + ", data=" + resultData); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return false; } @@ -3578,7 +3896,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r, int mode) { - final int index = indexOfTokenLocked(r, false); + final int index = indexOfTokenLocked(r); if (index < 0) { return null; } @@ -3703,7 +4021,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -4253,7 +4571,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -4523,6 +4841,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mPendingBroadcast = null; scheduleBroadcastsLocked(); } + if (mBackupTarget != null && mBackupTarget.app.pid == pid) { + Log.w(TAG, "Unattached app died before backup, skipping"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // Can't happen; the backup manager is local + } + } } else { Log.w(TAG, "Spurious process start timeout - pid not known for " + app); } @@ -4587,6 +4915,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.thread = thread; app.curAdj = app.setAdj = -100; + app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; app.forcingToForeground = null; app.foregroundServices = false; app.debugging = false; @@ -4610,11 +4939,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWaitForDebugger = mOrigWaitForDebugger; } } + // If the app is being launched for restore or full backup, set it up specially + boolean isRestrictedBackupMode = false; + if (mBackupTarget != null && mBackupAppName.equals(processName)) { + isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) + || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); + } + ensurePackageDexOpt(app.instrumentationInfo != null + ? app.instrumentationInfo.packageName + : app.info.packageName); + if (app.instrumentationClass != null) { + ensurePackageDexOpt(app.instrumentationClass.getPackageName()); + } thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, - mConfiguration, getCommonServicesLocked()); + isRestrictedBackupMode, mConfiguration, getCommonServicesLocked()); updateLRUListLocked(app, false); app.lastRequestedGc = SystemClock.uptimeMillis(); } catch (Exception e) { @@ -4695,6 +5036,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + // Check whether the next backup agent is in this process... + if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { + if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app); + ensurePackageDexOpt(mBackupTarget.appInfo.packageName); + try { + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + } catch (Exception e) { + Log.w(TAG, "Exception scheduling backup agent creation: "); + e.printStackTrace(); + } + } + if (badApp) { // todo: Also need to kill application to deal with all // kinds of exceptions. @@ -4790,7 +5143,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Get the activity record. - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); @@ -4911,6 +5264,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + final void ensureScreenEnabled() { + boolean enableScreen; + synchronized (this) { + enableScreen = !mBooted; + mBooted = true; + } + + if (enableScreen) { + EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, + SystemClock.uptimeMillis()); + enableScreenAfterBoot(); + } + } + public final void activityPaused(IBinder token, Bundle icicle) { // Refuse possible leaked file descriptors if (icicle != null && icicle.hasFileDescriptors()) { @@ -4930,7 +5297,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = null; synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); if (!timeout) { @@ -4961,7 +5328,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); r.thumbnail = thumbnail; @@ -4991,7 +5358,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r.state == ActivityState.DESTROYING) { @@ -5018,7 +5385,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private HistoryRecord getCallingRecordLocked(IBinder token) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r != null) { @@ -5030,7 +5397,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.intent.getComponent(); @@ -5041,7 +5408,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public String getPackageForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.packageName; @@ -5080,7 +5447,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } HistoryRecord activity = null; if (type == INTENT_SENDER_ACTIVITY_RESULT) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return null; } @@ -6251,6 +6618,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskToFront()"); synchronized(this) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to front")) { + return; + } final long origId = Binder.clearCallingIdentity(); try { int N = mRecentTasks.size(); @@ -6335,6 +6706,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskToBack()"); synchronized(this) { + if (mResumedActivity != null && mResumedActivity.task.taskId == task) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to back")) { + return; + } + } final long origId = Binder.clearCallingIdentity(); moveTaskToBackLocked(task); Binder.restoreCallingIdentity(origId); @@ -6438,6 +6815,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "moveTaskBackwards()"); synchronized(this) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task backwards")) { + return; + } final long origId = Binder.clearCallingIdentity(); moveTaskBackwardsLocked(task); Binder.restoreCallingIdentity(origId); @@ -6587,7 +6968,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { if (r == null) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -6670,6 +7051,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.pubProviders.put(cpi.name, cpr); app.addPackage(cpi.applicationInfo.packageName); + ensurePackageDexOpt(cpi.applicationInfo.packageName); } } return providers; @@ -7179,6 +7561,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public void stopAppSwitches() { + if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.STOP_APP_SWITCHES); + } + + synchronized(this) { + mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + + APP_SWITCH_DELAY_TIME; + mDidAppSwitch = false; + mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG); + Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG); + mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME); + } + } + + public void resumeAppSwitches() { + if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.STOP_APP_SWITCHES); + } + + synchronized(this) { + // Note that we don't execute any pending app switches... we will + // let those wait until either the timeout, or the next start + // activity request. + mAppSwitchesAllowedTime = 0; + } + } + + boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid, + String name) { + if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { + return true; + } + + final int perm = checkComponentPermission( + android.Manifest.permission.STOP_APP_SWITCHES, callingPid, + callingUid, -1); + if (perm == PackageManager.PERMISSION_GRANTED) { + return true; + } + + Log.w(TAG, name + " request from " + callingUid + " stopped"); + return false; + } + public void setDebugApp(String packageName, boolean waitForDebugger, boolean persistent) { enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP, @@ -7595,6 +8026,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return handleAppCrashLocked(app); } + private ComponentName getErrorReportReceiver(ProcessRecord app) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + // was an installer package name specified when this app was + // installed? + String installerPackageName = pm.getInstallerPackageName(app.info.packageName); + if (installerPackageName == null) { + return null; + } + + // is there an Activity in this package that handles ACTION_APP_ERROR? + Intent intent = new Intent(Intent.ACTION_APP_ERROR); + intent.setPackage(installerPackageName); + ResolveInfo info = pm.resolveIntent(intent, null, 0); + if (info == null || info.activityInfo == null) { + return null; + } + + return new ComponentName(installerPackageName, info.activityInfo.name); + } catch (RemoteException e) { + // will return null and no error report will be delivered + } + return null; + } + void makeAppNotRespondingLocked(ProcessRecord app, String tag, String shortMsg, String longMsg, byte[] crashData) { app.notResponding = true; @@ -7713,6 +8169,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void startAppProblemLocked(ProcessRecord app) { + app.errorReportReceiver = getErrorReportReceiver(app); skipCurrentReceiverLocked(app); } @@ -7745,7 +8202,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public int handleApplicationError(IBinder app, int flags, String tag, String shortMsg, String longMsg, byte[] crashData) { AppErrorResult result = new AppErrorResult(); - ProcessRecord r = null; synchronized (this) { if (app != null) { @@ -7834,16 +8290,103 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int res = result.get(); + Intent appErrorIntent = null; synchronized (this) { if (r != null) { mProcessCrashTimes.put(r.info.processName, r.info.uid, SystemClock.uptimeMillis()); } + if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { + appErrorIntent = createAppErrorIntentLocked(r); + res = AppErrorDialog.FORCE_QUIT; + } + } + + if (appErrorIntent != null) { + try { + mContext.startActivity(appErrorIntent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "bug report receiver dissappeared", e); + } } return res; } + Intent createAppErrorIntentLocked(ProcessRecord r) { + ApplicationErrorReport report = createAppErrorReportLocked(r); + if (report == null) { + return null; + } + Intent result = new Intent(Intent.ACTION_APP_ERROR); + result.setComponent(r.errorReportReceiver); + result.putExtra(Intent.EXTRA_BUG_REPORT, report); + result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return result; + } + + ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) { + if (r.errorReportReceiver == null) { + return null; + } + + if (!r.crashing && !r.notResponding) { + return null; + } + + try { + ApplicationErrorReport report = new ApplicationErrorReport(); + report.packageName = r.info.packageName; + report.installerPackageName = r.errorReportReceiver.getPackageName(); + report.processName = r.processName; + + if (r.crashing) { + report.type = ApplicationErrorReport.TYPE_CRASH; + report.crashInfo = new ApplicationErrorReport.CrashInfo(); + + ByteArrayInputStream byteStream = new ByteArrayInputStream( + r.crashingReport.crashData); + DataInputStream dataStream = new DataInputStream(byteStream); + CrashData crashData = new CrashData(dataStream); + ThrowableData throwData = crashData.getThrowableData(); + + report.time = crashData.getTime(); + report.crashInfo.stackTrace = throwData.toString(); + + // Extract the source of the exception, useful for report + // clustering. Also extract the "deepest" non-null exception + // message. + String exceptionMessage = throwData.getMessage(); + while (throwData.getCause() != null) { + throwData = throwData.getCause(); + String msg = throwData.getMessage(); + if (msg != null && msg.length() > 0) { + exceptionMessage = msg; + } + } + StackTraceElementData trace = throwData.getStackTrace()[0]; + report.crashInfo.exceptionMessage = exceptionMessage; + report.crashInfo.exceptionClassName = throwData.getType(); + report.crashInfo.throwFileName = trace.getFileName(); + report.crashInfo.throwClassName = trace.getClassName(); + report.crashInfo.throwMethodName = trace.getMethodName(); + } else if (r.notResponding) { + report.type = ApplicationErrorReport.TYPE_ANR; + report.anrInfo = new ApplicationErrorReport.AnrInfo(); + + report.anrInfo.activity = r.notRespondingReport.tag; + report.anrInfo.cause = r.notRespondingReport.shortMsg; + report.anrInfo.info = r.notRespondingReport.longMsg; + } + + return report; + } catch (IOException e) { + // we don't send it + } + + return null; + } + public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { // assume our apps are happy - lazy create the list List<ActivityManager.ProcessErrorStateInfo> errList = null; @@ -8475,9 +9018,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " #" + i + ":"); r.dump(pw, prefix + " "); } else if (inclOomAdj) { - pw.println(String.format("%s%s #%2d: oom_adj=%3d %s", + pw.println(String.format("%s%s #%2d: adj=%3d/%d %s", prefix, (r.persistent ? persistentLabel : normalLabel), - i, r.setAdj, r.toString())); + i, r.setAdj, r.setSchedGroup, r.toString())); } else { pw.println(String.format("%s%s #%2d: %s", prefix, (r.persistent ? persistentLabel : normalLabel), @@ -8540,7 +9083,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } - private final int indexOfTokenLocked(IBinder token, boolean required) { + private final int indexOfTokenLocked(IBinder token) { int count = mHistory.size(); // convert the token to an entry in the history. @@ -8554,19 +9097,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } - if (index < 0 && required) { - RuntimeInit.crash(TAG, new InvalidTokenException(token)); - } return index; } - static class InvalidTokenException extends Exception { - InvalidTokenException(IBinder token) { - super("Bad activity token: " + token); - } - } - private final void killServicesLocked(ProcessRecord app, boolean allowRestart) { // Report disconnected services. @@ -8790,6 +9324,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.receivers.clear(); } + // If the app is undergoing backup, tell the backup manager about it + if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { + if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // can't happen; backup manager is local + } + } + // If the caller is restarting this app, then leave it in its // current lists and let the caller take care of it. if (restarting) { @@ -9130,6 +9676,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } + ensurePackageDexOpt(r.serviceInfo.packageName); app.thread.scheduleCreateService(r, r.serviceInfo); created = true; } finally { @@ -9579,7 +10126,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord activity = null; if (token != null) { - int aindex = indexOfTokenLocked(token, false); + int aindex = indexOfTokenLocked(token); if (aindex < 0) { Log.w(TAG, "Binding with unknown activity: " + token); return 0; @@ -9906,6 +10453,128 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // ========================================================= + // BACKUP AND RESTORE + // ========================================================= + + // Cause the target app to be launched if necessary and its backup agent + // instantiated. The backup agent will invoke backupAgentCreated() on the + // activity manager to announce its creation. + public boolean bindBackupAgent(ApplicationInfo app, int backupMode) { + if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); + enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); + + synchronized(this) { + // !!! TODO: currently no check here that we're already bound + BatteryStatsImpl.Uid.Pkg.Serv ss = null; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); + } + + BackupRecord r = new BackupRecord(ss, app, backupMode); + ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); + // startProcessLocked() returns existing proc's record if it's already running + ProcessRecord proc = startProcessLocked(app.processName, app, + false, 0, "backup", hostingName); + if (proc == null) { + Log.e(TAG, "Unable to start backup agent process " + r); + return false; + } + + r.app = proc; + mBackupTarget = r; + mBackupAppName = app.packageName; + + // Try not to kill the process during backup + updateOomAdjLocked(proc); + + // If the process is already attached, schedule the creation of the backup agent now. + // If it is not yet live, this will be done when it attaches to the framework. + if (proc.thread != null) { + if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc); + try { + proc.thread.scheduleCreateBackupAgent(app, backupMode); + } catch (RemoteException e) { + // !!! TODO: notify the backup manager that we crashed, or rely on + // death notices, or...? + } + } else { + if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach"); + } + // Invariants: at this point, the target app process exists and the application + // is either already running or in the process of coming up. mBackupTarget and + // mBackupAppName describe the app, so that when it binds back to the AM we + // know that it's scheduled for a backup-agent operation. + } + + return true; + } + + // A backup agent has just come up + public void backupAgentCreated(String agentPackageName, IBinder agent) { + if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName + + " = " + agent); + + synchronized(this) { + if (!agentPackageName.equals(mBackupAppName)) { + Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); + return; + } + + long oldIdent = Binder.clearCallingIdentity(); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentConnected(agentPackageName, agent); + } catch (RemoteException e) { + // can't happen; the backup manager service is local + } catch (Exception e) { + Log.w(TAG, "Exception trying to deliver BackupAgent binding: "); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldIdent); + } + } + } + + // 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; + } + + ProcessRecord proc = mBackupTarget.app; + mBackupTarget = null; + mBackupAppName = null; + + // Not backing this app up any more; reset its OOM adjustment + updateOomAdjLocked(proc); + + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo); + } catch (Exception e) { + Log.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } + } + } + } + // ========================================================= // BROADCASTS // ========================================================= @@ -10078,7 +10747,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST_LIGHT) Log.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); if ((resultTo != null) && !ordered) { @@ -10114,6 +10783,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { uninstallPackageLocked(ssp, intent.getIntExtra(Intent.EXTRA_UID, -1), false); + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.removePackage(ssp); + } } } } @@ -10176,8 +10849,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - final ContentResolver resolver = mContext.getContentResolver(); - // Figure out who all will receive this broadcast. List receivers = null; List<BroadcastFilter> registeredReceivers = null; @@ -10200,8 +10871,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityThread.getPackageManager().queryIntentReceivers( intent, resolvedType, STOCK_PM_FLAGS); } - registeredReceivers = mReceiverResolver.queryIntent(resolver, - intent, resolvedType, false); + registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false); } } catch (RemoteException ex) { // pm is in same process, this will never happen. @@ -10573,9 +11243,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean started = false; try { - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Delivering to component " + r.curComponent + ": " + r); + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, r.resultCode, r.resultData, r.resultExtras, r.ordered); started = true; @@ -10643,12 +11314,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.curFilter = filter; filter.receiverList.curBroadcast = r; r.state = BroadcastRecord.CALL_IN_RECEIVE; + if (filter.receiverList.app != null) { + // Bump hosting application to no longer be in background + // scheduling class. Note that we can't do that if there + // isn't an app... but we can only be in that case for + // things that directly call the IActivityManager API, which + // are already core system stuff so don't matter for this. + r.curApp = filter.receiverList.app; + filter.receiverList.app.curReceiver = r; + updateOomAdjLocked(); + } } try { - if (DEBUG_BROADCAST) { + if (DEBUG_BROADCAST_LIGHT) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Sending broadcast " + r.intent.getAction() + " seq=" + seq - + " app=" + filter.receiverList.app); + Log.i(TAG, "Delivering to " + filter.receiverList.app + + " (seq=" + seq + "): " + r); } performReceive(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, @@ -10662,6 +11343,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.receiver = null; r.curFilter = null; filter.receiverList.curBroadcast = null; + if (filter.receiverList.app != null) { + filter.receiverList.app.curReceiver = null; + } } } } @@ -10685,6 +11369,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (mParallelBroadcasts.size() > 0) { r = mParallelBroadcasts.remove(0); final int N = r.receivers.size(); + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing parallel broadcast " + + r); for (int i=0; i<N; i++) { Object target = r.receivers.get(i); if (DEBUG_BROADCAST) Log.v(TAG, @@ -10692,6 +11378,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + target + ": " + r); deliverToRegisteredReceiver(r, (BroadcastFilter)target, false); } + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Done with parallel broadcast " + + r); } // Now take care of the next serialized one... @@ -10717,10 +11405,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + boolean looped = false; + do { if (mOrderedBroadcasts.size() == 0) { // No more broadcasts pending, so all done! scheduleAppGcsLocked(); + if (looped) { + // If we had finished the last ordered broadcast, then + // make sure all processes have correct oom and sched + // adjustments. + updateOomAdjLocked(); + } return; } r = mOrderedBroadcasts.get(0); @@ -10777,9 +11473,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_BROADCAST) Log.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Finished with ordered broadcast " + + r); + // ... and on to the next... mOrderedBroadcasts.remove(0); r = null; + looped = true; continue; } } while (r == null); @@ -10793,6 +11493,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (recIdx == 0) { r.dispatchTime = r.startTime; + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing ordered broadcast " + + r); if (DEBUG_BROADCAST) Log.v(TAG, "Submitting BROADCAST_TIMEOUT_MSG for " + (r.startTime + BROADCAST_TIMEOUT)); @@ -11135,6 +11837,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID); + + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.updateConfiguration(mConfiguration); + } } } @@ -11380,6 +12087,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.curRawAdj = adj; app.curAdj = adj <= app.maxAdj ? adj : app.maxAdj; + if (mBackupTarget != null && app == mBackupTarget.app) { + // If possible we want to avoid killing apps while they're being backed up + if (adj > BACKUP_APP_ADJ) { + if (DEBUG_BACKUP) Log.v(TAG, "oom BACKUP_APP_ADJ for " + app); + adj = BACKUP_APP_ADJ; + } + } + if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) { // If this process has active services running in it, we would // like to avoid killing it unless it would prevent the current @@ -11506,7 +12221,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.curAdj = adj; - + app.curSchedGroup = (adj > VISIBLE_APP_ADJ && !app.persistent) + ? Process.THREAD_GROUP_BG_NONINTERACTIVE + : Process.THREAD_GROUP_DEFAULT; + return adj; } @@ -11651,6 +12369,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } } + if (app.setSchedGroup != app.curSchedGroup) { + app.setSchedGroup = app.curSchedGroup; + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Log.v(TAG, + "Setting process group of " + app.processName + + " to " + app.curSchedGroup); + if (true) { + long oldId = Binder.clearCallingIdentity(); + try { + Process.setProcessGroup(app.pid, app.curSchedGroup); + } catch (Exception e) { + Log.w(TAG, "Failed setting process group of " + app.pid + + " to " + app.curSchedGroup); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldId); + } + } + if (false) { + if (app.thread != null) { + try { + app.thread.setSchedulingGroup(app.curSchedGroup); + } catch (RemoteException e) { + } + } + } + } } return true; @@ -11942,51 +12686,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/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java index 3fcfad060ac5..33894d66d3cb 100644 --- a/services/java/com/android/server/am/AppErrorDialog.java +++ b/services/java/com/android/server/am/AppErrorDialog.java @@ -19,17 +19,22 @@ package com.android.server.am; import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR; import android.content.Context; +import android.content.DialogInterface; import android.content.res.Resources; import android.os.Handler; import android.os.Message; +import android.util.Log; class AppErrorDialog extends BaseErrorDialog { + private final static String TAG = "AppErrorDialog"; + private final AppErrorResult mResult; private final ProcessRecord mProc; // Event 'what' codes static final int FORCE_QUIT = 0; static final int DEBUG = 1; + static final int FORCE_QUIT_AND_REPORT = 2; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; @@ -58,12 +63,22 @@ class AppErrorDialog extends BaseErrorDialog { setCancelable(false); - setButton(res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(FORCE_QUIT)); + setButton(DialogInterface.BUTTON_POSITIVE, + res.getText(com.android.internal.R.string.force_close), + mHandler.obtainMessage(FORCE_QUIT)); + if ((flags&1) != 0) { - setButton(res.getText(com.android.internal.R.string.debug), + setButton(DialogInterface.BUTTON_NEUTRAL, + res.getText(com.android.internal.R.string.debug), mHandler.obtainMessage(DEBUG)); } + + if (app.errorReportReceiver != null) { + setButton(DialogInterface.BUTTON_NEGATIVE, + res.getText(com.android.internal.R.string.report), + mHandler.obtainMessage(FORCE_QUIT_AND_REPORT)); + } + setTitle(res.getText(com.android.internal.R.string.aerr_title)); getWindow().addFlags(FLAG_SYSTEM_ERROR); getWindow().setTitle("Application Error: " + app.info.processName); diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java index 7390ed0263c8..03c2a0416354 100644 --- a/services/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/java/com/android/server/am/AppNotRespondingDialog.java @@ -18,7 +18,10 @@ package com.android.server.am; import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.os.Message; @@ -26,6 +29,13 @@ import android.os.Process; import android.util.Log; class AppNotRespondingDialog extends BaseErrorDialog { + private static final String TAG = "AppNotRespondingDialog"; + + // Event 'what' codes + static final int FORCE_CLOSE = 1; + static final int WAIT = 2; + static final int WAIT_AND_REPORT = 3; + private final ActivityManagerService mService; private final ProcessRecord mProc; @@ -67,10 +77,19 @@ class AppNotRespondingDialog extends BaseErrorDialog { ? res.getString(resid, name1.toString(), name2.toString()) : res.getString(resid, name1.toString())); - setButton(res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(1)); - setButton2(res.getText(com.android.internal.R.string.wait), - mHandler.obtainMessage(2)); + setButton(DialogInterface.BUTTON_POSITIVE, + res.getText(com.android.internal.R.string.force_close), + mHandler.obtainMessage(FORCE_CLOSE)); + setButton(DialogInterface.BUTTON_NEUTRAL, + res.getText(com.android.internal.R.string.wait), + mHandler.obtainMessage(WAIT)); + + if (app.errorReportReceiver != null) { + setButton(DialogInterface.BUTTON_NEGATIVE, + res.getText(com.android.internal.R.string.report), + mHandler.obtainMessage(WAIT_AND_REPORT)); + } + setTitle(res.getText(com.android.internal.R.string.anr_title)); getWindow().addFlags(FLAG_SYSTEM_ERROR); getWindow().setTitle("Application Not Responding: " + app.info.processName); @@ -81,16 +100,23 @@ class AppNotRespondingDialog extends BaseErrorDialog { private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { + Intent appErrorIntent = null; switch (msg.what) { - case 1: + case FORCE_CLOSE: // Kill the application. mService.killAppAtUsersRequest(mProc, AppNotRespondingDialog.this, true); break; - case 2: + case WAIT_AND_REPORT: + case WAIT: // Continue waiting for the application. synchronized (mService) { ProcessRecord app = mProc; + + if (msg.what == WAIT_AND_REPORT) { + appErrorIntent = mService.createAppErrorIntentLocked(app); + } + app.notResponding = false; app.notRespondingReport = null; if (app.anrDialog == AppNotRespondingDialog.this) { @@ -99,6 +125,14 @@ class AppNotRespondingDialog extends BaseErrorDialog { } break; } + + if (appErrorIntent != null) { + try { + getContext().startActivity(appErrorIntent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "bug report receiver dissappeared", e); + } + } } }; } diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java new file mode 100644 index 000000000000..5ac8e0d802ff --- /dev/null +++ b/services/java/com/android/server/am/BackupRecord.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import com.android.internal.os.BatteryStatsImpl; + +import android.content.pm.ApplicationInfo; + +/** @hide */ +class BackupRecord { + // backup/restore modes + public static final int BACKUP_NORMAL = 0; + public static final int BACKUP_FULL = 1; + public static final int RESTORE = 2; + + final BatteryStatsImpl.Uid.Pkg.Serv stats; + String stringName; // cached toString() output + final ApplicationInfo appInfo; // information about BackupAgent's app + final int backupMode; // full backup / incremental / restore + ProcessRecord app; // where this agent is running or null + + // ----- Implementation ----- + + BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo, + int _backupMode) { + stats = _agentStats; + appInfo = _appInfo; + backupMode = _backupMode; + } + + public String toString() { + if (stringName != null) { + return stringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("BackupRecord{") + .append(Integer.toHexString(System.identityHashCode(this))) + .append(' ').append(appInfo.packageName) + .append(' ').append(appInfo.name) + .append(' ').append(appInfo.backupAgentName).append('}'); + return stringName = sb.toString(); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 0387be5285fd..39a1ee050226 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -16,17 +16,19 @@ 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; 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; @@ -177,10 +179,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void notePhoneSignalStrength(int asu) { + public void notePhoneSignalStrength(SignalStrength signalStrength) { enforceCallingPermission(); synchronized (mStats) { - mStats.notePhoneSignalStrengthLocked(asu); + mStats.notePhoneSignalStrengthLocked(signalStrength); } } @@ -190,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) { @@ -205,6 +214,34 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteStartAudio(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAudioOnLocked(uid); + } + } + + public void noteStopAudio(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAudioOffLocked(uid); + } + } + + public void noteStartVideo(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteVideoOnLocked(uid); + } + } + + public void noteStopVideo(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteVideoOffLocked(uid); + } + } + public void noteWifiRunning() { enforceCallingPermission(); synchronized (mStats) { diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 4057ae82d374..da55049de7ad 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index 14887910cdbd..b3fc313f4abf 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -66,6 +66,7 @@ class HistoryRecord extends IApplicationToken.Stub { int theme; // resource identifier of activity's theme. TaskRecord task; // the task this is in. long startTime; // when we starting launching this activity + long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity Configuration configuration; // configuration activity was last running in HistoryRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. @@ -85,6 +86,7 @@ class HistoryRecord extends IApplicationToken.Stub { boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? + boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? boolean configDestroy; // need to destroy due to config change? int configChangeFlags; // which config values have changed @@ -146,6 +148,7 @@ class HistoryRecord extends IApplicationToken.Stub { pw.print(" icicle="); pw.println(icicle); pw.print(prefix); pw.print("state="); pw.print(state); pw.print(" stopped="); pw.print(stopped); + pw.print(" delayedResume="); pw.print(delayedResume); pw.print(" finishing="); pw.println(finishing); pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused); pw.print(" inHistory="); pw.print(inHistory); @@ -191,6 +194,7 @@ class HistoryRecord extends IApplicationToken.Stub { launchFailed = false; haveState = false; stopped = false; + delayedResume = false; finishing = false; configDestroy = false; keysPaused = false; @@ -459,6 +463,12 @@ class HistoryRecord extends IApplicationToken.Stub { return false; } + if (service.mDidDexOpt) { + // Give more time since we were dexopting. + service.mDidDexOpt = false; + return false; + } + if (r.app.instrumentationClass == null) { service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut"); } else { diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 438139282255..fa2a1004cda7 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -17,8 +17,8 @@ package com.android.server.am; import android.app.IActivityManager; -import android.app.IIntentSender; -import android.app.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.app.PendingIntent; import android.content.Intent; import android.os.Binder; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 68aebc3adc1a..3f59710b33db 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -56,6 +56,8 @@ class ProcessRecord implements Watchdog.PssRequestor { int setRawAdj; // Last set OOM unlimited adjustment for this process int curAdj; // Current OOM adjustment for this process int setAdj; // Last set OOM adjustment for this process + int curSchedGroup; // Currently desired scheduling class + int setSchedGroup; // Last set to background scheduling class boolean isForeground; // Is this app running the foreground UI? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? @@ -107,6 +109,10 @@ class ProcessRecord implements Watchdog.PssRequestor { ActivityManager.ProcessErrorStateInfo crashingReport; ActivityManager.ProcessErrorStateInfo notRespondingReport; + // Who will be notified of the error. This is usually an activity in the + // app that installed the package. + ComponentName errorReportReceiver; + void dump(PrintWriter pw, String prefix) { if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); @@ -143,6 +149,8 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" setRaw="); pw.print(setRawAdj); pw.print(" cur="); pw.print(curAdj); pw.print(" set="); pw.println(setAdj); + pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); + pw.print(" setSchedGroup="); pw.println(setSchedGroup); pw.print(prefix); pw.print("isForeground="); pw.print(isForeground); pw.print(" setIsForeground="); pw.print(setIsForeground); pw.print(" foregroundServices="); pw.print(foregroundServices); @@ -157,7 +165,14 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.print(" "); pw.print(crashDialog); pw.print(" notResponding="); pw.print(notResponding); pw.print(" " ); pw.print(anrDialog); - pw.print(" bad="); pw.println(bad); + pw.print(" bad="); pw.print(bad); + + // crashing or notResponding is always set before errorReportReceiver + if (errorReportReceiver != null) { + pw.print(" errorReportReceiver="); + pw.print(errorReportReceiver.flattenToShortString()); + } + pw.println(); } if (activities.size() > 0) { pw.print(prefix); pw.print("activities="); pw.println(activities); diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java index 0facefc52d3a..32c24c6cbf6f 100644 --- a/services/java/com/android/server/am/ReceiverList.java +++ b/services/java/com/android/server/am/ReceiverList.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.Intent; import android.os.Binder; import android.os.Bundle; diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 866334b2d28e..2d58659dfd3b 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -56,9 +56,9 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1004; + private static final int VERSION = 1005; - private static final int CHECKIN_VERSION = 3; + private static final int CHECKIN_VERSION = 4; private static final String FILE_PREFIX = "usage-"; @@ -82,7 +82,9 @@ public final class UsageStatsService extends IUsageStats.Stub { // this lock held. final Object mFileLock; // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks - private String mResumedPkg; + private String mLastResumedPkg; + private String mLastResumedComp; + private boolean mIsResumed; private File mFile; private String mFileLeaf; //private File mBackupFile; @@ -92,11 +94,16 @@ public final class UsageStatsService extends IUsageStats.Stub { private int mLastWriteDay; static class TimeStats { + int count; int[] times = new int[NUM_LAUNCH_TIME_BINS]; TimeStats() { } + void incCount() { + count++; + } + void add(int val) { final int[] bins = LAUNCH_TIME_BINS; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { @@ -109,6 +116,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } TimeStats(Parcel in) { + count = in.readInt(); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { localTimes[i] = in.readInt(); @@ -116,6 +124,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } void writeToParcel(Parcel out) { + out.writeInt(count); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { out.writeInt(localTimes[i]); @@ -152,8 +161,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume() { - mLaunchCount ++; + void updateResume(boolean launched) { + if (launched) { + mLaunchCount ++; + } mResumedTime = SystemClock.elapsedRealtime(); } @@ -162,6 +173,15 @@ public final class UsageStatsService extends IUsageStats.Stub { mUsageTime += (mPausedTime - mResumedTime); } + void addLaunchCount(String comp) { + TimeStats times = mLaunchTimes.get(comp); + if (times == null) { + times = new TimeStats(); + mLaunchTimes.put(comp, times); + } + times.incCount(); + } + void addLaunchTime(String comp, int millis) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -436,43 +456,70 @@ public final class UsageStatsService extends IUsageStats.Stub { public void noteResumeComponent(ComponentName componentName) { enforceCallingPermission(); String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) { - // Moving across activities in same package. just return - return; - } - if (localLOGV) Log.i(TAG, "started component:"+pkgName); synchronized (mStatsLock) { + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + + final boolean samePackage = pkgName.equals(mLastResumedPkg); + if (mIsResumed) { + if (samePackage) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be resumed"); + return; + } + + if (mLastResumedPkg != null) { + // We last resumed some other package... just pause it now + // to recover. + Log.w(TAG, "Unexpected resume of " + pkgName + + " while already resumed in " + mLastResumedPkg); + PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + if (pus != null) { + pus.updatePause(); + } + } + } + + final boolean sameComp = samePackage + && componentName.getClassName().equals(mLastResumedComp); + + mIsResumed = true; + mLastResumedPkg = pkgName; + mLastResumedComp = componentName.getClassName(); + + if (localLOGV) Log.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { pus = new PkgUsageStatsExtended(); mStats.put(pkgName, pus); } - pus.updateResume(); + pus.updateResume(!samePackage); + if (!sameComp) { + pus.addLaunchCount(mLastResumedComp); + } } - mResumedPkg = pkgName; } public void notePauseComponent(ComponentName componentName) { enforceCallingPermission(); - String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) { - Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused"); - return; - } - if (localLOGV) Log.i(TAG, "paused component:"+pkgName); - - // Persist current data to file if needed. - writeStatsToFile(false); synchronized (mStatsLock) { + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if (!mIsResumed) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be paused"); + return; + } + mIsResumed = false; + + if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here @@ -481,6 +528,9 @@ public final class UsageStatsService extends IUsageStats.Stub { } pus.updatePause(); } + + // Persist current data to file if needed. + writeStatsToFile(false); } public void noteLaunchTime(ComponentName componentName, int millis) { @@ -631,9 +681,9 @@ public final class UsageStatsService extends IUsageStats.Stub { if (isCompactOutput) { sb.append("P:"); sb.append(pkgName); - sb.append(","); + sb.append(','); sb.append(pus.mLaunchCount); - sb.append(","); + sb.append(','); sb.append(pus.mUsageTime); sb.append('\n'); final int NC = pus.mLaunchTimes.size(); @@ -642,6 +692,8 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append("A:"); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(','); + sb.append(times.count); for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { sb.append(","); sb.append(times.times[i]); @@ -665,25 +717,26 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(" "); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(": "); + sb.append(times.count); + sb.append(" starts"); int lastBin = 0; - boolean first = true; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { if (times.times[i] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(lastBin); sb.append('-'); sb.append(LAUNCH_TIME_BINS[i]); - sb.append('='); + sb.append("ms="); sb.append(times.times[i]); - first = false; } lastBin = LAUNCH_TIME_BINS[i]; } if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(">="); sb.append(lastBin); - sb.append('='); + sb.append("ms="); sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index 0b161d66b659..7a8d4e5ed40b 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -41,6 +41,7 @@ import android.os.RemoteException; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.format.DateFormat; import android.util.Log; @@ -57,6 +58,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsLocationProvider; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; import com.android.server.am.BatteryStatsService; @@ -97,7 +99,7 @@ public class StatusBarPolicy { private IBinder mBatteryIcon; private IconData mBatteryData; private boolean mBatteryFirst = true; - private boolean mBatteryPlugged; + private int mBatteryPlugged; private int mBatteryLevel; private int mBatteryThreshold = 0; // index into mBatteryThresholds private int[] mBatteryThresholds = new int[] { 20, 15, -1 }; @@ -106,50 +108,146 @@ public class StatusBarPolicy { private View mBatteryView; private int mBatteryViewSequence; private boolean mBatteryShowLowOnEndCall = false; + private boolean mSentLowBatteryBroadcast = false; private static final boolean SHOW_LOW_BATTERY_WARNING = true; // phone private TelephonyManager mPhone; private IBinder mPhoneIcon; + private IBinder mPhoneEvdoIcon; //***** Signal strength icons private IconData mPhoneData; + private IconData mPhoneEvdoData; //GSM/UMTS private static final int[] sSignalImages = new int[] { - com.android.internal.R.drawable.stat_sys_signal_0, - com.android.internal.R.drawable.stat_sys_signal_1, - com.android.internal.R.drawable.stat_sys_signal_2, - com.android.internal.R.drawable.stat_sys_signal_3, - com.android.internal.R.drawable.stat_sys_signal_4 - }; + com.android.internal.R.drawable.stat_sys_signal_0, + com.android.internal.R.drawable.stat_sys_signal_1, + com.android.internal.R.drawable.stat_sys_signal_2, + com.android.internal.R.drawable.stat_sys_signal_3, + com.android.internal.R.drawable.stat_sys_signal_4 + }; private static final int[] sSignalImages_r = new int[] { - com.android.internal.R.drawable.stat_sys_r_signal_0, - com.android.internal.R.drawable.stat_sys_r_signal_1, - com.android.internal.R.drawable.stat_sys_r_signal_2, - com.android.internal.R.drawable.stat_sys_r_signal_3, - com.android.internal.R.drawable.stat_sys_r_signal_4 - }; + com.android.internal.R.drawable.stat_sys_r_signal_0, + com.android.internal.R.drawable.stat_sys_r_signal_1, + com.android.internal.R.drawable.stat_sys_r_signal_2, + com.android.internal.R.drawable.stat_sys_r_signal_3, + com.android.internal.R.drawable.stat_sys_r_signal_4 + }; //CDMA private static final int[] sSignalImages_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_signal_4_cdma + com.android.internal.R.drawable.stat_sys_signal_cdma_0, + com.android.internal.R.drawable.stat_sys_signal_cdma_1, + com.android.internal.R.drawable.stat_sys_signal_cdma_2, + com.android.internal.R.drawable.stat_sys_signal_cdma_3, + com.android.internal.R.drawable.stat_sys_signal_cdma_4 }; - private static final int[] sSignalImages_r_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_r_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_r_signal_4_cdma + private static final int[] sRoamingIndicatorImages_cdma = new int[] { + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator + // 1 is Standard Roaming Indicator OFF + // TODO T: image never used, remove and put 0 instead? + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 2 is Standard Roaming Indicator FLASHING + // TODO T: image never used, remove and put 0 instead? + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 3-12 Standard ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 13-63 Reserved for Standard ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + + // 64-127 Reserved for Non Standard (Operator Specific) ERI + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64 + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0, + com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83 + + // 128-255 Reserved }; - private static final int[] sSignalImages_ra_cdma = new int[] { - com.android.internal.R.drawable.stat_sys_ra_signal_0_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_1_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_2_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_3_cdma, - com.android.internal.R.drawable.stat_sys_ra_signal_4_cdma + // EVDO + private static final int[] sSignalImages_evdo = new int[] { + com.android.internal.R.drawable.stat_sys_signal_evdo_0, + com.android.internal.R.drawable.stat_sys_signal_evdo_1, + com.android.internal.R.drawable.stat_sys_signal_evdo_2, + com.android.internal.R.drawable.stat_sys_signal_evdo_3, + com.android.internal.R.drawable.stat_sys_signal_evdo_4 }; //***** Data connection icons @@ -179,12 +277,14 @@ public class StatusBarPolicy { com.android.internal.R.drawable.stat_sys_data_in_evdo, com.android.internal.R.drawable.stat_sys_data_out_evdo, com.android.internal.R.drawable.stat_sys_data_inandout_evdo, + com.android.internal.R.drawable.stat_sys_data_dormant_evdo, }; private static final int[] sDataNetType_1xrtt = new int[] { com.android.internal.R.drawable.stat_sys_data_connected_1xrtt, com.android.internal.R.drawable.stat_sys_data_in_1xrtt, com.android.internal.R.drawable.stat_sys_data_out_1xrtt, com.android.internal.R.drawable.stat_sys_data_inandout_1xrtt, + com.android.internal.R.drawable.stat_sys_data_dormant_1xrtt, }; // Assume it's all good unless we hear otherwise. We don't always seem @@ -194,7 +294,7 @@ public class StatusBarPolicy { int mDataState = TelephonyManager.DATA_DISCONNECTED; int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; ServiceState mServiceState; - int mSignalAsu = -1; + SignalStrength mSignalStrength; // data connection private IBinder mDataIcon; @@ -249,6 +349,10 @@ public class StatusBarPolicy { private IBinder mTTYModeIcon; private IconData mTTYModeEnableIconData; + // Cdma Roaming Indicator, ERI + private IBinder mCdmaRoamingIndicatorIcon; + private IconData mCdmaRoamingIndicatorIconData; + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -306,6 +410,7 @@ public class StatusBarPolicy { private StatusBarPolicy(Context context, StatusBarService service) { mContext = context; mService = service; + mSignalStrength = new SignalStrength(); mBatteryStats = BatteryStatsService.getService(); // clock @@ -321,14 +426,21 @@ public class StatusBarPolicy { // phone_signal mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - mPhoneData = IconData.makeIcon("phone_signal", + mPhoneData = IconData.makeIcon("phone_signal", null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0); mPhoneIcon = service.addIcon(mPhoneData, null); + + // phone_evdo_signal + mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal", + null, com.android.internal.R.drawable.stat_sys_signal_evdo_0, 0, 0); + mPhoneEvdoIcon = service.addIcon(mPhoneEvdoData, null); + service.setIconVisibility(mPhoneEvdoIcon, false); + // register for phone state notifications. ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)) .listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTH + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_DATA_ACTIVITY); @@ -351,6 +463,12 @@ public class StatusBarPolicy { mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null); service.setIconVisibility(mTTYModeIcon, false); + // Cdma Roaming Indicator, ERI + mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri", + null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0); + mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null); + service.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + // bluetooth status mBluetoothData = IconData.makeIcon("bluetooth", null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0); @@ -464,7 +582,7 @@ public class StatusBarPolicy { mBatteryData.iconLevel = intent.getIntExtra("level", 0); mService.updateIcon(mBatteryIcon, mBatteryData, null); - boolean plugged = intent.getIntExtra("plugged", 0) != 0; + int plugged = intent.getIntExtra("plugged", 0); int level = intent.getIntExtra("level", -1); if (false) { Log.d(TAG, "updateBattery level=" + level @@ -475,7 +593,7 @@ public class StatusBarPolicy { + " mBatteryFirst=" + mBatteryFirst); } - boolean oldPlugged = mBatteryPlugged; + int oldPlugged = mBatteryPlugged; int oldThreshold = mBatteryThreshold; pickNextBatteryLevel(level); @@ -502,11 +620,12 @@ public class StatusBarPolicy { Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold); } - if (!plugged - && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING]) + if (plugged == 0 + && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING]) || (mBatteryThreshold > oldThreshold && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) { // Broadcast the low battery warning + mSentLowBatteryBroadcast = true; mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW)); if (SHOW_LOW_BATTERY_WARNING) { @@ -522,7 +641,11 @@ public class StatusBarPolicy { mBatteryShowLowOnEndCall = true; } } - } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) { + } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) { + if (mSentLowBatteryBroadcast == true) { + mSentLowBatteryBroadcast = false; + mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY)); + } if (SHOW_LOW_BATTERY_WARNING) { if (mLowBatteryDialog != null) { mLowBatteryDialog.dismiss(); @@ -611,6 +734,23 @@ public class StatusBarPolicy { b.setView(v); b.setIcon(android.R.drawable.ic_dialog_alert); b.setPositiveButton(android.R.string.ok, null); + + final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_NO_HISTORY); + if (intent.resolveActivity(mContext.getPackageManager()) != null) { + b.setNegativeButton(com.android.internal.R.string.battery_low_why, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mContext.startActivity(intent); + if (mLowBatteryDialog != null) { + mLowBatteryDialog.dismiss(); + } + } + }); + } AlertDialog d = b.create(); d.setOnDismissListener(mLowBatteryListener); @@ -629,7 +769,7 @@ public class StatusBarPolicy { } if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) { if (mBatteryShowLowOnEndCall) { - if (!mBatteryPlugged) { + if (mBatteryPlugged == 0) { showLowBatteryWarning(); } mBatteryShowLowOnEndCall = false; @@ -666,8 +806,8 @@ public class StatusBarPolicy { private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override - public void onSignalStrengthChanged(int asu) { - mSignalAsu = asu; + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + mSignalStrength = signalStrength; updateSignalStrength(); } @@ -675,6 +815,7 @@ public class StatusBarPolicy { public void onServiceStateChanged(ServiceState state) { mServiceState = state; updateSignalStrength(); + updateCdmaRoamingIcon(); updateDataIcon(); } @@ -696,7 +837,6 @@ public class StatusBarPolicy { updateDataIcon(); } }; - private final void updateSimState(Intent intent) { String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); @@ -723,25 +863,31 @@ public class StatusBarPolicy { updateDataIcon(); } - private final void updateSignalStrength() { - int asu = mSignalAsu; - ServiceState ss = mServiceState; + private boolean isCdma() { + return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)); + } - boolean hasService = true; - - if (ss != null) { - int state = ss.getState(); - switch (state) { + private boolean hasService() { + if (mServiceState != null) { + switch (mServiceState.getState()) { case ServiceState.STATE_OUT_OF_SERVICE: case ServiceState.STATE_POWER_OFF: - hasService = false; - break; + return false; + default: + return true; } } else { - hasService = false; + return false; } + } - if (!hasService) { + private final void updateSignalStrength() { + int iconLevel = -1; + int evdoIconLevel = -1; + int[] iconList; + int[] evdoIconList; + + if (!hasService()) { //Log.d(TAG, "updateSignalStrength: no service"); if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1) { @@ -750,48 +896,92 @@ public class StatusBarPolicy { mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null; } mService.updateIcon(mPhoneIcon, mPhoneData, null); + mService.setIconVisibility(mPhoneEvdoIcon,false); return; } - // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 - // asu = 0 (-113dB or less) is very weak - // signal, its better to show 0 bars to the user in such cases. - // asu = 99 is a special case, where the signal strength is unknown. - if (asu <= 0 || asu == 99) asu = 0; - else if (asu >= 16) asu = 4; - else if (asu >= 8) asu = 3; - else if (asu >= 4) asu = 2; - else asu = 1; - - int[] iconList; - if (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { - switch(ss.getExtendedCdmaRoaming()) { - case ServiceState.REGISTRATION_STATE_ROAMING: - iconList = this.sSignalImages_r_cdma; - break; - case ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE: - iconList = this.sSignalImages_ra_cdma; - break; - default: - iconList = this.sSignalImages_cdma; - break; + if (!isCdma()) { + int asu = mSignalStrength.getGsmSignalStrength(); + + // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 + // asu = 0 (-113dB or less) is very weak + // signal, its better to show 0 bars to the user in such cases. + // asu = 99 is a special case, where the signal strength is unknown. + if (asu <= 0 || asu == 99) iconLevel = 0; + else if (asu >= 16) iconLevel = 4; + else if (asu >= 8) iconLevel = 3; + else if (asu >= 4) iconLevel = 2; + else iconLevel = 1; + + if (mPhone.isNetworkRoaming()) { + iconList = sSignalImages_r; + } else { + iconList = sSignalImages; } - } else if (mPhone.isNetworkRoaming()) { - iconList = sSignalImages_r; } else { - iconList = sSignalImages; + iconList = this.sSignalImages_cdma; + + int cdmaDbm = mSignalStrength.getCdmaDbm(); + int cdmaEcio = mSignalStrength.getCdmaEcio(); + int levelDbm = 0; + int levelEcio = 0; + + if (cdmaDbm >= -75) levelDbm = 4; + else if (cdmaDbm >= -85) levelDbm = 3; + else if (cdmaDbm >= -95) levelDbm = 2; + else if (cdmaDbm >= -100) levelDbm = 1; + else levelDbm = 0; + + // Ec/Io are in dB*10 + if (cdmaEcio >= -90) levelEcio = 4; + else if (cdmaEcio >= -110) levelEcio = 3; + else if (cdmaEcio >= -130) levelEcio = 2; + else if (cdmaEcio >= -150) levelEcio = 1; + else levelEcio = 0; + + iconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; } - mPhoneData.iconId = iconList[asu]; + if ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0) + || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) { + // Use Evdo icon + evdoIconList = this.sSignalImages_evdo; + + int evdoEcio = mSignalStrength.getEvdoEcio(); + int evdoSnr = mSignalStrength.getEvdoSnr(); + int levelEvdoEcio = 0; + int levelEvdoSnr = 0; + + // Ec/Io are in dB*10 + if (evdoEcio >= -650) levelEvdoEcio = 4; + else if (evdoEcio >= -750) levelEvdoEcio = 3; + else if (evdoEcio >= -900) levelEvdoEcio = 2; + else if (evdoEcio >= -1050) levelEvdoEcio = 1; + else levelEvdoEcio = 0; + + if (evdoSnr > 7) levelEvdoSnr = 4; + else if (evdoSnr > 5) levelEvdoSnr = 3; + else if (evdoSnr > 3) levelEvdoSnr = 2; + else if (evdoSnr > 1) levelEvdoSnr = 1; + else levelEvdoSnr = 0; + + evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; + + mPhoneEvdoData.iconId = evdoIconList[evdoIconLevel]; + mService.updateIcon(mPhoneEvdoIcon, mPhoneEvdoData, null); + mService.setIconVisibility(mPhoneEvdoIcon,true); + } else { + mService.setIconVisibility(mPhoneEvdoIcon,false); + } + + mPhoneData.iconId = iconList[iconLevel]; mService.updateIcon(mPhoneIcon, mPhoneData, null); } private final void updateDataNetType() { int net = mPhone.getNetworkType(); - ServiceState ss = this.mServiceState; switch (net) { - case TelephonyManager.NETWORK_TYPE_EDGE: mDataIconList = sDataNetType_e; break; @@ -819,32 +1009,51 @@ public class StatusBarPolicy { int iconId; boolean visible = true; - if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { - int data = mDataState; - - int[] list = mDataIconList; - - ServiceState ss = mServiceState; - - boolean hasService = false; - - if (ss != null) { - hasService = (ss.getState() == ServiceState.STATE_IN_SERVICE); + if (!isCdma()) { + // GSM case, we have to check also the sim state + if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + default: + iconId = mDataIconList[0]; + break; + } + mDataData.iconId = iconId; + mService.updateIcon(mDataIcon, mDataData, null); + } else { + visible = false; + } + } else { + mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim; + mService.updateIcon(mDataIcon, mDataData, null); } - - if (hasService && data == TelephonyManager.DATA_CONNECTED) { + } else { + // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: - iconId = list[1]; + iconId = mDataIconList[1]; break; case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = list[2]; + iconId = mDataIconList[2]; break; case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = list[3]; + iconId = mDataIconList[3]; + break; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + iconId = mDataIconList[4]; break; default: - iconId = list[0]; + iconId = mDataIconList[0]; break; } mDataData.iconId = iconId; @@ -852,10 +1061,8 @@ public class StatusBarPolicy { } else { visible = false; } - } else { - mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim; - mService.updateIcon(mDataIcon, mDataData, null); } + long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible); @@ -863,6 +1070,7 @@ public class StatusBarPolicy { } finally { Binder.restoreCallingIdentity(ident); } + if (mDataIconVisible != visible) { mService.setIconVisibility(mDataIcon, visible); mDataIconVisible = visible; @@ -873,7 +1081,7 @@ public class StatusBarPolicy { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || - ringerMode == AudioManager.RINGER_MODE_VIBRATE; + ringerMode == AudioManager.RINGER_MODE_VIBRATE; final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER) ? com.android.internal.R.drawable.stat_sys_ringer_vibrate : com.android.internal.R.drawable.stat_sys_ringer_silent; @@ -905,7 +1113,7 @@ public class StatusBarPolicy { } else { return; } - + if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED || mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) { @@ -920,15 +1128,15 @@ public class StatusBarPolicy { private final void updateWifi(Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - + final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - + if (!enabled) { // If disabled, hide the icon. (We show icon when connected.) mService.setIconVisibility(mWifiIcon, false); } - + } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) { final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); @@ -937,9 +1145,9 @@ public class StatusBarPolicy { } } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final NetworkInfo networkInfo = (NetworkInfo) + final NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - + int iconId; if (networkInfo != null && networkInfo.isConnected()) { mIsWifiConnected = true; @@ -986,18 +1194,18 @@ public class StatusBarPolicy { if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) { // GPS is getting fixes mService.updateIcon(mGpsIcon, mGpsFixIconData, null); - mService.setIconVisibility(mGpsIcon, true); + mService.setIconVisibility(mGpsIcon, true); } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) { // GPS is off - mService.setIconVisibility(mGpsIcon, false); + mService.setIconVisibility(mGpsIcon, false); } else { // GPS is on, but not receiving fixes mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null); - mService.setIconVisibility(mGpsIcon, true); + mService.setIconVisibility(mGpsIcon, true); } } - private final void updateTTY(Intent intent) { + private final void updateTTY(Intent intent) { final String action = intent.getAction(); final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false); @@ -1007,14 +1215,63 @@ public class StatusBarPolicy { // TTY is on Log.i(TAG, "updateTTY: set TTY on"); mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null); - mService.setIconVisibility(mTTYModeIcon, true); + mService.setIconVisibility(mTTYModeIcon, true); } else { // TTY is off Log.i(TAG, "updateTTY: set TTY off"); - mService.setIconVisibility(mTTYModeIcon, false); + mService.setIconVisibility(mTTYModeIcon, false); } } + private final void updateCdmaRoamingIcon() { + if (!hasService()) { + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + if (!isCdma()) { + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + int[] iconList = sRoamingIndicatorImages_cdma; + int iconIndex = mPhone.getCdmaEriIconIndex(); + int iconMode = mPhone.getCdmaEriIconMode(); + + if (iconIndex == -1) { + Log.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update"); + return; + } + + if (iconMode == -1) { + Log.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update"); + return; + } + + if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) { + Log.d(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon"); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false); + return; + } + + switch (iconMode) { + case EriInfo.ROAMING_ICON_MODE_NORMAL: + mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex]; + mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true); + break; + case EriInfo.ROAMING_ICON_MODE_FLASH: + mCdmaRoamingIndicatorIconData.iconId = + com.android.internal.R.drawable.stat_sys_roaming_cdma_flash; + mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null); + mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true); + break; + + } + mService.updateIcon(mPhoneIcon, mPhoneData, null); + } + + private class StatusBarHandler extends Handler { @Override public void handleMessage(Message msg) { @@ -1028,6 +1285,3 @@ public class StatusBarPolicy { } } } - - - diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 5336e2707e9a..b44168abdc91 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -19,6 +19,7 @@ package com.android.server.status; import com.android.internal.R; import com.android.internal.util.CharSequences; +import android.app.ActivityManagerNative; import android.app.Dialog; import android.app.IStatusBar; import android.app.PendingIntent; @@ -118,6 +119,7 @@ public class StatusBarService extends IStatusBar.Stub public void binderDied() { Log.i(TAG, "binder died for pkg=" + pkg); disable(0, token, pkg); + token.unlinkToDeath(this, 0); } } @@ -493,6 +495,7 @@ public class StatusBarService extends IStatusBar.Stub if (what == 0 || !token.isBinderAlive()) { if (tok != null) { mDisableRecords.remove(i); + tok.token.unlinkToDeath(tok, 0); } } else { if (tok == null) { @@ -1254,6 +1257,14 @@ public class StatusBarService extends IStatusBar.Stub public void onClick(View v) { try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + } catch (RemoteException e) { + } + try { mIntent.send(); mNotificationCallbacks.onNotificationClick(mPkg, mId); } catch (PendingIntent.CanceledException e) { diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp index 695a8a3898fb..7390786fe4e4 100644 --- a/services/jni/com_android_server_SensorService.cpp +++ b/services/jni/com_android_server_SensorService.cpp @@ -14,7 +14,10 @@ * limitations under the License. */ -#define LOG_TAG "Sensors" +#define LOG_TAG "SensorService" + +#define LOG_NDEBUG 0 +#include "utils/Log.h" #include <hardware/sensors.h> @@ -36,6 +39,14 @@ static struct parcel_file_descriptor_offsets_t jmethodID mConstructor; } gParcelFileDescriptorOffsets; +static struct bundle_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jmethodID mPutIntArray; + jmethodID mPutParcelableArray; +} gBundleOffsets; + /* * The method below are not thread-safe and not intended to be */ @@ -59,21 +70,45 @@ android_init(JNIEnv *env, jclass clazz) static jobject android_open(JNIEnv *env, jclass clazz) { - int fd = sSensorDevice->open_data_source(sSensorDevice); - // new FileDescriptor() - jobject filedescriptor = env->NewObject( - gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - - if (filedescriptor != NULL) { - env->SetIntField(filedescriptor, gFileDescriptorOffsets.mDescriptor, fd); - // new ParcelFileDescriptor() - return env->NewObject(gParcelFileDescriptorOffsets.mClass, - gParcelFileDescriptorOffsets.mConstructor, - filedescriptor); + native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice); + if (!handle) { + return NULL; } - close(fd); - return NULL; + + // new Bundle() + jobject bundle = env->NewObject( + gBundleOffsets.mClass, + gBundleOffsets.mConstructor); + + if (handle->numFds > 0) { + jobjectArray fdArray = env->NewObjectArray(handle->numFds, + gParcelFileDescriptorOffsets.mClass, NULL); + for (int i = 0; i < handle->numFds; i++) { + // new FileDescriptor() + jobject fd = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]); + // new ParcelFileDescriptor() + jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fd); + env->SetObjectArrayElement(fdArray, i, pfd); + } + // bundle.putParcelableArray("fds", fdArray); + env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray, + env->NewStringUTF("fds"), fdArray); + } + + if (handle->numInts > 0) { + jintArray intArray = env->NewIntArray(handle->numInts); + env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]); + // bundle.putIntArray("ints", intArray); + env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray, + env->NewStringUTF("ints"), intArray); + } + + // delete the file handle, but don't close any file descriptors + native_handle_delete(handle); + return bundle; } static jboolean @@ -99,7 +134,7 @@ android_data_wake(JNIEnv *env, jclass clazz) static JNINativeMethod gMethods[] = { {"_sensors_control_init", "()I", (void*) android_init }, - {"_sensors_control_open", "()Landroid/os/ParcelFileDescriptor;", (void*) android_open }, + {"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open }, {"_sensors_control_activate", "(IZ)Z", (void*) android_activate }, {"_sensors_control_wake", "()I", (void*) android_data_wake }, {"_sensors_control_set_delay","(I)I", (void*) android_set_delay }, @@ -116,7 +151,15 @@ int register_android_server_SensorService(JNIEnv *env) clazz = env->FindClass("android/os/ParcelFileDescriptor"); gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", + "(Ljava/io/FileDescriptor;)V"); + + clazz = env->FindClass("android/os/Bundle"); + gBundleOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gBundleOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gBundleOffsets.mPutIntArray = env->GetMethodID(clazz, "putIntArray", "(Ljava/lang/String;[I)V"); + gBundleOffsets.mPutParcelableArray = env->GetMethodID(clazz, "putParcelableArray", + "(Ljava/lang/String;[Landroid/os/Parcelable;)V"); return jniRegisterNativeMethods(env, "com/android/server/SensorService", gMethods, NELEM(gMethods)); diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index bb5f1267f2e1..7d600f0562e3 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -62,13 +62,10 @@ public abstract class CellLocation { * @hide */ public static CellLocation newFromBundle(Bundle bundle) { - // TODO: My need to be use: Settings.Secure.getInt(mContext, Settings.Secure.CURRENT_ACTIVE_PHONE, 0)) - // instead of SystemProperties??? - - // NOTE here TelephonyManager.getDefault().getPhoneType() cannot be used since at startup - // ITelephony have not been created - if (RILConstants.CDMA_PHONE == - SystemProperties.getInt(Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.GSM_PHONE)) { + // TelephonyManager.getDefault().getPhoneType() handles the case when + // ITelephony interface is not up yet. + int type = TelephonyManager.getDefault().getPhoneType(); + if (type == RILConstants.CDMA_PHONE) { return new CdmaCellLocation(bundle); } else { return new GsmCellLocation(bundle); @@ -85,17 +82,13 @@ public abstract class CellLocation { * */ public static CellLocation getEmpty() { - // TODO: My need to be use: Settings.Secure.getInt(mContext, Settings.Secure.CURRENT_ACTIVE_PHONE, 0)) - // instead of SystemProperties??? - - // NOTE here TelephonyManager.getDefault().getPhoneType() cannot be used since at startup - // ITelephony have not been created - if (RILConstants.CDMA_PHONE == - SystemProperties.getInt(Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.GSM_PHONE)) { + // TelephonyManager.getDefault().getPhoneType() handles the case when + // ITelephony interface is not up yet. + int type = TelephonyManager.getDefault().getPhoneType(); + if (type == RILConstants.CDMA_PHONE) { return new CdmaCellLocation(); } else { return new GsmCellLocation(); } } - } diff --git a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java index 8a82966c248f..6390d8e29ec9 100644 --- a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java +++ b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java @@ -21,11 +21,11 @@ import android.text.Editable; /* * Japanese Phone number formatting rule is a bit complicated. * Here are some valid examples: - * + * * 022-229-1234 0223-23-1234 022-301-9876 015-482-7849 0154-91-3478 * 01547-5-4534 090-1234-1234 080-0123-6789 * 0800-000-9999 0570-000-000 0276-00-0000 - * + * * As you can see, there is no straight-forward rule here. * In order to handle this, a big array is prepared. */ @@ -151,14 +151,14 @@ import android.text.Editable; -100, -100, -45, -45, -100, -100, -100, -100, -100, -100, -25, -35, -35, -35, -35, -35, -35, -25, -25, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -45}; - + public static void format(Editable text) { // Here, "root" means the position of "'": // 0'3, 0'90, and +81'-90 // (dash will be deleted soon, so it is actually +81'90). int rootIndex = 1; int length = text.length(); - if (length > 3 + if (length > 3 && text.subSequence(0, 3).toString().equals("+81")) { rootIndex = 3; } else if (length < 1 || text.charAt(0) != '0') { @@ -176,10 +176,10 @@ import android.text.Editable; i++; } } - + length = text.length(); int dashposition; - + i = rootIndex; int base = 0; while (i < length) { @@ -208,7 +208,7 @@ import android.text.Editable; i++; } } - + if (length > 3 && rootIndex == 3) { text.insert(rootIndex, "-"); } diff --git a/telephony/java/android/telephony/NeighboringCellInfo.aidl b/telephony/java/android/telephony/NeighboringCellInfo.aidl index a7e709e644ce..c46433206ecc 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.aidl +++ b/telephony/java/android/telephony/NeighboringCellInfo.aidl @@ -2,16 +2,16 @@ ** ** Copyright 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 +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** http://www.apache.org/licenses/LICENSE-2.0 +** http://www.apache.org/licenses/LICENSE-2.0 ** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java index 326401a386e2..f492abd88aee 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.java +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -20,7 +20,7 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Represents the neighboring cell information, including + * Represents the neighboring cell information, including * Received Signal Strength and Cell ID location. */ public class NeighboringCellInfo implements Parcelable @@ -52,7 +52,7 @@ public class NeighboringCellInfo implements Parcelable mRssi = rssi; mCid = cid; } - + /** * Initialize the object from a parcel. */ @@ -60,12 +60,12 @@ public class NeighboringCellInfo implements Parcelable mRssi = in.readInt(); mCid = in.readInt(); } - + /** - * @return received signal strength in "asu", ranging from 0 - 31, + * @return received signal strength in "asu", ranging from 0 - 31, * or UNKNOWN_RSSI if unknown * - * For GSM, dBm = -113 + 2*asu, + * For GSM, dBm = -113 + 2*asu, * 0 means "-113 dBm or less" and 31 means "-51 dBm or greater" */ public int getRssi() { @@ -95,7 +95,7 @@ public class NeighboringCellInfo implements Parcelable @Override public String toString() { - return "["+ ((mCid == UNKNOWN_CID) ? "/" : Integer.toHexString(mCid)) + return "["+ ((mCid == UNKNOWN_CID) ? "/" : Integer.toHexString(mCid)) + " at " + ((mRssi == UNKNOWN_RSSI)? "/" : mRssi) + "]"; } @@ -105,7 +105,7 @@ public class NeighboringCellInfo implements Parcelable public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mRssi); - dest.writeInt(mCid); + dest.writeInt(mCid); } public static final Parcelable.Creator<NeighboringCellInfo> CREATOR diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index d3942fcfe2f5..dbe843176fae 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -701,17 +701,6 @@ public class PhoneNumberUtils } /** - * Note: calls extractNetworkPortion(), so do not use for - * SIM EF[ADN] style records - * - * Exceptions thrown if extractNetworkPortion(s).length() == 0 - */ - public static byte[] - networkPortionToCalledPartyBCD(String s) { - return numberToCalledPartyBCD(extractNetworkPortion(s)); - } - - /** * Return true iff the network portion of <code>address</code> is, * as far as we can tell on the device, suitable for use as an SMS * destination address. @@ -744,12 +733,25 @@ public class PhoneNumberUtils } /** + * Note: calls extractNetworkPortion(), so do not use for + * SIM EF[ADN] style records + * + * Returns null if network portion is empty. + */ + public static byte[] + networkPortionToCalledPartyBCD(String s) { + String networkPortion = extractNetworkPortion(s); + return numberToCalledPartyBCDHelper(networkPortion, false); + } + + /** * Same as {@link #networkPortionToCalledPartyBCD}, but includes a * one-byte length prefix. */ public static byte[] networkPortionToCalledPartyBCDWithLength(String s) { - return numberToCalledPartyBCDWithLength(extractNetworkPortion(s)); + String networkPortion = extractNetworkPortion(s); + return numberToCalledPartyBCDHelper(networkPortion, true); } /** @@ -761,61 +763,46 @@ public class PhoneNumberUtils */ public static byte[] numberToCalledPartyBCD(String number) { - // The extra byte required for '+' is taken into consideration while calculating - // length of ret. - int size = (hasPlus(number) ? number.length() - 1 : number.length()); - byte[] ret = new byte[(size + 1) / 2 + 1]; - - return numberToCalledPartyBCDHelper(ret, 0, number); + return numberToCalledPartyBCDHelper(number, false); } /** - * Same as {@link #numberToCalledPartyBCD}, but includes a - * one-byte length prefix. + * If includeLength is true, prepend a one-byte length value to + * the return array. */ private static byte[] - numberToCalledPartyBCDWithLength(String number) { - // The extra byte required for '+' is taken into consideration while calculating - // length of ret. - int size = (hasPlus(number) ? number.length() - 1 : number.length()); - int length = (size + 1) / 2 + 1; - byte[] ret = new byte[length + 1]; - - ret[0] = (byte) (length & 0xff); - return numberToCalledPartyBCDHelper(ret, 1, number); - } - - private static boolean - hasPlus(String s) { - return s.indexOf('+') >= 0; - } - - private static byte[] - numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) { - if (hasPlus(number)) { - number = number.replaceAll("\\+", ""); - ret[offset] = (byte) TOA_International; - } else { - ret[offset] = (byte) TOA_Unknown; + numberToCalledPartyBCDHelper(String number, boolean includeLength) { + int numberLenReal = number.length(); + int numberLenEffective = numberLenReal; + boolean hasPlus = number.indexOf('+') != -1; + if (hasPlus) numberLenEffective--; + + if (numberLenEffective == 0) return null; + + int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each. + int extraBytes = 1; // Prepended TOA byte. + if (includeLength) extraBytes++; // Optional prepended length byte. + resultLen += extraBytes; + + byte[] result = new byte[resultLen]; + + int digitCount = 0; + for (int i = 0; i < numberLenReal; i++) { + char c = number.charAt(i); + if (c == '+') continue; + int shift = ((digitCount & 0x01) == 1) ? 4 : 0; + result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift); + digitCount++; } - int size = number.length(); - int curChar = 0; - int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1); - for (int i = 1; i < 1 + countFullBytes; i++) { - ret[offset + i] - = (byte) ((charToBCD(number.charAt(curChar++))) - | (charToBCD(number.charAt(curChar++))) << 4); - } + // 1-fill any trailing odd nibble/quartet. + if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0; - // The left-over octet for odd-length phone numbers should be - // filled with 0xf. - if (countFullBytes + offset < ret.length - 1) { - ret[ret.length - 1] - = (byte) (charToBCD(number.charAt(curChar)) - | (0xf << 4)); - } - return ret; + int offset = 0; + if (includeLength) result[offset++] = (byte)(resultLen - 1); + result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown); + + return result; } /** all of 'a' up to len must match non-US trunk prefix ('0') */ diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index df6860b08365..e113680b4e69 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.CellLocation; import android.util.Log; @@ -41,16 +42,22 @@ public class PhoneStateListener { /** * Listen for changes to the network signal strength (cellular). + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * <p> - * Example: The status bar uses this to control the signal-strength - * icon. * * @see #onSignalStrengthChanged + * + * TODO: @deprecated to be deprecated by LISTEN_SIGNAL_STRENGTHS, @see #onSignalStrengthsChanged */ public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002; /** * Listen for changes to the message-waiting indicator. + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * <p> * Example: The status bar uses this to determine when to display the * voicemail icon. @@ -61,7 +68,9 @@ public class PhoneStateListener { /** * Listen for changes to the call-forwarding indicator. - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * @see #onCallForwardingIndicatorChanged */ public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; @@ -84,7 +93,9 @@ public class PhoneStateListener { /** * Listen for changes to the device call state. - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * @see #onCallStateChanged */ public static final int LISTEN_CALL_STATE = 0x00000020; @@ -99,7 +110,9 @@ public class PhoneStateListener { /** * Listen for changes to the direction of data traffic on the data * connection (cellular). - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * Example: The status bar uses this to display the appropriate * data-traffic icon. * @@ -107,6 +120,18 @@ public class PhoneStateListener { */ public static final int LISTEN_DATA_ACTIVITY = 0x00000080; + /** + * Listen for changes to the network signal strengths (cellular). + * <p> + * Example: The status bar uses this to control the signal-strength + * icon. + * + * @see #onSignalStrengthsChanged + * + * @hide + */ + public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; + public PhoneStateListener() { } @@ -129,6 +154,7 @@ public class PhoneStateListener { * @see ServiceState#STATE_IN_SERVICE * @see ServiceState#STATE_OUT_OF_SERVICE * @see ServiceState#STATE_POWER_OFF + * @deprecated, @see #onSignalStrengthsChanged */ public void onSignalStrengthChanged(int asu) { // default implementation empty @@ -185,12 +211,27 @@ public class PhoneStateListener { * @see TelephonyManager#DATA_ACTIVITY_IN * @see TelephonyManager#DATA_ACTIVITY_OUT * @see TelephonyManager#DATA_ACTIVITY_INOUT + * @see TelephonyManager#DATA_ACTIVITY_DORMANT */ public void onDataActivity(int direction) { // default implementation empty } /** + * Callback invoked when network signal strengths changes. + * + * @see ServiceState#STATE_EMERGENCY_ONLY + * @see ServiceState#STATE_IN_SERVICE + * @see ServiceState#STATE_OUT_OF_SERVICE + * @see ServiceState#STATE_POWER_OFF + * + * @hide + */ + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + // default implementation empty + } + + /** * The callback methods need to be called on the handler thread where * this object was created. If the binder did that for us it'd be nice. */ @@ -229,6 +270,9 @@ public class PhoneStateListener { public void onDataActivity(int direction) { Message.obtain(mHandler, LISTEN_DATA_ACTIVITY, direction, 0, null).sendToTarget(); } + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength).sendToTarget(); + } }; Handler mHandler = new Handler() { @@ -259,6 +303,9 @@ public class PhoneStateListener { case LISTEN_DATA_ACTIVITY: PhoneStateListener.this.onDataActivity(msg.arg1); break; + case LISTEN_SIGNAL_STRENGTHS: + PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj); + break; } } }; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 4de0954f5279..50c4d417d86a 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -99,12 +99,9 @@ public class ServiceState implements Parcelable { public static final int REGISTRATION_STATE_UNKNOWN = 4; /** @hide */ public static final int REGISTRATION_STATE_ROAMING = 5; - /** @hide */ - public static final int REGISTRATION_STATE_ROAMING_AFFILIATE = 6; private int mState = STATE_OUT_OF_SERVICE; private boolean mRoaming; - private int mExtendedCdmaRoaming; private String mOperatorAlphaLong; private String mOperatorAlphaShort; private String mOperatorNumeric; @@ -115,6 +112,8 @@ public class ServiceState implements Parcelable { private boolean mCssIndicator; private int mNetworkId; private int mSystemId; + private int mCdmaRoamingIndicator; + private int mCdmaDefaultRoamingIndicator; /** * Create a new ServiceState from a intent notifier Bundle @@ -159,7 +158,8 @@ public class ServiceState implements Parcelable { mCssIndicator = s.mCssIndicator; mNetworkId = s.mNetworkId; mSystemId = s.mSystemId; - mExtendedCdmaRoaming = s.mExtendedCdmaRoaming; + mCdmaRoamingIndicator = s.mCdmaRoamingIndicator; + mCdmaDefaultRoamingIndicator = s.mCdmaDefaultRoamingIndicator; } /** @@ -176,7 +176,8 @@ public class ServiceState implements Parcelable { mCssIndicator = (in.readInt() != 0); mNetworkId = in.readInt(); mSystemId = in.readInt(); - mExtendedCdmaRoaming = in.readInt(); + mCdmaRoamingIndicator = in.readInt(); + mCdmaDefaultRoamingIndicator = in.readInt(); } public void writeToParcel(Parcel out, int flags) { @@ -190,7 +191,8 @@ public class ServiceState implements Parcelable { out.writeInt(mCssIndicator ? 1 : 0); out.writeInt(mNetworkId); out.writeInt(mSystemId); - out.writeInt(mExtendedCdmaRoaming); + out.writeInt(mCdmaRoamingIndicator); + out.writeInt(mCdmaDefaultRoamingIndicator); } public int describeContents() { @@ -231,15 +233,25 @@ public class ServiceState implements Parcelable { return mRoaming; } - /** @hide */ - public int getExtendedCdmaRoaming(){ - return this.mExtendedCdmaRoaming; + /** + * @hide + */ + public int getCdmaRoamingIndicator(){ + return this.mCdmaRoamingIndicator; + } + + /** + * @hide + */ + public int getCdmaDefaultRoamingIndicator(){ + return this.mCdmaDefaultRoamingIndicator; } /** * Get current registered operator name in long alphanumeric format * * In GSM/UMTS, long format can be upto 16 characters long + * In CDMA, returns the ERI text, if set, otherwise the ONS * * @return long name of operator, null if unregistered or unknown */ @@ -289,7 +301,8 @@ public class ServiceState implements Parcelable { + ((null == mOperatorAlphaLong) ? 0 : mOperatorAlphaLong.hashCode()) + ((null == mOperatorAlphaShort) ? 0 : mOperatorAlphaShort.hashCode()) + ((null == mOperatorNumeric) ? 0 : mOperatorNumeric.hashCode()) - + (mExtendedCdmaRoaming)); + + mCdmaRoamingIndicator + + mCdmaDefaultRoamingIndicator); } @Override @@ -316,7 +329,9 @@ public class ServiceState implements Parcelable { && equalsHandlesNulls(mCssIndicator, s.mCssIndicator) && equalsHandlesNulls(mNetworkId, s.mNetworkId) && equalsHandlesNulls(mSystemId, s.mSystemId) - && equalsHandlesNulls(mExtendedCdmaRoaming, s.mExtendedCdmaRoaming)); + && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator) + && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, + s.mCdmaDefaultRoamingIndicator)); } @Override @@ -363,9 +378,10 @@ public class ServiceState implements Parcelable { + " " + (mIsManualNetworkSelection ? "(manual)" : "") + " " + radioTechnology + " " + (mCssIndicator ? "CSS supported" : "CSS not supported") - + "NetworkId: " + mNetworkId - + "SystemId: " + mSystemId - + "ExtendedCdmaRoaming: " + mExtendedCdmaRoaming); + + " " + mNetworkId + + " " + mSystemId + + "RoamInd: " + mCdmaRoamingIndicator + + "DefRoamInd: " + mCdmaDefaultRoamingIndicator); } public void setStateOutOfService() { @@ -379,7 +395,8 @@ public class ServiceState implements Parcelable { mCssIndicator = false; mNetworkId = -1; mSystemId = -1; - mExtendedCdmaRoaming = -1; + mCdmaRoamingIndicator = -1; + mCdmaDefaultRoamingIndicator = -1; } public void setStateOff() { @@ -393,7 +410,8 @@ public class ServiceState implements Parcelable { mCssIndicator = false; mNetworkId = -1; mSystemId = -1; - mExtendedCdmaRoaming = -1; + mCdmaRoamingIndicator = -1; + mCdmaDefaultRoamingIndicator = -1; } public void setState(int state) { @@ -404,9 +422,18 @@ public class ServiceState implements Parcelable { mRoaming = roaming; } - /** @hide */ - public void setExtendedCdmaRoaming (int roaming) { - this.mExtendedCdmaRoaming = roaming; + /** + * @hide + */ + public void setCdmaRoamingIndicator(int roaming) { + this.mCdmaRoamingIndicator = roaming; + } + + /** + * @hide + */ + public void setCdmaDefaultRoamingIndicator (int roaming) { + this.mCdmaDefaultRoamingIndicator = roaming; } public void setOperatorName(String longName, String shortName, String numeric) { @@ -415,6 +442,16 @@ public class ServiceState implements Parcelable { mOperatorNumeric = numeric; } + /** + * In CDMA mOperatorAlphaLong can be set from the ERI + * text, this is done from the CDMAPhone and not from the CdmaServiceStateTracker + * + * @hide + */ + public void setCdmaEriText(String longName) { + mOperatorAlphaLong = longName; + } + public void setIsManualSelection(boolean isManual) { mIsManualNetworkSelection = isManual; } @@ -447,7 +484,8 @@ public class ServiceState implements Parcelable { mCssIndicator = m.getBoolean("cssIndicator"); mNetworkId = m.getInt("networkId"); mSystemId = m.getInt("systemId"); - mExtendedCdmaRoaming = m.getInt("extendedCdmaRoaming"); + mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator"); + mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator"); } /** @@ -467,7 +505,8 @@ public class ServiceState implements Parcelable { m.putBoolean("cssIndicator", mCssIndicator); m.putInt("networkId", mNetworkId); m.putInt("systemId", mSystemId); - m.putInt("extendedCdmaRoaming", mExtendedCdmaRoaming); + m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator); + m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator); } //***** CDMA diff --git a/telephony/java/android/telephony/SignalStrength.aidl b/telephony/java/android/telephony/SignalStrength.aidl new file mode 100644 index 000000000000..c25411e23abe --- /dev/null +++ b/telephony/java/android/telephony/SignalStrength.aidl @@ -0,0 +1,22 @@ +/* //device/java/android/android/content/Intent.aidl +** +** Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved. +** 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.telephony; + +parcelable SignalStrength; + diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java new file mode 100644 index 000000000000..8ed0065cdd7a --- /dev/null +++ b/telephony/java/android/telephony/SignalStrength.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved. + * 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.telephony; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Contains phone signal strength related information. + * + * @hide + */ +public class SignalStrength implements Parcelable { + + static final String LOG_TAG = "PHONE"; + + private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 + private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 + private int mCdmaDbm; // This value is the RSSI value + private int mCdmaEcio; // This value is the Ec/Io + private int mEvdoDbm; // This value is the EVDO RSSI value + private int mEvdoEcio; // This value is the EVDO Ec/Io + private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio + + private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult + + /** + * Create a new SignalStrength from a intent notifier Bundle + * + * This method is used by PhoneStateIntentReceiver and maybe by + * external applications. + * + * @param m Bundle from intent notifier + * @return newly created SignalStrength + * + */ + public static SignalStrength newFromBundle(Bundle m) { + SignalStrength ret; + ret = new SignalStrength(); + ret.setFromNotifierBundle(m); + return ret; + } + + /** + * Empty constructor + * + */ + public SignalStrength() { + mGsmSignalStrength = 99; + mGsmBitErrorRate = -1; + mCdmaDbm = -1; + mCdmaEcio = -1; + mEvdoDbm = -1; + mEvdoEcio = -1; + mEvdoSnr = -1; + isGsm = true; + } + + /** + * Constructor + * + */ + public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate, + int cdmaDbm, int cdmaEcio, + int evdoDbm, int evdoEcio, int evdoSnr, boolean gsm) { + mGsmSignalStrength = gsmSignalStrength; + mGsmBitErrorRate = gsmBitErrorRate; + mCdmaDbm = cdmaDbm; + mCdmaEcio = cdmaEcio; + mEvdoDbm = evdoDbm; + mEvdoEcio = evdoEcio; + mEvdoSnr = evdoSnr; + isGsm = gsm; + } + + /** + * Copy constructors + * + * @param s Source SignalStrength + */ + public SignalStrength(SignalStrength s) { + copyFrom(s); + } + + /** + * @hide + */ + protected void copyFrom(SignalStrength s) { + mGsmSignalStrength = s.mGsmSignalStrength; + mGsmBitErrorRate = s.mGsmBitErrorRate; + mCdmaDbm = s.mCdmaDbm; + mCdmaEcio = s.mCdmaEcio; + mEvdoDbm = s.mEvdoDbm; + mEvdoEcio = s.mEvdoEcio; + mEvdoSnr = s.mEvdoSnr; + isGsm = s.isGsm; + } + + /** + * Construct a SignalStrength object from the given parcel. + */ + public SignalStrength(Parcel in) { + mGsmSignalStrength = in.readInt(); + mGsmBitErrorRate = in.readInt(); + mCdmaDbm = in.readInt(); + mCdmaEcio = in.readInt(); + mEvdoDbm = in.readInt(); + mEvdoEcio = in.readInt(); + mEvdoSnr = in.readInt(); + isGsm = (in.readInt() != 0); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mGsmSignalStrength); + out.writeInt(mGsmBitErrorRate); + out.writeInt(mCdmaDbm); + out.writeInt(mCdmaEcio); + out.writeInt(mEvdoDbm); + out.writeInt(mEvdoEcio); + out.writeInt(mEvdoSnr); + out.writeInt(isGsm ? 1 : 0); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<SignalStrength> CREATOR = new Parcelable.Creator() { + public SignalStrength createFromParcel(Parcel in) { + return new SignalStrength(in); + } + + public SignalStrength[] newArray(int size) { + return new SignalStrength[size]; + } + }; + + /** + * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS 27.007 8.5 + */ + public int getGsmSignalStrength() { + return this.mGsmSignalStrength; + } + + /** + * Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5 + */ + public int getGsmBitErrorRate() { + return this.mGsmBitErrorRate; + } + + /** + * Get the CDMA RSSI value in dBm + */ + public int getCdmaDbm() { + return this.mCdmaDbm; + } + + /** + * Get the CDMA Ec/Io value in dB*10 + */ + public int getCdmaEcio() { + return this.mCdmaEcio; + } + + /** + * Get the EVDO RSSI value in dBm + */ + public int getEvdoDbm() { + return this.mEvdoDbm; + } + + /** + * Get the EVDO Ec/Io value in dB*10 + */ + public int getEvdoEcio() { + return this.mEvdoEcio; + } + + /** + * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest. + */ + public int getEvdoSnr() { + return this.mEvdoSnr; + } + + /** + * @hide + */ + public boolean isGsm() { + return this.isGsm; + } + + /** + * @hide + */ + @Override + public int hashCode() { + return ((mGsmSignalStrength * 0x1234) + + mGsmBitErrorRate + + mCdmaDbm + mCdmaEcio + + mEvdoDbm + mEvdoEcio + mEvdoSnr + + (isGsm ? 1 : 0)); + } + + /** + * @hide + */ + @Override + public boolean equals (Object o) { + SignalStrength s; + + try { + s = (SignalStrength) o; + } catch (ClassCastException ex) { + return false; + } + + if (o == null) { + return false; + } + + return (mGsmSignalStrength == s.mGsmSignalStrength + && mGsmBitErrorRate == s.mGsmBitErrorRate + && mCdmaDbm == s.mCdmaDbm + && mCdmaEcio == s.mCdmaEcio + && mEvdoDbm == s.mEvdoDbm + && mEvdoEcio == s.mEvdoEcio + && mEvdoSnr == s.mEvdoSnr + && isGsm == s.isGsm); + } + + /** + * @hide + */ + @Override + public String toString() { + return ("SignalStrength:" + + " " + mGsmSignalStrength + + " " + mGsmBitErrorRate + + " " + mCdmaDbm + + " " + mCdmaEcio + + " " + mEvdoDbm + + " " + mEvdoEcio + + " " + mEvdoSnr + + " " + (isGsm ? "gsm" : "cdma")); + } + + /** + * Test whether two objects hold the same data values or both are null + * + * @param a first obj + * @param b second obj + * @return true if two objects equal or both are null + * @hide + */ + private static boolean equalsHandlesNulls (Object a, Object b) { + return (a == null) ? (b == null) : a.equals (b); + } + + /** + * Set SignalStrength based on intent notifier map + * + * @param m intent notifier map + * @hide + */ + private void setFromNotifierBundle(Bundle m) { + mGsmSignalStrength = m.getInt("GsmSignalStrength"); + mGsmBitErrorRate = m.getInt("GsmBitErrorRate"); + mCdmaDbm = m.getInt("CdmaDbm"); + mCdmaEcio = m.getInt("CdmaEcio"); + mEvdoDbm = m.getInt("EvdoDbm"); + mEvdoEcio = m.getInt("EvdoEcio"); + mEvdoSnr = m.getInt("EvdoSnr"); + isGsm = m.getBoolean("isGsm"); + } + + /** + * Set intent notifier Bundle based on SignalStrength + * + * @param m intent notifier Bundle + * @hide + */ + public void fillInNotifierBundle(Bundle m) { + m.putInt("GsmSignalStrength", mGsmSignalStrength); + m.putInt("GsmBitErrorRate", mGsmBitErrorRate); + m.putInt("CdmaDbm", mCdmaDbm); + m.putInt("CdmaEcio", mCdmaEcio); + m.putInt("EvdoDbm", mEvdoDbm); + m.putInt("EvdoEcio", mEvdoEcio); + m.putInt("EvdoSnr", mEvdoSnr); + m.putBoolean("isGsm", Boolean.valueOf(isGsm)); + } +} diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 9395d6625064..890f930c337b 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -22,7 +22,6 @@ import android.os.ServiceManager; import android.text.TextUtils; import com.android.internal.telephony.EncodeException; -import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.ISms; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.SmsRawData; @@ -31,14 +30,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static android.telephony.SmsMessage.ENCODING_7BIT; -import static android.telephony.SmsMessage.ENCODING_8BIT; -import static android.telephony.SmsMessage.ENCODING_16BIT; -import static android.telephony.SmsMessage.ENCODING_UNKNOWN; -import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; -import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; -import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS; -import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER; +/* + * TODO(code review): Curious question... Why are a lot of these + * methods not declared as static, since they do not seem to require + * any local object state? Assumedly this cannot be changed without + * interfering with the API... + */ /** * Manages SMS operations such as sending data, text, and pdu SMS messages. @@ -88,7 +85,7 @@ public final class SmsManager { } /** - * Divide a text message into several messages, none bigger than + * Divide a message text into several fragments, none bigger than * the maximum SMS message size. * * @param text the original message. Must not be null. @@ -96,40 +93,7 @@ public final class SmsManager { * comprise the original message */ public ArrayList<String> divideMessage(String text) { - int size = text.length(); - int[] params = SmsMessage.calculateLength(text, false); - /* SmsMessage.calculateLength returns an int[4] with: - * int[0] being the number of SMS's required, - * int[1] the number of code units used, - * int[2] is the number of code units remaining until the next message. - * int[3] is the encoding type that should be used for the message. - */ - int messageCount = params[0]; - int encodingType = params[3]; - ArrayList<String> result = new ArrayList<String>(messageCount); - - int start = 0; - int limit; - - if (messageCount > 1) { - limit = (encodingType == ENCODING_7BIT)? - MAX_USER_DATA_SEPTETS_WITH_HEADER: MAX_USER_DATA_BYTES_WITH_HEADER; - } else { - limit = (encodingType == ENCODING_7BIT)? - MAX_USER_DATA_SEPTETS: MAX_USER_DATA_BYTES; - } - - try { - while (start < size) { - int end = GsmAlphabet.findLimitIndex(text, start, limit, encodingType); - result.add(text.substring(start, end)); - start = end; - } - } - catch (EncodeException e) { - // ignore it. - } - return result; + return SmsMessage.fragmentText(text); } /** diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 3b7f4b522314..775b0343b751 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -17,11 +17,17 @@ package android.telephony; import android.os.Parcel; +import android.util.Log; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; + +import java.lang.Math; +import java.util.ArrayList; import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; @@ -43,19 +49,41 @@ public class SmsMessage { UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; } - /** Unknown encoding scheme (see TS 23.038) */ + /** + * TODO(cleanup): given that we now have more than one possible + * 7bit encoding, this result starts to look rather vague and + * maybe confusing... If this is just an indication of code unit + * size, maybe that is no problem. Otherwise, should we try to + * create an aggregate collection of GSM and CDMA encodings? CDMA + * contains a superset of the encodings we use (it does not + * support 8-bit GSM, but we also do not use that encoding + * currently)... We could get rid of these and directly reference + * the CDMA encoding definitions... + */ + + /** User data text encoding code unit size */ public static final int ENCODING_UNKNOWN = 0; - /** 7-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_7BIT = 1; - /** 8-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_8BIT = 2; - /** 16-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_16BIT = 3; /** The maximum number of payload bytes per message */ public static final int MAX_USER_DATA_BYTES = 140; /** + * TODO(cleanup): It would be more flexible and less fragile to + * rewrite this (meaning get rid of the following constant) such + * that an actual UDH is taken into consideration (meaning its + * length is measured), allowing for messages that actually + * contain other UDH fields... Hence it is actually a shame to + * extend the API with this constant. If necessary, maybe define + * the size of such a header and let the math for calculating + * max_octets/septets be done elsewhere. And, while I am griping, + * if we use the word septet, we should use the word octet in + * corresponding places, not byte... + */ + + /** * The maximum number of payload bytes per message if a user data header * is present. This assumes the header only contains the * CONCATENATED_8_BIT_REFERENCE element. @@ -221,54 +249,95 @@ public class SmsMessage { } } + /* + * TODO(cleanup): It would make some sense if the result of + * preprocessing a message to determine the proper encoding (ie + * the resulting datastructure from calculateLength) could be + * passed as an argument to the actual final encoding function. + * This would better ensure that the logic behind size calculation + * actually matched the encoding. + */ + /** * Calculates the number of SMS's required to encode the message body and - * the number of characters remaining until the next message, given the - * current encoding. + * the number of characters remaining until the next message. * - * @param messageBody the message to encode - * @param use7bitOnly if true, characters that are not part of the GSM - * alphabet are counted as a single space char. If false, a - * messageBody containing non-GSM alphabet characters is calculated - * for 16-bit encoding. - * @return an int[4] with int[0] being the number of SMS's required, int[1] - * the number of code units used, and int[2] is the number of code - * units remaining until the next message. int[3] is the encoding - * type that should be used for the message. - */ - public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) { + * @param msgBody the message to encode + * @param use7bitOnly if true, characters that are not part of the + * radio-specific 7-bit encoding are counted as single + * space chars. If false, and if the messageBody contains + * non-7-bit encodable characters, length is calculated + * using a 16-bit encoding. + * @return an int[4] with int[0] being the number of SMS's + * required, int[1] the number of code units used, and + * int[2] is the number of code units remaining until the + * next message. int[3] is an indicator of the encoding + * code unit size (see the ENCODING_* definitions in this + * class). + */ + public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) { + int activePhone = TelephonyManager.getDefault().getPhoneType(); + TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ? + com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) : + com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly); int ret[] = new int[4]; + ret[0] = ted.msgCount; + ret[1] = ted.codeUnitCount; + ret[2] = ted.codeUnitsRemaining; + ret[3] = ted.codeUnitSize; + return ret; + } + + /** + * Divide a message text into several fragments, none bigger than + * the maximum SMS message text size. + * + * @param text text, must not be null. + * @return an <code>ArrayList</code> of strings that, in order, + * comprise the original msg text + */ + public static ArrayList<String> fragmentText(String text) { + int activePhone = TelephonyManager.getDefault().getPhoneType(); + TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ? + com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) : + com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false); + + // TODO(cleanup): The code here could be rolled into the logic + // below cleanly if these MAX_* constants were defined more + // flexibly... + + int limit; + if (ted.msgCount > 1) { + limit = (ted.codeUnitSize == ENCODING_7BIT) ? + MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER; + } else { + limit = (ted.codeUnitSize == ENCODING_7BIT) ? + MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES; + } - try { - // Try GSM alphabet - int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly); - ret[1] = septets; - if (septets > MAX_USER_DATA_SEPTETS) { - ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; - ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER - - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); - } else { - ret[0] = 1; - ret[2] = MAX_USER_DATA_SEPTETS - septets; + int pos = 0; // Index in code units. + int textLen = text.length(); + ArrayList<String> result = new ArrayList<String>(ted.msgCount); + while (pos < textLen) { + int nextPos = 0; // Counts code units. + if (ted.codeUnitSize == ENCODING_7BIT) { + if (PHONE_TYPE_CDMA == activePhone) { + nextPos = pos + Math.min(limit, textLen - pos); + } else { + nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit); + } + } else { // Assume unicode. + nextPos = pos + Math.min(limit / 2, textLen - pos); } - ret[3] = ENCODING_7BIT; - } catch (EncodeException ex) { - // fall back to UCS-2 - int octets = messageBody.length() * 2; - ret[1] = messageBody.length(); - if (octets > MAX_USER_DATA_BYTES) { - // 6 is the size of the user data header - ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER - - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; - } else { - ret[0] = 1; - ret[2] = (MAX_USER_DATA_BYTES - octets)/2; + if ((nextPos <= pos) || (nextPos > textLen)) { + Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " + + nextPos + " >= " + textLen + ")"); + break; } - ret[3] = ENCODING_16BIT; + result.add(text.substring(pos, nextPos)); + pos = nextPos; } - - return ret; + return result; } /** @@ -307,7 +376,8 @@ public class SmsMessage { if (PHONE_TYPE_CDMA == activePhone) { spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested, header); + destinationAddress, message, statusReportRequested, + SmsHeader.fromByteArray(header)); } else { spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header); @@ -331,7 +401,7 @@ public class SmsMessage { if (PHONE_TYPE_CDMA == activePhone) { spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested); + destinationAddress, message, statusReportRequested, null); } else { spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested); @@ -515,9 +585,14 @@ public class SmsMessage { return mWrappedSmsMessage.getUserData(); } - /* Not part of the SDK interface and only needed by specific classes: - protected SmsHeader getUserDataHeader() - */ + /** + * Return the user data header (UDH). + * + * @hide + */ + public SmsHeader getUserDataHeader() { + return mWrappedSmsMessage.getUserDataHeader(); + } /** * Returns the raw PDU for the message. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 559542a474c7..c9dcd8b0dd2a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16,26 +16,24 @@ package android.telephony; -import com.android.internal.telephony.*; - -import java.util.ArrayList; -import java.util.List; - -import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; -import android.telephony.CellLocation; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.ITelephonyRegistry; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.TelephonyProperties; +import java.util.List; + /** * Provides access to information about the telephony services on * the device. Applications can use the methods in this class to @@ -192,8 +190,9 @@ public class TelephonyManager { /** * Returns the current location of the device. * - * <p>Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION - * ACCESS_COARSE_LOCATION}. + * <p>Requires Permission: + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION}. */ public CellLocation getCellLocation() { try { @@ -238,10 +237,10 @@ public class TelephonyManager { /** * Returns the neighboring cell information of the device. - * + * * @return List of NeighboringCellInfo or null if info unavailable. - * - * <p>Requires Permission: + * + * <p>Requires Permission: * (@link android.Manifest.permission#ACCESS_COARSE_UPDATES} */ public List<NeighboringCellInfo> getNeighboringCellInfo() { @@ -250,24 +249,25 @@ public class TelephonyManager { } catch (RemoteException ex) { } return null; - + } - + /** * No phone module + * */ public static final int PHONE_TYPE_NONE = 0; /** * GSM phone */ - public static final int PHONE_TYPE_GSM = 1; + public static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE; /** * CDMA phone * @hide */ - public static final int PHONE_TYPE_CDMA = 2; + public static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE; /** * Returns a constant indicating the device phone type. @@ -278,16 +278,41 @@ public class TelephonyManager { */ public int getPhoneType() { try{ - if(getITelephony().getActivePhoneType() == RILConstants.CDMA_PHONE) { - return PHONE_TYPE_CDMA; + ITelephony telephony = getITelephony(); + if (telephony != null) { + if(telephony.getActivePhoneType() == RILConstants.CDMA_PHONE) { + return PHONE_TYPE_CDMA; + } else { + return PHONE_TYPE_GSM; + } } else { - return PHONE_TYPE_GSM; + // This can happen when the ITelephony interface is not up yet. + return getPhoneTypeFromProperty(); } - }catch(RemoteException ex){ - return PHONE_TYPE_NONE; + } catch(RemoteException ex){ + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(); } } + + private int getPhoneTypeFromProperty() { + int type = + SystemProperties.getInt(TelephonyProperties.CURRENT_ACTIVE_PHONE, + getPhoneTypeFromNetworkType()); + return type; + } + + private int getPhoneTypeFromNetworkType() { + // When the system property CURRENT_ACTIVE_PHONE, has not been set, + // use the system property for default network type. + // This is a fail safe, and can only happen at first boot. + int mode = SystemProperties.getInt("ro.telephony.default_network", -1); + if (mode == -1) + return PHONE_TYPE_NONE; + return PhoneFactory.getPhoneType(mode); + } // // // Current Network @@ -587,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> @@ -627,7 +667,10 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return CALL_STATE_IDLE; - } + } catch (NullPointerException ex) { + // the phone process is restarting. + return CALL_STATE_IDLE; + } } /** Data connection activity: No traffic. */ @@ -639,6 +682,11 @@ public class TelephonyManager { /** Data connection activity: Currently both sending and receiving * IP PPP traffic. */ public static final int DATA_ACTIVITY_INOUT = DATA_ACTIVITY_IN | DATA_ACTIVITY_OUT; + /** + * Data connection is active, but physical link is down + * @hide + */ + public static final int DATA_ACTIVITY_DORMANT = 0x00000004; /** * Returns a constant indicating the type of activity on a data connection @@ -648,6 +696,7 @@ public class TelephonyManager { * @see #DATA_ACTIVITY_IN * @see #DATA_ACTIVITY_OUT * @see #DATA_ACTIVITY_INOUT + * @see #DATA_ACTIVITY_DORMANT */ public int getDataActivity() { try { @@ -655,7 +704,10 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return DATA_ACTIVITY_NONE; - } + } catch (NullPointerException ex) { + // the phone process is restarting. + return DATA_ACTIVITY_NONE; + } } /** Data connection state: Disconnected. IP traffic not available. */ @@ -729,4 +781,48 @@ public class TelephonyManager { // system process dead } } + + /** + * Returns the CDMA ERI icon index to display + * + * @hide + */ + public int getCdmaEriIconIndex() { + try { + return getITelephony().getCdmaEriIconIndex(); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } + } + + /** + * Returns the CDMA ERI icon mode, + * 0 - ON + * 1 - FLASHING + * + * @hide + */ + public int getCdmaEriIconMode() { + try { + return getITelephony().getCdmaEriIconMode(); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } + } + + /** + * Returns the CDMA ERI text, + * + * @hide + */ + public String getCdmaEriText() { + try { + return getITelephony().getCdmaEriText(); + } catch (RemoteException ex) { + // the phone process is restarting. + return null; + } + } } diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java index 0928ddf6bc7c..84dfca02c08d 100644 --- a/telephony/java/android/telephony/gsm/SmsMessage.java +++ b/telephony/java/android/telephony/gsm/SmsMessage.java @@ -21,6 +21,7 @@ import android.telephony.TelephonyManager; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; @@ -369,7 +370,8 @@ public class SmsMessage { if (PHONE_TYPE_CDMA == activePhone) { spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested, header); + destinationAddress, message, statusReportRequested, + SmsHeader.fromByteArray(header)); } else { spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header); @@ -395,7 +397,7 @@ public class SmsMessage { if (PHONE_TYPE_CDMA == activePhone) { spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested); + destinationAddress, message, statusReportRequested, null); } else { spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested); @@ -744,4 +746,3 @@ public class SmsMessage { } } } - diff --git a/telephony/java/android/telephony/package.html b/telephony/java/android/telephony/package.html index aee4a6ff94d9..cb2fb4999f3f 100644 --- a/telephony/java/android/telephony/package.html +++ b/telephony/java/android/telephony/package.html @@ -1,6 +1,6 @@ <HTML> <BODY> -Provides APIs for monitoring the basic phone information, such as +Provides APIs for monitoring the basic phone information, such as the network type and connection state, plus utilities for manipulating phone number strings. </BODY> diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index fbc596cd73aa..3edca6669de0 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -55,33 +55,39 @@ public abstract class BaseCommands implements CommandsInterface { protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList(); protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList(); protected Registrant mUnsolOemHookRawRegistrant; + protected RegistrantList mOtaProvisionRegistrants = new RegistrantList(); + protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList(); + protected RegistrantList mDisplayInfoRegistrants = new RegistrantList(); + protected RegistrantList mSignalInfoRegistrants = new RegistrantList(); + protected RegistrantList mNumberInfoRegistrants = new RegistrantList(); + protected RegistrantList mRedirNumInfoRegistrants = new RegistrantList(); + protected RegistrantList mLineControlInfoRegistrants = new RegistrantList(); + protected RegistrantList mT53ClirInfoRegistrants = new RegistrantList(); + protected RegistrantList mT53AudCntrlInfoRegistrants = new RegistrantList(); + protected Registrant mSMSRegistrant; protected Registrant mNITZTimeRegistrant; protected Registrant mSignalStrengthRegistrant; protected Registrant mUSSDRegistrant; protected Registrant mSmsOnSimRegistrant; - /** Registrant for handling SMS Status Reports */ protected Registrant mSmsStatusRegistrant; - /** Registrant for handling Supplementary Service Notifications */ protected Registrant mSsnRegistrant; protected Registrant mStkSessionEndRegistrant; protected Registrant mStkProCmdRegistrant; protected Registrant mStkEventRegistrant; protected Registrant mStkCallSetUpRegistrant; - /** Registrant for handling SIM/RUIM SMS storage full messages */ protected Registrant mIccSmsFullRegistrant; - /** Registrant for handling Icc Refresh notifications */ + protected Registrant mEmergencyCallbackModeRegistrant; protected Registrant mIccRefreshRegistrant; - /** Registrant for handling RING notifications */ protected Registrant mRingRegistrant; - /** Registrant for handling RESTRICTED STATE changed notification */ protected Registrant mRestrictedStateRegistrant; + protected Registrant mGsmBroadcastSmsRegistrant; - //Network Mode received from PhoneFactory + // Network Mode received from PhoneFactory protected int mNetworkMode; - //CDMA subscription received from PhoneFactory + // CDMA subscription received from PhoneFactory protected int mCdmaSubscription; - //Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. + // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. protected int mPhoneType; @@ -332,6 +338,14 @@ public abstract class BaseCommands implements CommandsInterface { mSMSRegistrant.clear(); } + public void setOnNewGsmBroadcastSms(Handler h, int what, Object obj) { + mGsmBroadcastSmsRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnNewGsmBroadcastSms(Handler h) { + mGsmBroadcastSmsRegistrant.clear(); + } + public void setOnSmsOnSim(Handler h, int what, Object obj) { mSmsOnSimRegistrant = new Registrant (h, what, obj); } @@ -424,6 +438,10 @@ public abstract class BaseCommands implements CommandsInterface { mIccRefreshRegistrant = new Registrant (h, what, obj); } + public void setEmergencyCallbackMode(Handler h, int what, Object obj) { + mEmergencyCallbackModeRegistrant = new Registrant (h, what, obj); + } + public void unSetOnIccRefresh(Handler h) { mIccRefreshRegistrant.clear(); } @@ -462,6 +480,29 @@ public abstract class BaseCommands implements CommandsInterface { mRestrictedStateRegistrant.clear(); } + public void registerForDisplayInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mDisplayInfoRegistrants.add(r); + } + + public void unregisterForDisplayInfo(Handler h) { + mDisplayInfoRegistrants.remove(h); + } + + public void registerForCallWaitingInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mCallWaitingInfoRegistrants.add(r); + } + + public void unregisterForCallWaitingInfo(Handler h) { + mCallWaitingInfoRegistrants.remove(h); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mSignalInfoRegistrants.add(r); + } + public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) { mUnsolOemHookRawRegistrant = new Registrant (h, what, obj); } @@ -470,6 +511,64 @@ public abstract class BaseCommands implements CommandsInterface { mUnsolOemHookRawRegistrant.clear(); } + public void unregisterForSignalInfo(Handler h) { + mSignalInfoRegistrants.remove(h); + } + + public void registerForCdmaOtaProvision(Handler h,int what, Object obj){ + Registrant r = new Registrant (h, what, obj); + mOtaProvisionRegistrants.add(r); + } + + public void unregisterForCdmaOtaProvision(Handler h){ + mOtaProvisionRegistrants.remove(h); + } + + public void registerForNumberInfo(Handler h,int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mNumberInfoRegistrants.add(r); + } + + public void unregisterForNumberInfo(Handler h){ + mNumberInfoRegistrants.remove(h); + } + + public void registerForRedirectedNumberInfo(Handler h,int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mRedirNumInfoRegistrants.add(r); + } + + public void unregisterForRedirectedNumberInfo(Handler h) { + mRedirNumInfoRegistrants.remove(h); + } + + public void registerForLineControlInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mLineControlInfoRegistrants.add(r); + } + + public void unregisterForLineControlInfo(Handler h) { + mLineControlInfoRegistrants.remove(h); + } + + public void registerFoT53ClirlInfo(Handler h,int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mT53ClirInfoRegistrants.add(r); + } + + public void unregisterForT53ClirInfo(Handler h) { + mT53ClirInfoRegistrants.remove(h); + } + + public void registerForT53AudioControlInfo(Handler h,int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mT53AudCntrlInfoRegistrants.add(r); + } + + public void unregisterForT53AudioControlInfo(Handler h) { + mT53AudCntrlInfoRegistrants.remove(h); + } + //***** Protected Methods /** * Store new RadioState and send notification based on the changes diff --git a/telephony/java/com/android/internal/telephony/Call.java b/telephony/java/com/android/internal/telephony/Call.java index 70471b634153..7eb9d85d6724 100644 --- a/telephony/java/com/android/internal/telephony/Call.java +++ b/telephony/java/com/android/internal/telephony/Call.java @@ -46,6 +46,13 @@ public abstract class Call { public State state = State.IDLE; + // Flag to indicate if the current calling/caller information + // is accurate. If false the information is known to be accurate. + // + // For CDMA, during call waiting/3 way, there is no network response + // if call waiting is answered, network timed out, dropped, 3 way + // merged, etc. + protected boolean isGeneric = false; /* Instance Methods */ @@ -126,6 +133,7 @@ public abstract class Call { if (t < time) { earliest = c; + time = t; } } @@ -157,10 +165,8 @@ public abstract class Call { public long getEarliestConnectTime() { - List l; long time = Long.MAX_VALUE; - - l = getConnections(); + List l = getConnections(); if (l.size() == 0) { return 0; @@ -189,4 +195,44 @@ public abstract class Call { return getState().isRinging(); } + /** + * Returns the Connection associated with this Call that was created + * last, or null if there are no Connections in this Call + */ + public Connection + getLatestConnection() { + List l = getConnections(); + if (l.size() == 0) { + return null; + } + + long time = 0; + Connection latest = null; + for (int i = 0, s = l.size() ; i < s ; i++) { + Connection c = (Connection) l.get(i); + long t = c.getCreateTime(); + + if (t > time) { + latest = c; + time = t; + } + } + + return latest; + } + + /** + * To indicate if the connection information is accurate + * or not. false means accurate. Only used for CDMA. + */ + public boolean isGeneric() { + return isGeneric; + } + + /** + * Set the generic instance variable + */ + public void setGeneric(boolean generic) { + isGeneric = generic; + } } diff --git a/telephony/java/com/android/internal/telephony/CallTracker.java b/telephony/java/com/android/internal/telephony/CallTracker.java index eb339f869b8a..9619a6696105 100644 --- a/telephony/java/com/android/internal/telephony/CallTracker.java +++ b/telephony/java/com/android/internal/telephony/CallTracker.java @@ -44,19 +44,21 @@ public abstract class CallTracker extends Handler { //***** Events - protected static final int EVENT_POLL_CALLS_RESULT = 1; - protected static final int EVENT_CALL_STATE_CHANGE = 2; - protected static final int EVENT_REPOLL_AFTER_DELAY = 3; - protected static final int EVENT_OPERATION_COMPLETE = 4; - protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; - - protected static final int EVENT_SWITCH_RESULT = 8; - protected static final int EVENT_RADIO_AVAILABLE = 9; - protected static final int EVENT_RADIO_NOT_AVAILABLE = 10; - protected static final int EVENT_CONFERENCE_RESULT = 11; - protected static final int EVENT_SEPARATE_RESULT = 12; - protected static final int EVENT_ECT_RESULT = 13; - + protected static final int EVENT_POLL_CALLS_RESULT = 1; + protected static final int EVENT_CALL_STATE_CHANGE = 2; + protected static final int EVENT_REPOLL_AFTER_DELAY = 3; + protected static final int EVENT_OPERATION_COMPLETE = 4; + protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; + + protected static final int EVENT_SWITCH_RESULT = 8; + protected static final int EVENT_RADIO_AVAILABLE = 9; + protected static final int EVENT_RADIO_NOT_AVAILABLE = 10; + protected static final int EVENT_CONFERENCE_RESULT = 11; + protected static final int EVENT_SEPARATE_RESULT = 12; + protected static final int EVENT_ECT_RESULT = 13; + protected static final int EVENT_EXIT_ECM_RESPONSE_CDMA = 14; + protected static final int EVENT_CALL_WAITING_INFO_CDMA = 15; + protected static final int EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA = 16; protected void pollCallsWhenSafe() { needsPoll = true; diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index da53e15e93ed..afc8b62ba334 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -70,17 +70,23 @@ public class CallerInfo { */ public String name; public String phoneNumber; + + public String cnapName; + public int numberPresentation; + public int namePresentation; + public boolean contactExists; + public String phoneLabel; /* Split up the phoneLabel into number type and label name */ public int numberType; public String numberLabel; - + public int photoResource; public long person_id; public boolean needUpdate; public Uri contactRefUri; - - // fields to hold individual contact preference data, + + // fields to hold individual contact preference data, // including the send to voicemail flag and the ringtone // uri reference. public Uri contactRingtoneUri; @@ -110,7 +116,7 @@ public class CallerInfo { * number. The returned CallerInfo is null if no number is supplied. */ public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) { - + CallerInfo info = new CallerInfo(); info.photoResource = 0; info.phoneLabel = null; @@ -118,9 +124,10 @@ public class CallerInfo { info.numberLabel = null; info.cachedPhoto = null; info.isCachedPhotoCurrent = false; - + info.contactExists = false; + if (Config.LOGV) Log.v(TAG, "construct callerInfo from cursor"); - + if (cursor != null) { if (cursor.moveToFirst()) { @@ -137,7 +144,7 @@ public class CallerInfo { if (columnIndex != -1) { info.phoneNumber = cursor.getString(columnIndex); } - + // Look for the label/type combo columnIndex = cursor.getColumnIndex(Phones.LABEL); if (columnIndex != -1) { @@ -161,7 +168,7 @@ public class CallerInfo { info.person_id = cursor.getLong(columnIndex); } } - + // look for the custom ringtone, create from the string stored // in the database. columnIndex = cursor.getColumnIndex(People.CUSTOM_RINGTONE); @@ -174,8 +181,9 @@ public class CallerInfo { // look for the send to voicemail flag, set it to true only // under certain circumstances. columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL); - info.shouldSendToVoicemail = (columnIndex != -1) && + info.shouldSendToVoicemail = (columnIndex != -1) && ((cursor.getInt(columnIndex)) == 1); + info.contactExists = true; } cursor.close(); } @@ -186,7 +194,7 @@ public class CallerInfo { return info; } - + /** * getCallerInfo given a URI, look up in the call-log database * for the uri unique key. @@ -196,11 +204,11 @@ public class CallerInfo { * number. The returned CallerInfo is null if no number is supplied. */ public static CallerInfo getCallerInfo(Context context, Uri contactRef) { - - return getCallerInfo(context, contactRef, + + return getCallerInfo(context, contactRef, context.getContentResolver().query(contactRef, null, null, null, null)); } - + /** * getCallerInfo given a phone number, look up in the call-log database * for the matching caller id info. @@ -216,13 +224,13 @@ public class CallerInfo { return null; } else { // Change the callerInfo number ONLY if it is an emergency number - // or if it is the voicemail number. If it is either, take a + // or if it is the voicemail number. If it is either, take a // shortcut and skip the query. if (PhoneNumberUtils.isEmergencyNumber(number)) { CallerInfo ci = new CallerInfo(); // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). + // comments at the top of CallerInfo class). ci.phoneNumber = context.getString( com.android.internal.R.string.emergency_call_dialog_number_for_display); return ci; @@ -233,14 +241,14 @@ public class CallerInfo { CallerInfo ci = new CallerInfo(); // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). + // comments at the top of CallerInfo class). ci.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag(); // TODO: FIND ANOTHER ICON //info.photoResource = android.R.drawable.badge_voicemail; return ci; } } catch (SecurityException ex) { - // Don't crash if this process doesn't have permission to + // Don't crash if this process doesn't have permission to // retrieve VM number. It's still allowed to look up caller info. // But don't try it again. sSkipVmCheck = true; @@ -249,16 +257,16 @@ public class CallerInfo { } Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, - Uri.encode(number)); - + Uri.encode(number)); + CallerInfo info = getCallerInfo(context, contactUri); - // if no query results were returned with a viable number, - // fill in the original number value we used to query with. + // if no query results were returned with a viable number, + // fill in the original number value we used to query with. if (TextUtils.isEmpty(info.phoneNumber)) { info.phoneNumber = number; } - + return info; } @@ -270,9 +278,9 @@ public class CallerInfo { * @param number a phone number. * @return if the number belongs to a contact, the contact's name is * returned; otherwise, the number itself is returned. - * - * TODO NOTE: This MAY need to refer to the Asynchronous Query API - * [startQuery()], instead of getCallerInfo, but since it looks like + * + * TODO NOTE: This MAY need to refer to the Asynchronous Query API + * [startQuery()], instead of getCallerInfo, but since it looks like * it is only being used by the provider calls in the messaging app: * 1. android.provider.Telephony.Mms.getDisplayAddress() * 2. android.provider.Telephony.Sms.getDisplayAddress() @@ -302,5 +310,4 @@ public class CallerInfo { return null; } } -} - +} diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index 04da9f719332..f81f42ad6c99 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -35,10 +35,10 @@ import android.util.Log; */ public class CallerInfoAsyncQuery { - + private static final boolean DBG = false; private static final String LOG_TAG = "PHONE"; - + private static final int EVENT_NEW_QUERY = 1; private static final int EVENT_ADD_LISTENER = 2; private static final int EVENT_END_OF_QUEUE = 3; @@ -55,24 +55,24 @@ public class CallerInfoAsyncQuery { */ public interface OnQueryCompleteListener { /** - * Called when the query is complete. - */ + * Called when the query is complete. + */ public void onQueryComplete(int token, Object cookie, CallerInfo ci); } - - + + /** * Wrap the cookie from the WorkerArgs with additional information needed by our - * classes. + * classes. */ private static final class CookieWrapper { public OnQueryCompleteListener listener; public Object cookie; public int event; public String number; - } - - + } + + /** * Simple exception used to communicate problems with the query pool. */ @@ -81,12 +81,12 @@ public class CallerInfoAsyncQuery { super(error); } } - + /** * Our own implementation of the AsyncQueryHandler. */ private class CallerInfoAsyncQueryHandler extends AsyncQueryHandler { - + /** * The information relevant to each CallerInfo query. Each query may have multiple * listeners, so each AsyncCursorInfo is associated with 2 or more CookieWrapper @@ -96,20 +96,20 @@ public class CallerInfoAsyncQuery { private Context mQueryContext; private Uri mQueryUri; private CallerInfo mCallerInfo; - + /** * Our own query worker thread. - * + * * This thread handles the messages enqueued in the looper. The normal sequence * of events is that a new query shows up in the looper queue, followed by 0 or * more add listener requests, and then an end request. Of course, these requests * can be interlaced with requests from other tokens, but is irrelevant to this * handler since the handler has no state. - * + * * Note that we depend on the queue to keep things in order; in other words, the - * looper queue must be FIFO with respect to input from the synchronous startQuery + * looper queue must be FIFO with respect to input from the synchronous startQuery * calls and output to this handleMessage call. - * + * * This use of the queue is required because CallerInfo objects may be accessed * multiple times before the query is complete. All accesses (listeners) must be * queued up and informed in order when the query is complete. @@ -123,22 +123,22 @@ public class CallerInfoAsyncQuery { public void handleMessage(Message msg) { WorkerArgs args = (WorkerArgs) msg.obj; CookieWrapper cw = (CookieWrapper) args.cookie; - + if (cw == null) { // Normally, this should never be the case for calls originating // from within this code. - // However, if there is any code that this Handler calls (such as in + // However, if there is any code that this Handler calls (such as in // super.handleMessage) that DOES place unexpected messages on the // queue, then we need pass these messages on. - if (DBG) log("Unexpected command (CookieWrapper is null): " + msg.what + + if (DBG) log("Unexpected command (CookieWrapper is null): " + msg.what + " ignored by CallerInfoWorkerHandler, passing onto parent."); - + super.handleMessage(msg); } else { - - if (DBG) log("Processing event: " + cw.event + " token (arg1): " + msg.arg1 + + + if (DBG) log("Processing event: " + cw.event + " token (arg1): " + msg.arg1 + " command: " + msg.what + " query URI: " + args.uri); - + switch (cw.event) { case EVENT_NEW_QUERY: //start the sql command. @@ -148,7 +148,7 @@ public class CallerInfoAsyncQuery { // shortcuts to avoid query for recognized numbers. case EVENT_EMERGENCY_NUMBER: case EVENT_VOICEMAIL_NUMBER: - + case EVENT_ADD_LISTENER: case EVENT_END_OF_QUEUE: // query was already completed, so just send the reply. @@ -157,17 +157,17 @@ public class CallerInfoAsyncQuery { Message reply = args.handler.obtainMessage(msg.what); reply.obj = args; reply.arg1 = msg.arg1; - + reply.sendToTarget(); - + break; default: } } } } - - + + /** * Asynchronous query handler class for the contact / callerinfo object. */ @@ -182,29 +182,29 @@ public class CallerInfoAsyncQuery { /** * Overrides onQueryComplete from AsyncQueryHandler. - * + * * This method takes into account the state of this class; we construct the CallerInfo * object only once for each set of listeners. When the query thread has done its work - * and calls this method, we inform the remaining listeners in the queue, until we're - * out of listeners. Once we get the message indicating that we should expect no new - * listeners for this CallerInfo object, we release the AsyncCursorInfo back into the + * and calls this method, we inform the remaining listeners in the queue, until we're + * out of listeners. Once we get the message indicating that we should expect no new + * listeners for this CallerInfo object, we release the AsyncCursorInfo back into the * pool. */ @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { if (DBG) log("query complete for token: " + token); - + //get the cookie and notify the listener. CookieWrapper cw = (CookieWrapper) cookie; if (cw == null) { // Normally, this should never be the case for calls originating // from within this code. - // However, if there is any code that calls this method, we should + // However, if there is any code that calls this method, we should // check the parameters to make sure they're viable. if (DBG) log("Cookie is null, ignoring onQueryComplete() request."); return; } - + if (cw.event == EVENT_END_OF_QUEUE) { release(); return; @@ -216,16 +216,16 @@ public class CallerInfoAsyncQuery { throw new QueryPoolException ("Bad context or query uri, or CallerInfoAsyncQuery already released."); } - + // adjust the callerInfo data as needed, and only if it was set from the // initial query request. // Change the callerInfo number ONLY if it is an emergency number or the - // voicemail number, and adjust other data (including photoResource) + // voicemail number, and adjust other data (including photoResource) // accordingly. if (cw.event == EVENT_EMERGENCY_NUMBER) { mCallerInfo = new CallerInfo(); // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). + // comments at the top of CallerInfo class). mCallerInfo.phoneNumber = mQueryContext.getString(com.android.internal .R.string.emergency_call_dialog_number_for_display); mCallerInfo.photoResource = com.android.internal.R.drawable.picture_emergency; @@ -234,8 +234,8 @@ public class CallerInfoAsyncQuery { mCallerInfo = new CallerInfo(); try { // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). - mCallerInfo.phoneNumber = + // comments at the top of CallerInfo class). + mCallerInfo.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag(); } catch (SecurityException ex) { // Should never happen: if this process does not have @@ -243,80 +243,80 @@ public class CallerInfoAsyncQuery { // permission to retrieve VM number and would not generate // an EVENT_VOICEMAIL_NUMBER. But if it happens, don't crash. } - } else { + } else { mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor); // Use the number entered by the user for display. if (!TextUtils.isEmpty(cw.number)) { mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number); } } - + if (DBG) log("constructing CallerInfo object for token: " + token); - + //notify that we can clean up the queue after this. CookieWrapper endMarker = new CookieWrapper(); endMarker.event = EVENT_END_OF_QUEUE; startQuery (token, endMarker, null, null, null, null, null); } - + //notify the listener that the query is complete. if (cw.listener != null) { - if (DBG) log("notifying listener: " + cw.listener.getClass().toString() + + if (DBG) log("notifying listener: " + cw.listener.getClass().toString() + " for token: " + token); cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo); } } } - + /** * Private constructor for factory methods. */ private CallerInfoAsyncQuery() { } - + /** * Factory method to start query with a Uri query spec */ - public static CallerInfoAsyncQuery startQuery(int token, Context context, Uri contactRef, + public static CallerInfoAsyncQuery startQuery(int token, Context context, Uri contactRef, OnQueryCompleteListener listener, Object cookie) { - + CallerInfoAsyncQuery c = new CallerInfoAsyncQuery(); c.allocate(context, contactRef); if (DBG) log("starting query for URI: " + contactRef + " handler: " + c.toString()); - + //create cookieWrapper, start query CookieWrapper cw = new CookieWrapper(); cw.listener = listener; cw.cookie = cookie; cw.event = EVENT_NEW_QUERY; - + c.mHandler.startQuery (token, cw, contactRef, null, null, null, null); - + return c; } - + /** * Factory method to start query with a number */ - public static CallerInfoAsyncQuery startQuery(int token, Context context, String number, + public static CallerInfoAsyncQuery startQuery(int token, Context context, String number, OnQueryCompleteListener listener, Object cookie) { //contruct the URI object and start Query. Uri contactRef = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, number); - + CallerInfoAsyncQuery c = new CallerInfoAsyncQuery(); c.allocate(context, contactRef); if (DBG) log("starting query for number: " + number + " handler: " + c.toString()); - + //create cookieWrapper, start query CookieWrapper cw = new CookieWrapper(); cw.listener = listener; cw.cookie = cookie; cw.number = number; - // check to see if these are recognized numbers, and use shortcuts if we can. + // check to see if these are recognized numbers, and use shortcuts if we can. if (PhoneNumberUtils.isEmergencyNumber(number)) { cw.event = EVENT_EMERGENCY_NUMBER; } else { @@ -325,13 +325,13 @@ public class CallerInfoAsyncQuery { try { vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); } catch (SecurityException ex) { - // Don't crash if this process doesn't have permission to + // Don't crash if this process doesn't have permission to // retrieve VM number. It's still allowed to look up caller info. // But don't try it again. sSkipVmCheck = true; } } - if (PhoneNumberUtils.compare(number, vmNumber)) { + if (PhoneNumberUtils.compare(number, vmNumber)) { cw.event = EVENT_VOICEMAIL_NUMBER; } else { cw.event = EVENT_NEW_QUERY; @@ -339,24 +339,24 @@ public class CallerInfoAsyncQuery { } c.mHandler.startQuery (token, cw, contactRef, null, null, null, null); - + return c; } - + /** * Method to add listeners to a currently running query */ public void addQueryListener(int token, OnQueryCompleteListener listener, Object cookie) { - if (DBG) log("adding listener to query: " + mHandler.mQueryUri + " handler: " + + if (DBG) log("adding listener to query: " + mHandler.mQueryUri + " handler: " + mHandler.toString()); - + //create cookieWrapper, add query request to end of queue. CookieWrapper cw = new CookieWrapper(); cw.listener = listener; cw.cookie = cookie; cw.event = EVENT_ADD_LISTENER; - + mHandler.startQuery (token, cw, null, null, null, null, null); } @@ -382,12 +382,12 @@ public class CallerInfoAsyncQuery { mHandler.mCallerInfo = null; mHandler = null; } - + /** * static logging method */ private static void log(String msg) { Log.d(LOG_TAG, msg); - } + } } diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index 5a1bb7e72fc7..25c512e385f0 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -16,6 +16,8 @@ package com.android.internal.telephony; +import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; + import android.os.Message; import android.os.Handler; @@ -147,6 +149,14 @@ public interface CommandsInterface { static final int SIM_REFRESH_INIT = 1; // SIM initialized; reload all static final int SIM_REFRESH_RESET = 2; // SIM reset; may be locked + // GSM SMS fail cause for acknowledgeLastIncomingSMS. From TS 23.040, 9.2.3.22. + static final int GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED = 0xD3; + static final int GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR = 0xFF; + + // CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S00005, 6.5.2.125. + static final int CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE = 35; + static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39; + //***** Methods RadioState getRadioState(); @@ -348,12 +358,12 @@ public interface CommandsInterface { void unSetOnCallRing(Handler h); /** - * Sets the handler for RESTRICTED_STATE changed notification, + * Sets the handler for RESTRICTED_STATE changed notification, * eg, for Domain Specific Access Control * unlike the register* methods, there's only one signal strength handler - * - * AsyncResult.result is an int[1] - * response.obj.result[0] is a bitmask of RIL_RESTRICTED_STATE_* values + * + * AsyncResult.result is an int[1] + * response.obj.result[0] is a bitmask of RIL_RESTRICTED_STATE_* values */ void setOnRestrictedStateChanged(Handler h, int what, Object obj); @@ -424,6 +434,104 @@ public interface CommandsInterface { void setSuppServiceNotifications(boolean enable, Message result); //void unSetSuppServiceNotifications(Handler h); + /** + * Sets the handler for Event Notifications for CDMA Display Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForDisplayInfo(Handler h, int what, Object obj); + void unregisterForDisplayInfo(Handler h); + + /** + * Sets the handler for Event Notifications for CallWaiting Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForCallWaitingInfo(Handler h, int what, Object obj); + void unregisterForCallWaitingInfo(Handler h); + + /** + * Sets the handler for Event Notifications for Signal Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForSignalInfo(Handler h, int what, Object obj); + void unregisterForSignalInfo(Handler h); + + /** + * Registers the handler for CDMA number information record + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForNumberInfo(Handler h, int what, Object obj); + void unregisterForNumberInfo(Handler h); + + /** + * Registers the handler for CDMA redirected number Information record + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForRedirectedNumberInfo(Handler h, int what, Object obj); + void unregisterForRedirectedNumberInfo(Handler h); + + /** + * Registers the handler for CDMA line control information record + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForLineControlInfo(Handler h, int what, Object obj); + void unregisterForLineControlInfo(Handler h); + + /** + * Registers the handler for CDMA T53 CLIR information record + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerFoT53ClirlInfo(Handler h, int what, Object obj); + void unregisterForT53ClirInfo(Handler h); + + /** + * Registers the handler for CDMA T53 audio control information record + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForT53AudioControlInfo(Handler h, int what, Object obj); + void unregisterForT53AudioControlInfo(Handler h); + + /** + * Fires on if Modem enters Emergency Callback mode + */ + void setEmergencyCallbackMode(Handler h, int what, Object obj); + + /** + * Fires on any CDMA OTA provision status change + */ + void registerForCdmaOtaProvision(Handler h,int what, Object obj); + void unregisterForCdmaOtaProvision(Handler h); /** * Returns current ICC status. @@ -516,7 +624,7 @@ public interface CommandsInterface { * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj - * ar.result contains a List of PDPContextState + * ar.result contains a List of DataCallState * @deprecated */ void getPDPContextList(Message result); @@ -526,7 +634,7 @@ public interface CommandsInterface { * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj - * ar.result contains a List of PDPContextState + * ar.result contains a List of DataCallState */ void getDataCallList(Message result); @@ -767,6 +875,12 @@ public interface CommandsInterface { */ void stopDtmf(Message result); + /** + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void sendBurstDtmf(String dtmfString, Message result); /** * smscPDU is smsc address in PDU form GSM BCD format prefixed @@ -833,9 +947,9 @@ public interface CommandsInterface { void setRadioPower(boolean on, Message response); - void acknowledgeLastIncomingSMS(boolean success, Message response); + void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response); - void acknowledgeLastIncomingCdmaSms(boolean success, Message response); + void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response); /** * parameters equivilient to 27.007 AT+CRSM command @@ -1038,6 +1152,20 @@ public interface CommandsInterface { */ void setSmscAddress(String address, Message result); + /** + * Indicates whether there is storage available for new SMS messages. + * @param available true if storage is available + * @param result callback message + */ + void reportSmsMemoryStatus(boolean available, Message result); + + /** + * Indicates to the vendor ril that StkService is running + * rand is eady to receive RIL_UNSOL_STK_XXXX commands. + * + * @param result callback message + */ + void reportStkServiceIsRunning(Message result); void invokeOemRilRequestRaw(byte[] data, Message response); @@ -1074,6 +1202,31 @@ public interface CommandsInterface { */ public void handleCallSetupRequestFromSim(boolean accept, Message response); + /** + * Activate or deactivate cell broadcast SMS for GSM. + * + * @param activate + * true = activate, false = deactivate + * @param result Callback message is empty on completion + */ + public void setGsmBroadcastActivation(boolean activate, Message result); + + /** + * Configure cell broadcast SMS for GSM. + * + * @param response Callback message is empty on completion + */ + public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response); + + /** + * Query the current configuration of cell broadcast SMS of GSM. + * + * @param response + * Callback message contains the configuration from the modem + * on completion + */ + public void getGsmBroadcastConfig(Message response); + //***** new Methods for CDMA support /** @@ -1087,13 +1240,12 @@ public interface CommandsInterface { public void getDeviceIdentity(Message response); /** - * Request the device IMSI_M / MDN / AH_SID / H_SID / H_NID. + * Request the device MDN / H_SID / H_NID / MIN. * "response" is const char ** - * [0] is IMSI_M if CDMA subscription is available - * [1] is MDN if CDMA subscription is available - * [2] is AH_SID (Analog Home SID) if CDMA subscription - * [3] is H_SID (Home SID) if CDMA subscription is available - * [4] is H_NID (Home SID) if CDMA subscription is available + * [0] is MDN if CDMA subscription is available + * [1] is H_SID (Home SID) if CDMA subscription is available + * [2] is H_NID (Home NID) if CDMA subscription is available + * [3] is MIN (10 digits, MIN2+MIN1) if CDMA subscription is available */ public void getCDMASubscription(Message response); @@ -1133,7 +1285,7 @@ public interface CommandsInterface { * @param enable is true to enable, false to disable * @param response is callback message */ - void setTTYModeEnabled(boolean enable, Message response); + void setTTYMode(int ttyMode, Message response); /** * Query the TTY mode for the CDMA phone @@ -1142,7 +1294,7 @@ public interface CommandsInterface { * * @param response is callback message */ - void queryTTYModeEnabled(Message response); + void queryTTYMode(Message response); /** * Setup a packet data connection On successful completion, the result @@ -1181,14 +1333,14 @@ public interface CommandsInterface { public void deactivateDataCall(int cid, Message result); /** - * Activate or deactivate cell broadcast SMS. + * Activate or deactivate cell broadcast SMS for CDMA. * * @param activate - * 0 = activate, 1 = deactivate + * true = activate, false = deactivate * @param result * Callback message is empty on completion */ - public void activateCdmaBroadcastSms(int activate, Message result); + public void setCdmaBroadcastActivation(boolean activate, Message result); /** * Configure cdma cell broadcast SMS. @@ -1196,6 +1348,7 @@ public interface CommandsInterface { * @param result * Callback message is empty on completion */ + // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig public void setCdmaBroadcastConfig(int[] configValuesArray, Message result); /** @@ -1205,4 +1358,12 @@ public interface CommandsInterface { * Callback message contains the configuration from the modem on completion */ public void getCdmaBroadcastConfig(Message result); + + /** + * Requests the radio's system selection module to exit emergency callback mode. + * This function should only be called from CDMAPHone.java. + * + * @param response callback message + */ + public void exitEmergencyCallbackMode(Message response); } diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index 86ceb89ee5bd..92f6cb822163 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -27,27 +27,36 @@ public abstract class Connection { public static int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network public static int PRESENTATION_PAYPHONE = 4; // show pay phone info - + public enum DisconnectCause { - NOT_DISCONNECTED, /* has not yet disconnected */ - INCOMING_MISSED, /* an incoming call that was missed and never answered */ - NORMAL, /* normal; remote */ - LOCAL, /* normal; local hangup */ - BUSY, /* outgoing call to busy line */ - CONGESTION, /* outgoing call to congested network */ - MMI, /* not presently used; dial() returns null */ - INVALID_NUMBER, /* invalid dial string */ + NOT_DISCONNECTED, /* has not yet disconnected */ + INCOMING_MISSED, /* an incoming call that was missed and never answered */ + NORMAL, /* normal; remote */ + LOCAL, /* normal; local hangup */ + BUSY, /* outgoing call to busy line */ + CONGESTION, /* outgoing call to congested network */ + MMI, /* not presently used; dial() returns null */ + INVALID_NUMBER, /* invalid dial string */ LOST_SIGNAL, - LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ - INCOMING_REJECTED, /* an incoming call that was rejected */ - POWER_OFF, /* radio is turned off explicitly */ - OUT_OF_SERVICE, /* out of service */ - ICC_ERROR, /* No ICC, ICC locked, or other ICC error */ - CALL_BARRED, /* call was blocked by call barrring */ - FDN_BLOCKED, /* call was blocked by fixed dial number */ - CS_RESTRICTED, /* call was blocked by restricted all voice access */ - CS_RESTRICTED_NORMAL,/* call was blocked by restricted normal voice access */ - CS_RESTRICTED_EMERGENCY/* call was blocked by restricted emergency voice access */ + LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ + INCOMING_REJECTED, /* an incoming call that was rejected */ + POWER_OFF, /* radio is turned off explicitly */ + OUT_OF_SERVICE, /* out of service */ + ICC_ERROR, /* No ICC, ICC locked, or other ICC error */ + CALL_BARRED, /* call was blocked by call barrring */ + FDN_BLOCKED, /* call was blocked by fixed dial number */ + CS_RESTRICTED, /* call was blocked by restricted all voice access */ + CS_RESTRICTED_NORMAL, /* call was blocked by restricted normal voice access */ + CS_RESTRICTED_EMERGENCY, /* call was blocked by restricted emergency voice access */ + CDMA_LOCKED_UNTIL_POWER_CYCLE, /* MS is locked until next power cycle */ + CDMA_DROP, + CDMA_INTERCEPT, /* INTERCEPT order received, MS state idle entered */ + CDMA_REORDER, /* MS has been redirected, call is cancelled */ + CDMA_SO_REJECT, /* service option rejection */ + CDMA_RETRY_ORDER, /* requeseted service is rejected, retry delay is set */ + CDMA_ACCESS_FAILURE, + CDMA_PREEMPTED, + CDMA_NOT_EMERGENCY /* not an emergency call */ } Object userData; @@ -64,6 +73,31 @@ public abstract class Connection { public abstract String getAddress(); /** + * Gets cdma CNAP name associated with connection + * @return cnap name or null if unavailable + */ + public String getCnapName() { + return null; + } + + /** + * Get orignal dial string + * @return orignal dial string or null if unavailable + */ + public String getOrigDialString(){ + return null; + } + + /** + * Gets cdma CNAP presentation associated with connection + * @return cnap name or null if unavailable + */ + + public int getCnapNamePresentation() { + return 0; + }; + + /** * @return Call that owns this Connection, or null if none */ public abstract Call getCall(); @@ -195,8 +229,14 @@ public abstract class Connection { WILD, /* The post dial string playback is waiting for a call to proceedAfterWildChar() */ COMPLETE, /* The post dial string playback is complete */ - CANCELLED /* The post dial string playback was cancelled + CANCELLED, /* The post dial string playback was cancelled with cancelPostDial() */ + PAUSE /* The post dial string playback is pausing for a + call to processNextPostDialChar*/ + } + + public void clearUserData(){ + userData = null; } public abstract PostDialState getPostDialState(); @@ -220,8 +260,8 @@ public abstract class Connection { /** * Cancel any post */ - public abstract void cancelPostDial(); - + public abstract void cancelPostDial(); + /** * Returns the caller id presentation type for incoming and waiting calls * @return one of PRESENTATION_* diff --git a/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java b/telephony/java/com/android/internal/telephony/DataCallState.java index 31cdacf53190..d0f3d24f53b3 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java +++ b/telephony/java/com/android/internal/telephony/DataCallState.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved. + * 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. @@ -14,25 +15,18 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm; +package com.android.internal.telephony; -/** - * {@hide} - */ -public class PDPContextState { +public class DataCallState { public int cid; public int active; public String type; public String apn; public String address; + @Override public String toString() { - return "com.android.internal.telephony.gsm.PDPContextState: {" + - " cid: " + cid + - ", active: " + active + - ", type: " + type + - ", apn: " + apn + - ", address: " + address + - " }"; + return "DataCallState: {" + " cid: " + cid + ", active: " + active + ", type: " + type + + ", apn: " + apn + ", address: " + address + " }"; } } diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 6e9d1abeb8e4..7809fed62d25 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -55,57 +55,83 @@ public abstract class DataConnection extends Handler { public enum FailCause { NONE, - BAD_APN, - BAD_PAP_SECRET, - BARRED, + OPERATOR_BARRED, + INSUFFICIENT_RESOURCES, + MISSING_UKNOWN_APN, + UNKNOWN_PDP_ADDRESS, USER_AUTHENTICATION, + ACTIVATION_REJECT_GGSN, + ACTIVATION_REJECT_UNSPECIFIED, SERVICE_OPTION_NOT_SUPPORTED, SERVICE_OPTION_NOT_SUBSCRIBED, - SIM_LOCKED, - RADIO_OFF, - NO_SIGNAL, - NO_DATA_PLAN, + SERVICE_OPTION_OUT_OF_ORDER, + NSAPI_IN_USE, + PROTOCOL_ERRORS, + REGISTRATION_FAIL, + GPRS_REGISTRATION_FAIL, + UNKNOWN, + RADIO_NOT_AVAILABLE, - SUSPENED_TEMPORARY, - RADIO_ERROR_RETRY, - UNKNOWN; + RADIO_ERROR_RETRY; public boolean isPermanentFail() { - return (this == RADIO_OFF); + return (this == OPERATOR_BARRED) || (this == MISSING_UKNOWN_APN) || + (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || + (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || + (this == SERVICE_OPTION_NOT_SUPPORTED) || + (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) || + (this == PROTOCOL_ERRORS); + } + + public boolean isEventLoggable() { + return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || + (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || + (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || + (this == SERVICE_OPTION_NOT_SUBSCRIBED) || + (this == SERVICE_OPTION_NOT_SUPPORTED) || + (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || + (this == PROTOCOL_ERRORS); } + @Override public String toString() { switch (this) { case NONE: - return "no error"; - case BAD_APN: - return "bad apn"; - case BAD_PAP_SECRET: - return "bad pap secret"; - case BARRED: - return "barred"; + return "No Error"; + case OPERATOR_BARRED: + return "Operator Barred"; + case INSUFFICIENT_RESOURCES: + return "Insufficient Resources"; + case MISSING_UKNOWN_APN: + return "Missing / Unknown APN"; + case UNKNOWN_PDP_ADDRESS: + return "Unknown PDP Address"; case USER_AUTHENTICATION: - return "error user autentication"; + return "Error User Autentication"; + case ACTIVATION_REJECT_GGSN: + return "Activation Reject GGSN"; + case ACTIVATION_REJECT_UNSPECIFIED: + return "Activation Reject unspecified"; case SERVICE_OPTION_NOT_SUPPORTED: - return "data not supported"; + return "Data Not Supported"; case SERVICE_OPTION_NOT_SUBSCRIBED: - return "datt not subcribed"; - case SIM_LOCKED: - return "sim locked"; - case RADIO_OFF: - return "radio is off"; - case NO_SIGNAL: - return "no signal"; - case NO_DATA_PLAN: - return "no data plan"; + return "Data Not subscribed"; + case SERVICE_OPTION_OUT_OF_ORDER: + return "Data Services Out of Order"; + case NSAPI_IN_USE: + return "NSAPI in use"; + case PROTOCOL_ERRORS: + return "Protocol Errors"; + case REGISTRATION_FAIL: + return "Network Registration Failure"; + case GPRS_REGISTRATION_FAIL: + return "Data Network Registration Failure"; case RADIO_NOT_AVAILABLE: - return "radio not available"; - case SUSPENED_TEMPORARY: - return "suspend temporary"; + return "Radio Not Available"; case RADIO_ERROR_RETRY: - return "transient radio error"; + return "Transient Radio Rrror"; default: - return "unknown data error"; + return "Unknown Data Error"; } } } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index 5b826b2d4a71..c074cb8a085c 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -63,7 +63,8 @@ public abstract class DataConnectionTracker extends Handler { NONE, DATAIN, DATAOUT, - DATAINANDOUT + DATAINANDOUT, + DORMANT } //***** Event Codes @@ -92,19 +93,20 @@ public abstract class DataConnectionTracker extends Handler { protected static final int EVENT_NV_READY = 31; protected static final int EVENT_PS_RESTRICT_ENABLED = 32; protected static final int EVENT_PS_RESTRICT_DISABLED = 33; + public static final int EVENT_CLEAN_UP_CONNECTION = 34; //***** Constants protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000; - /** Cap out with 1 hour retry interval. */ - protected static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000; + /** Cap out with 30 min retry interval. */ + protected static final int RECONNECT_DELAY_MAX_MILLIS = 30 * 60 * 1000; /** Slow poll when attempting connection recovery. */ protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; /** Default ping deadline, in seconds. */ - protected final int DEFAULT_PING_DEADLINE = 5; + protected static final int DEFAULT_PING_DEADLINE = 5; /** Default max failure count before attempting to network re-registration. */ - protected final int DEFAULT_MAX_PDP_RESET_FAIL = 3; + protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3; /** * After detecting a potential connection problem, this is the max number @@ -148,7 +150,7 @@ public abstract class DataConnectionTracker extends Handler { /** Intent sent when the reconnect alarm fires. */ protected PendingIntent mReconnectIntent = null; - + /** CID of active data connection */ protected int cidActive; @@ -216,7 +218,7 @@ public abstract class DataConnectionTracker extends Handler { } // abstract handler methods - protected abstract void onTrySetupData(); + protected abstract void onTrySetupData(String reason); protected abstract void onRoamingOff(); protected abstract void onRoamingOn(); protected abstract void onRadioAvailable(); @@ -225,13 +227,18 @@ public abstract class DataConnectionTracker extends Handler { protected abstract void onDisconnectDone(AsyncResult ar); protected abstract void onVoiceCallStarted(); protected abstract void onVoiceCallEnded(); + protected abstract void onCleanUpConnection(boolean tearDown, String reason); //***** Overridden from Handler public void handleMessage (Message msg) { switch (msg.what) { case EVENT_TRY_SETUP_DATA: - onTrySetupData(); + String reason = null; + if (msg.obj instanceof String) { + reason = (String)msg.obj; + } + onTrySetupData(reason); break; case EVENT_ROAMING_OFF: @@ -267,6 +274,11 @@ public abstract class DataConnectionTracker extends Handler { onVoiceCallEnded(); break; + case EVENT_CLEAN_UP_CONNECTION: + boolean tearDown = (msg.arg1 == 0) ? false : true; + onCleanUpConnection(tearDown, (String)msg.obj); + break; + default: Log.e("DATA", "Unidentified event = " + msg.what); break; @@ -274,12 +286,6 @@ public abstract class DataConnectionTracker extends Handler { } /** - * Simply tear down data connections due to radio off - * and don't setup again. - */ - public abstract void cleanConnectionBeforeRadioOff(); - - /** * Report the current state of data connectivity (enabled or disabled) * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 79b4afe86984..d6151c612c88 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -62,7 +62,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier { public void notifySignalStrength(Phone sender) { try { - mRegistry.notifySignalStrength(sender.getSignalStrengthASU()); + mRegistry.notifySignalStrength(sender.getSignalStrength()); } catch (RemoteException ex) { // system process is dead } @@ -200,6 +200,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier { return TelephonyManager.DATA_ACTIVITY_OUT; case DATAINANDOUT: return TelephonyManager.DATA_ACTIVITY_INOUT; + case DORMANT: + return TelephonyManager.DATA_ACTIVITY_DORMANT; default: return TelephonyManager.DATA_ACTIVITY_NONE; } @@ -217,6 +219,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier { return Phone.DataActivityState.DATAOUT; case TelephonyManager.DATA_ACTIVITY_INOUT: return Phone.DataActivityState.DATAINANDOUT; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + return Phone.DataActivityState.DORMANT; default: return Phone.DataActivityState.NONE; } diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index 8f4c69caf86d..e8095e172b86 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -182,7 +182,7 @@ public class GsmAlphabet { return stringToGsm7BitPacked(data); } - int headerBits = header.length * 8; + int headerBits = (header.length + 1) * 8; int headerSeptets = headerBits / 7; headerSeptets += (headerBits % 7) > 0 ? 1 : 0; @@ -194,7 +194,8 @@ public class GsmAlphabet { (headerSeptets*7), true); // Paste in the header - System.arraycopy(header, 0, ret, 1, header.length); + ret[1] = (byte)header.length; + System.arraycopy(header, 0, ret, 2, header.length); return ret; } @@ -575,52 +576,6 @@ public class GsmAlphabet { return size; } - /** - * Returns the index into <code>s</code> of the first character - * after <code>limit</code> octets have been reached, starting at - * index <code>start</code>. This is used when dividing messages - * in UCS2 encoding into units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. <code>MAX_USER_DATA_BYTES</code> - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findUCS2LimitIndex(String s, int start, int limit) { - int numCharToBeEncoded = s.length() - start; - return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start; - } - - /** - * Returns the index into <code>s</code> of the first character - * after <code>limit</code> septets/octets have been reached - * according to the <code>encodingType</code>, starting at - * index <code>start</code>. This is used when dividing messages - * units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. <code>MAX_USER_DATA_BYTES</code> - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException { - if (encodingType == SmsMessage.ENCODING_7BIT) { - return findGsmSeptetLimitIndex(s, start, limit); - } - else if (encodingType == SmsMessage.ENCODING_16BIT) { - return findUCS2LimitIndex(s, start, limit); - } - else { - throw new EncodeException("Unsupported encoding type: " + encodingType); - } - } - // Set in the static initializer private static int sGsmSpaceChar; diff --git a/telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl b/telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl index 6c1f4bb36db9..facdc499910d 100644 --- a/telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl +++ b/telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl @@ -1,11 +1,11 @@ package com.android.internal.telephony; /** - * Interface used to interact with extended MMI/USSD network service. + * Interface used to interact with extended MMI/USSD network service. */ interface IExtendedNetworkService { /** - * Set a MMI/USSD command to ExtendedNetworkService for further process. + * Set a MMI/USSD command to ExtendedNetworkService for further process. * This should be called when a MMI command is placed from panel. * @param number the dialed MMI/USSD number. */ diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index e0884b34d28c..0202ec81edf3 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -18,6 +18,7 @@ package com.android.internal.telephony; import android.os.Bundle; import android.telephony.ServiceState; +import android.telephony.SignalStrength; oneway interface IPhoneStateListener { void onServiceStateChanged(in ServiceState serviceState); @@ -30,5 +31,6 @@ oneway interface IPhoneStateListener { void onCallStateChanged(int state, String incomingNumber); void onDataConnectionStateChanged(int state); void onDataActivity(int direction); + void onSignalStrengthsChanged(in SignalStrength signalStrength); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index bab0603230ea..d83b1354927d 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -221,5 +221,27 @@ interface ITelephony { */ int getActivePhoneType(); + /** + * Returns the CDMA ERI icon index to display + */ + int getCdmaEriIconIndex(); + + /** + * Returns the CDMA ERI icon mode, + * 0 - ON + * 1 - FLASHING + */ + int getCdmaEriIconMode(); + + /** + * Returns the CDMA ERI text, + */ + String getCdmaEriText(); + + /** + * Returns the unread count of voicemails + */ + int getVoiceMessageCount(); + } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 1b011fe4047e..865c6caf6fe0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -19,6 +19,7 @@ package com.android.internal.telephony; import android.content.Intent; import android.os.Bundle; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import com.android.internal.telephony.IPhoneStateListener; interface ITelephonyRegistry { @@ -26,7 +27,7 @@ interface ITelephonyRegistry { void notifyCallState(int state, String incomingNumber); void notifyServiceState(in ServiceState state); - void notifySignalStrength(int signalStrengthASU); + void notifySignalStrength(in SignalStrength signalStrength); void notifyMessageWaitingChanged(boolean mwi); void notifyCallForwardingChanged(boolean cfi); void notifyDataActivity(int state); diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java index 014fbb6f6973..7eafafd5bda2 100644 --- a/telephony/java/com/android/internal/telephony/IccConstants.java +++ b/telephony/java/com/android/internal/telephony/IccConstants.java @@ -61,4 +61,5 @@ public interface IccConstants { static final String DF_TELECOM = "7F10"; static final String DF_GRAPHICS = "5F50"; static final String DF_GSM = "7F20"; + static final String DF_CDMA = "7F25"; } diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java index 114094b74181..ea24c25c2a9b 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/IccVmFixedException.java b/telephony/java/com/android/internal/telephony/IccVmFixedException.java index 45679c1b90de..a75496ff69a1 100644 --- a/telephony/java/com/android/internal/telephony/IccVmFixedException.java +++ b/telephony/java/com/android/internal/telephony/IccVmFixedException.java @@ -28,5 +28,5 @@ public final class IccVmFixedException extends IccException { public IccVmFixedException(String s) { super(s); - } + } }
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java b/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java index 7e90d24ad767..3c9d12689acd 100644 --- a/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java +++ b/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java @@ -28,5 +28,5 @@ public final class IccVmNotSupportedException extends IccException { public IccVmNotSupportedException(String s) { super(s); - } + } } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 03c1c56f26eb..c8d384d8d5f6 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -23,6 +23,7 @@ import android.os.Message; import android.preference.PreferenceManager; import android.telephony.CellLocation; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.gsm.NetworkInfo; @@ -82,9 +83,11 @@ public interface Phone { * <li>DATAIN = Receiving IP ppp traffic</li> * <li>DATAOUT = Sending IP ppp traffic</li> * <li>DATAINANDOUT = Both receiving and sending IP ppp traffic</li> + * <li>DORMANT = The data connection is still active, + but physical link is down</li> * </ul> */ - NONE, DATAIN, DATAOUT, DATAINANDOUT; + NONE, DATAIN, DATAOUT, DATAINANDOUT, DORMANT; }; enum SuppService { @@ -99,6 +102,7 @@ public interface Phone { static final String DATA_APN_KEY = "apn"; static final String DATA_IFACE_NAME_KEY = "iface"; static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable"; + static final String PHONE_IN_ECM_STATE = "phoneinECMState"; /** * APN types for data connections. These are usage categories for an APN @@ -150,7 +154,7 @@ public interface Phone { static final String REASON_PS_RESTRICT_ENABLED = "psRestrictEnabled"; static final String REASON_PS_RESTRICT_DISABLED = "psRestrictDisabled"; static final String REASON_SIM_LOADED = "simLoaded"; - + // Used for band mode selection methods static final int BM_UNSPECIFIED = 0; // selected by baseband automatically static final int BM_EURO_BAND = 1; // GSM-900 / DCS-1800 / WCDMA-IMT-2000 @@ -162,28 +166,53 @@ public interface Phone { // Used for preferred network type // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone - int NT_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */ - int NT_MODE_GSM_ONLY = 1; /* GSM only */ - int NT_MODE_WCDMA_ONLY = 2; /* WCDMA only */ - int NT_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL) - AVAILABLE Application Settings menu*/ - int NT_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL) - AVAILABLE Application Settings menu*/ - int NT_MODE_CDMA_NO_EVDO = 5; /* CDMA only */ - int NT_MODE_EVDO_NO_CDMA = 6; /* EvDo only */ - int NT_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL) - AVAILABLE Application Settings menu*/ - int PREFERRED_NT_MODE = NT_MODE_GSM_ONLY; + int NT_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF; + int NT_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY; + int NT_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY; + int NT_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS; + + int NT_MODE_CDMA = RILConstants.NETWORK_MODE_CDMA; + + int NT_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO; + int NT_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA; + int NT_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL; + + int PREFERRED_NT_MODE = RILConstants.PREFERRED_NETWORK_MODE; // Used for CDMA roaming mode - static final int CDMA_RM_HOME = 0; //Home Networks only, as defined in PRL - static final int CDMA_RM_AFFILIATED = 1; //Roaming an Affiliated networks, as defined in PRL - static final int CDMA_RM_ANY = 2; //Roaming on Any Network, as defined in PRL + static final int CDMA_RM_HOME = 0; // Home Networks only, as defined in PRL + static final int CDMA_RM_AFFILIATED = 1; // Roaming an Affiliated networks, as defined in PRL + static final int CDMA_RM_ANY = 2; // Roaming on Any Network, as defined in PRL // Used for CDMA subscription mode - static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; //RUIM/SIM (default) - static final int CDMA_SUBSCRIPTION_NV = 1; //NV -> non-volatile memory + static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // RUIM/SIM (default) + static final int CDMA_SUBSCRIPTION_NV = 1; // NV -> non-volatile memory + + static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_NV; + + static final int TTY_MODE_OFF = 0; + static final int TTY_MODE_FULL = 1; + static final int TTY_MODE_HCO = 2; + static final int TTY_MODE_VCO = 3; + + /** + * CDMA OTA PROVISION STATUS, the same as RIL_CDMA_OTA_Status in ril.h + */ + + public static final int CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED = 0; + public static final int CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED = 1; + public static final int CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED = 2; + public static final int CDMA_OTA_PROVISION_STATUS_SSD_UPDATED = 3; + public static final int CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED = 4; + public static final int CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED = 5; + public static final int CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED = 6; + public static final int CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED = 7; + public static final int CDMA_OTA_PROVISION_STATUS_COMMITTED = 8; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED = 9; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED = 10; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED = 11; + /** * Get the current ServiceState. Use @@ -200,7 +229,7 @@ public interface Phone { /** * Get the current DataState. No change notification exists at this * interface -- use - * {@link com.android.internal.telephony.PhoneStateIntentReceiver PhoneStateIntentReceiver} + * {@link com.android.telephony.PhoneStateListener PhoneStateListener} * instead. */ DataState getDataConnectionState(); @@ -270,9 +299,9 @@ public interface Phone { * <ul><li>0 means "-113 dBm or less".</li> * <li>31 means "-51 dBm or greater".</li></ul> * - * @return Current signal strength in ASU's. + * @return Current signal strength as SignalStrength */ - int getSignalStrengthASU(); + SignalStrength getSignalStrength(); /** * Notifies when a previously untracked non-ringing/waiting connection has appeared. @@ -494,6 +523,21 @@ public interface Phone { void unregisterForInCallVoicePrivacyOff(Handler h); /** + * Register for notifications when CDMA OTA Provision status change + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForCdmaOtaStatusChange(Handler h, int what, Object obj); + + /** + * Unegister for notifications when CDMA OTA Provision status change + * @param h Handler to be removed from the registrant list. + */ + void unregisterForCdmaOtaStatusChange(Handler h); + + /** * Returns SIM record load state. Use * <code>getSimCard().registerForReady()</code> for change notification. * @@ -707,6 +751,19 @@ public interface Phone { */ void stopDtmf(); + /** + * send burst DTMF tone, it can send the string as single character or multiple character + * ignore if there is no active call or not valid digits string. + * Valid digit means only includes characters ISO-LATIN characters 0-9, *, # + * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character, + * this api can send single character and multiple character, also, this api has response + * back to caller. + * + * @param dtmfString is string representing the dialing digit(s) in the active call + * @param onCompelte is the callback message when the action is processed by BP + * + */ + void sendBurstDtmf(String dtmfString, Message onComplete); /** * Sets the radio power on/off state (off is sometimes @@ -771,6 +828,12 @@ public interface Phone { String getVoiceMailNumber(); /** + * Returns unread voicemail count. This count is shown when the voicemail + * notification is expanded.<p> + */ + int getVoiceMessageCount(); + + /** * Returns the alpha tag associated with the voice mail number. * If there is no alpha tag associated or the record is not yet available, * returns a default localized string. <p> @@ -803,7 +866,7 @@ public interface Phone { * * @param commandInterfaceCFReason is one of the valid call forwarding * CF_REASONS, as defined in - * <code>com.android.internal.telephony.CommandsInterface./code> + * <code>com.android.internal.telephony.CommandsInterface.</code> * @param onComplete a callback message when the action is completed. * @see com.android.internal.telephony.CallForwardInfo for details. */ @@ -816,10 +879,10 @@ public interface Phone { * * @param commandInterfaceCFReason is one of the valid call forwarding * CF_REASONS, as defined in - * <code>com.android.internal.telephony.CommandsInterface./code> + * <code>com.android.internal.telephony.CommandsInterface.</code> * @param commandInterfaceCFAction is one of the valid call forwarding * CF_ACTIONS, as defined in - * <code>com.android.internal.telephony.CommandsInterface./code> + * <code>com.android.internal.telephony.CommandsInterface.</code> * @param dialingNumber is the target phone number to forward calls to * @param timerSeconds is used by CFNRy to indicate the timeout before * forwarding is attempted. @@ -1279,6 +1342,20 @@ public interface Phone { //***** CDMA support methods + /* + * TODO(Moto) TODO(Teleca): can getCdmaMin, getEsn, getMeid use more generic calls + * already defined getXxxx above? + */ + + /** + * Retrieves the MIN for CDMA phones. + */ + String getCdmaMin(); + + /** + * Retrieves PRL Version for CDMA phones + */ + String getCdmaPrlVersion(); /** * Retrieves the ESN for CDMA phones. @@ -1306,22 +1383,22 @@ public interface Phone { public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(); /** - * setTTYModeEnabled + * setTTYMode * sets a TTY mode option. * * @param enable is a boolean representing the state that you are * requesting, true for enabled, false for disabled. * @param onComplete a callback message when the action is completed */ - void setTTYModeEnabled(boolean enable, Message onComplete); + void setTTYMode(int ttyMode, Message onComplete); /** - * queryTTYModeEnabled + * queryTTYMode * query the status of the TTY mode * * @param onComplete a callback message when the action is completed. */ - void queryTTYModeEnabled(Message onComplete); + void queryTTYMode(Message onComplete); /** * Activate or deactivate cell broadcast SMS. @@ -1344,10 +1421,220 @@ public interface Phone { /** * Configure cell broadcast SMS. * + * TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig + * * @param response * Callback message is empty on completion */ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response); public void notifyDataActivity(); + + /** + * Returns the CDMA ERI icon index to display + */ + public int getCdmaEriIconIndex(); + + /** + * Returns the CDMA ERI icon mode, + * 0 - ON + * 1 - FLASHING + */ + public int getCdmaEriIconMode(); + + /** + * Returns the CDMA ERI text, + */ + public String getCdmaEriText(); + + /** + * request to exit emergency call back mode + * the caller should use setOnECMModeExitResponse + * to receive the emergency callback mode exit response + */ + void exitEmergencyCallbackMode(); + + /** + * this decides if the dial number is OTA(Over the air provision) number or not + * @param dialStr is string representing the dialing digit(s) + * @return true means the dialStr is OTA number, and false means the dialStr is not OTA number + */ + boolean isOtaSpNumber(String dialStr); + + /** + * Register for notifications when CDMA call waiting comes + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForCallWaiting(Handler h, int what, Object obj); + + /** + * Unegister for notifications when CDMA Call waiting comes + * @param h Handler to be removed from the registrant list. + */ + void unregisterForCallWaiting(Handler h); + + + /** + * Register for signal information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + + void registerForSignalInfo(Handler h, int what, Object obj) ; + /** + * Unregisters for signal information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForSignalInfo(Handler h); + + /** + * Register for display information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForDisplayInfo(Handler h, int what, Object obj); + + /** + * Unregisters for display information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForDisplayInfo(Handler h) ; + + /** + * Register for CDMA number information record notification from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a CdmaInformationRecords.CdmaNumberInfoRec + * instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForNumberInfo(Handler h, int what, Object obj); + + /** + * Unregisters for number information record notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForNumberInfo(Handler h); + + /** + * Register for CDMA redirected number information record notification + * from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a CdmaInformationRecords.CdmaRedirectingNumberInfoRec + * instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForRedirectedNumberInfo(Handler h, int what, Object obj); + + /** + * Unregisters for redirected number information record notification. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForRedirectedNumberInfo(Handler h); + + /** + * Register for CDMA line control information record notification + * from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a CdmaInformationRecords.CdmaLineControlInfoRec + * instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForLineControlInfo(Handler h, int what, Object obj); + + /** + * Unregisters for line control information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForLineControlInfo(Handler h); + + /** + * Register for CDMA T53 CLIR information record notifications + * from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a CdmaInformationRecords.CdmaT53ClirInfoRec + * instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerFoT53ClirlInfo(Handler h, int what, Object obj); + + /** + * Unregisters for T53 CLIR information record notification + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForT53ClirInfo(Handler h); + + /** + * Register for CDMA T53 audio control information record notifications + * from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a CdmaInformationRecords.CdmaT53AudioControlInfoRec + * instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForT53AudioControlInfo(Handler h, int what, Object obj); + + /** + * Unregisters for T53 audio control information record notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForT53AudioControlInfo(Handler h); + + /** + * registers for exit emergency call back mode request response + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + + void setOnEcbModeExitResponse(Handler h, int what, Object obj); + + /** + * Unregisters for exit emergency call back mode request response + * + * @param h Handler to be removed from the registrant list. + */ + void unsetOnEcbModeExitResponse(Handler h); + + } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 0314034f2424..a26e7296b2b6 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -29,6 +29,7 @@ import android.os.RegistrantList; import android.os.SystemProperties; import android.preference.PreferenceManager; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.text.TextUtils; import android.util.Log; @@ -90,6 +91,8 @@ public abstract class PhoneBase implements Phone { protected static final int EVENT_RUIM_RECORDS_LOADED = 21; protected static final int EVENT_NV_READY = 22; protected static final int EVENT_SET_ENHANCED_VP = 23; + protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 24; + protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 25; // Key used to read/write current CLIR setting public static final String CLIR_KEY = "clir_key"; @@ -187,7 +190,7 @@ public abstract class PhoneBase implements Phone { setUnitTestMode(unitTestMode); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false); + mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false); } // Inherited documentation suffices. @@ -204,7 +207,7 @@ public abstract class PhoneBase implements Phone { mDnsCheckDisabled = b; SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); + editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); editor.commit(); } @@ -282,7 +285,6 @@ public abstract class PhoneBase implements Phone { mCM.unregisterForInCallVoicePrivacyOff(h); } - /** * Notifiy registrants of a new ringing Connection. * Subclasses of Phone probably want to replace this with a @@ -567,9 +569,6 @@ public abstract class PhoneBase implements Phone { mCM.setPreferredNetworkType(networkType, response); } - /** - * Set the status of the preferred Network Type: Global, CDMA only or GSM/UMTS only - */ public void getPreferredNetworkType(Message response) { mCM.getPreferredNetworkType(response); } @@ -582,12 +581,12 @@ public abstract class PhoneBase implements Phone { mCM.setSmscAddress(address, result); } - public void setTTYModeEnabled(boolean enable, Message onComplete) { + public void setTTYMode(int ttyMode, Message onComplete) { // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); } - public void queryTTYModeEnabled(Message onComplete) { + public void queryTTYMode(Message onComplete) { // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); } @@ -632,10 +631,159 @@ 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); } public abstract String getPhoneName(); + /** @hide */ + public int getVoiceMessageCount(){ + return 0; + } + + /** + * Returns the CDMA ERI icon index to display + */ + public int getCdmaEriIconIndex() { + Log.e(LOG_TAG, "Error! getCdmaEriIconIndex should never be executed in GSM mode"); + return -1; + } + + /** + * Returns the CDMA ERI icon mode, + * 0 - ON + * 1 - FLASHING + */ + public int getCdmaEriIconMode() { + Log.e(LOG_TAG, "Error! getCdmaEriIconMode should never be executed in GSM mode"); + return -1; + } + + /** + * Returns the CDMA ERI text, + */ + public String getCdmaEriText() { + Log.e(LOG_TAG, "Error! getCdmaEriText should never be executed in GSM mode"); + return "GSM nw, no ERI"; + } + + public String getCdmaMin() { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + return null; + } + + public String getCdmaPrlVersion(){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + return null; + } + + public void sendBurstDtmf(String dtmfString, Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void exitEmergencyCallbackMode() { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public boolean isOtaSpNumber(String dialStr) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + return false; + } + + public void registerForCallWaiting(Handler h, int what, Object obj){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unregisterForCallWaiting(Handler h){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + mCM.registerForSignalInfo(h, what, obj); + } + + public void unregisterForSignalInfo(Handler h) { + mCM.unregisterForSignalInfo(h); + } + + public void registerForDisplayInfo(Handler h, int what, Object obj) { + mCM.registerForDisplayInfo(h, what, obj); + } + + public void unregisterForDisplayInfo(Handler h) { + mCM.unregisterForDisplayInfo(h); + } + + public void registerForNumberInfo(Handler h, int what, Object obj) { + mCM.registerForNumberInfo(h, what, obj); + } + + public void unregisterForNumberInfo(Handler h) { + mCM.unregisterForNumberInfo(h); + } + + public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) { + mCM.registerForRedirectedNumberInfo(h, what, obj); + } + + public void unregisterForRedirectedNumberInfo(Handler h) { + mCM.unregisterForRedirectedNumberInfo(h); + } + + public void registerForLineControlInfo(Handler h, int what, Object obj) { + mCM.registerForLineControlInfo( h, what, obj); + } + + public void unregisterForLineControlInfo(Handler h) { + mCM.unregisterForLineControlInfo(h); + } + + public void registerFoT53ClirlInfo(Handler h, int what, Object obj) { + mCM.registerFoT53ClirlInfo(h, what, obj); + } + + public void unregisterForT53ClirInfo(Handler h) { + mCM.unregisterForT53ClirInfo(h); + } + + public void registerForT53AudioControlInfo(Handler h, int what, Object obj) { + mCM.registerForT53AudioControlInfo( h, what, obj); + } + + public void unregisterForT53AudioControlInfo(Handler h) { + mCM.unregisterForT53AudioControlInfo(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unsetOnEcbModeExitResponse(Handler h){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } } diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java index 86e2f0449da4..a84f74e7d265 100644 --- a/telephony/java/com/android/internal/telephony/PhoneFactory.java +++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java @@ -107,30 +107,49 @@ public class PhoneFactory { //reads the system properties and makes commandsinterface sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); - switch(networkMode) { - case RILConstants.NETWORK_MODE_CDMA: - case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: - case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: - case RILConstants.NETWORK_MODE_GLOBAL: - sProxyPhone = new PhoneProxy(new CDMAPhone(context, - sCommandsInterface, sPhoneNotifier)); - Log.i(LOG_TAG, "Creating CDMAPhone"); - break; - case RILConstants.NETWORK_MODE_WCDMA_PREF: - case RILConstants.NETWORK_MODE_GSM_ONLY: - case RILConstants.NETWORK_MODE_WCDMA_ONLY: - case RILConstants.NETWORK_MODE_GSM_UMTS: - default: - sProxyPhone = new PhoneProxy(new GSMPhone(context, - sCommandsInterface, sPhoneNotifier)); - Log.i(LOG_TAG, "Creating GSMPhone"); - break; + int phoneType = getPhoneType(networkMode); + if (phoneType == RILConstants.GSM_PHONE) { + sProxyPhone = new PhoneProxy(new GSMPhone(context, + sCommandsInterface, sPhoneNotifier)); + Log.i(LOG_TAG, "Creating GSMPhone"); + } else if (phoneType == RILConstants.CDMA_PHONE) { + sProxyPhone = new PhoneProxy(new CDMAPhone(context, + sCommandsInterface, sPhoneNotifier)); + Log.i(LOG_TAG, "Creating CDMAPhone"); } + sMadeDefaults = true; } } } + /* + * This function returns the type of the phone, depending + * on the network mode. + * + * @param network mode + * @return Phone Type + */ + public static int getPhoneType(int networkMode) { + switch(networkMode) { + case RILConstants.NETWORK_MODE_CDMA: + case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: + case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: + return RILConstants.CDMA_PHONE; + + case RILConstants.NETWORK_MODE_WCDMA_PREF: + case RILConstants.NETWORK_MODE_GSM_ONLY: + case RILConstants.NETWORK_MODE_WCDMA_ONLY: + case RILConstants.NETWORK_MODE_GSM_UMTS: + return RILConstants.GSM_PHONE; + + case RILConstants.NETWORK_MODE_GLOBAL: + return RILConstants.CDMA_PHONE; + default: + return RILConstants.GSM_PHONE; + } + } + public static Phone getDefaultPhone() { if (sLooper != Looper.myLooper()) { throw new RuntimeException( diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index b76d8015a93a..5b3c8dd4c162 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -26,6 +26,7 @@ import android.os.Message; import android.preference.PreferenceManager; import android.telephony.CellLocation; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.util.Log; import com.android.internal.telephony.cdma.CDMAPhone; @@ -127,10 +128,9 @@ public class PhoneProxy extends Handler implements Phone { Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); intent.putExtra(Phone.PHONE_NAME_KEY, mActivePhone.getPhoneName()); ActivityManagerNative.broadcastStickyIntent(intent, null); - break; default: - Log.e(LOG_TAG, "Error! This handler was not registered for this message type. Message: " + Log.e(LOG_TAG,"Error! This handler was not registered for this message type. Message: " + msg.what); break; } @@ -198,8 +198,8 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getActiveApn(); } - public int getSignalStrengthASU() { - return mActivePhone.getSignalStrengthASU(); + public SignalStrength getSignalStrength() { + return mActivePhone.getSignalStrength(); } public void registerForUnknownConnection(Handler h, int what, Object obj) { @@ -306,6 +306,14 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.unregisterForInCallVoicePrivacyOff(h); } + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + mActivePhone.registerForCdmaOtaStatusChange(h,what,obj); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + mActivePhone.unregisterForCdmaOtaStatusChange(h); + } + public boolean getIccRecordsLoaded() { return mActivePhone.getIccRecordsLoaded(); } @@ -406,6 +414,14 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getLine1Number(); } + public String getCdmaMin() { + return mActivePhone.getCdmaMin(); + } + + public String getCdmaPrlVersion() { + return mActivePhone.getCdmaPrlVersion(); + } + public String getLine1AlphaTag() { return mActivePhone.getLine1AlphaTag(); } @@ -418,6 +434,11 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getVoiceMailNumber(); } + /** @hide */ + public int getVoiceMessageCount(){ + return mActivePhone.getVoiceMessageCount(); + } + public String getVoiceMailAlphaTag() { return mActivePhone.getVoiceMailAlphaTag(); } @@ -648,12 +669,12 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getIccPhoneBookInterfaceManager(); } - public void setTTYModeEnabled(boolean enable, Message onComplete) { - mActivePhone.setTTYModeEnabled(enable, onComplete); + public void setTTYMode(int ttyMode, Message onComplete) { + mActivePhone.setTTYMode(ttyMode, onComplete); } - public void queryTTYModeEnabled(Message onComplete) { - mActivePhone.queryTTYModeEnabled(onComplete); + public void queryTTYMode(Message onComplete) { + mActivePhone.queryTTYMode(onComplete); } public void activateCellBroadcastSms(int activate, Message response) { @@ -679,5 +700,100 @@ public class PhoneProxy extends Handler implements Phone { public void setSmscAddress(String address, Message result) { mActivePhone.setSmscAddress(address, result); } -} + public int getCdmaEriIconIndex() { + return mActivePhone.getCdmaEriIconIndex(); + } + + public String getCdmaEriText() { + return mActivePhone.getCdmaEriText(); + } + + public int getCdmaEriIconMode() { + return mActivePhone.getCdmaEriIconMode(); + } + + public void sendBurstDtmf(String dtmfString, Message onComplete){ + mActivePhone.sendBurstDtmf(dtmfString,onComplete); + } + + public void exitEmergencyCallbackMode(){ + mActivePhone.exitEmergencyCallbackMode(); + } + + public boolean isOtaSpNumber(String dialStr){ + return mActivePhone.isOtaSpNumber(dialStr); + } + + public void registerForCallWaiting(Handler h, int what, Object obj){ + mActivePhone.registerForCallWaiting(h,what,obj); + } + + public void unregisterForCallWaiting(Handler h){ + mActivePhone.unregisterForCallWaiting(h); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + mActivePhone.registerForSignalInfo(h,what,obj); + } + + public void unregisterForSignalInfo(Handler h) { + mActivePhone.unregisterForSignalInfo(h); + } + + public void registerForDisplayInfo(Handler h, int what, Object obj) { + mActivePhone.registerForDisplayInfo(h,what,obj); + } + + public void unregisterForDisplayInfo(Handler h) { + mActivePhone.unregisterForDisplayInfo(h); + } + + public void registerForNumberInfo(Handler h, int what, Object obj) { + mActivePhone.registerForNumberInfo(h, what, obj); + } + + public void unregisterForNumberInfo(Handler h) { + mActivePhone.unregisterForNumberInfo(h); + } + + public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) { + mActivePhone.registerForRedirectedNumberInfo(h, what, obj); + } + + public void unregisterForRedirectedNumberInfo(Handler h) { + mActivePhone.unregisterForRedirectedNumberInfo(h); + } + + public void registerForLineControlInfo(Handler h, int what, Object obj) { + mActivePhone.registerForLineControlInfo( h, what, obj); + } + + public void unregisterForLineControlInfo(Handler h) { + mActivePhone.unregisterForLineControlInfo(h); + } + + public void registerFoT53ClirlInfo(Handler h, int what, Object obj) { + mActivePhone.registerFoT53ClirlInfo(h, what, obj); + } + + public void unregisterForT53ClirInfo(Handler h) { + mActivePhone.unregisterForT53ClirInfo(h); + } + + public void registerForT53AudioControlInfo(Handler h, int what, Object obj) { + mActivePhone.registerForT53AudioControlInfo( h, what, obj); + } + + public void unregisterForT53AudioControlInfo(Handler h) { + mActivePhone.unregisterForT53AudioControlInfo(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ + mActivePhone.setOnEcbModeExitResponse(h,what,obj); + } + + public void unsetOnEcbModeExitResponse(Handler h){ + mActivePhone.unsetOnEcbModeExitResponse(h); + } +} diff --git a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java index fd822cda07bc..b31161cb9281 100644 --- a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java +++ b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java @@ -23,6 +23,7 @@ import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Log; @@ -39,8 +40,6 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { private static final String LOG_TAG = "PHONE"; private static final boolean DBG = false; - public static final String INTENT_KEY_ASU = "asu"; - private static final int NOTIF_PHONE = 1 << 0; private static final int NOTIF_SERVICE = 1 << 1; private static final int NOTIF_SIGNAL = 1 << 2; @@ -49,7 +48,8 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { Phone.State mPhoneState = Phone.State.IDLE; ServiceState mServiceState = new ServiceState(); - int mAsu = -1; + SignalStrength mSignalStrength = new SignalStrength(); + private Context mContext; private Handler mTarget; private IntentFilter mFilter; @@ -106,12 +106,14 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { * Throws RuntimeException if client has not called notifySignalStrength() */ public int getSignalStrength() { + // TODO: use new SignalStrength instead of asu if ((mWants & NOTIF_SIGNAL) == 0) { throw new RuntimeException ("client must call notifySignalStrength(int)"); } + int gsmSignalStrength = mSignalStrength.getGsmSignalStrength(); - return mAsu; + return (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); } /** @@ -129,10 +131,15 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { int dBm = -1; - if (mAsu != -1) { - dBm = -113 + 2*mAsu; + if(!mSignalStrength.isGsm()) { + dBm = mSignalStrength.getCdmaDbm(); + } else { + int gsmSignalStrength = mSignalStrength.getGsmSignalStrength(); + int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); + if (asu != -1) { + dBm = -113 + 2*asu; + } } - return dBm; } @@ -180,8 +187,7 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { try { if (TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED.equals(action)) { - mAsu = intent.getIntExtra(INTENT_KEY_ASU, mAsu); - if (DBG) Log.d(LOG_TAG, "onReceiveIntent: set asu=" + mAsu); + mSignalStrength = SignalStrength.newFromBundle(intent.getExtras()); if (mTarget != null && getNotifySignalStrength()) { Message message = Message.obtain(mTarget, mAsuEventWhat); diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index be18a3993b19..4db3e5bfcf8b 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -38,9 +38,19 @@ import android.telephony.SmsMessage; import android.util.Config; import android.util.Log; +import com.android.internal.telephony.CallForwardInfo; +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.gsm.NetworkInfo; -import com.android.internal.telephony.gsm.PDPContextState; +import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; +import com.android.internal.telephony.IccCardApplication; +import com.android.internal.telephony.IccCardStatus; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.SmsResponse; +import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; +import com.android.internal.telephony.cdma.CdmaInformationRecords; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -156,7 +166,7 @@ class RILRequest { } void - onError(int error) { + onError(int error, Object ret) { CommandException ex; ex = CommandException.fromRilErrno(error); @@ -166,7 +176,7 @@ class RILRequest { + " error: " + ex); if (mResult != null) { - AsyncResult.forMessage(mResult, null, ex); + AsyncResult.forMessage(mResult, ret, ex); mResult.sendToTarget(); } @@ -233,6 +243,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { private static final int CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES = 31; BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { sendScreenState(true); @@ -280,7 +291,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { s = mSocket; if (s == null) { - rr.onError(RADIO_NOT_AVAILABLE); + rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); mRequestMessagesPending--; alreadySubtracted = true; @@ -321,7 +332,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null || !alreadySubtracted) { - rr.onError(RADIO_NOT_AVAILABLE); + rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); } } catch (RuntimeException exc) { @@ -330,7 +341,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null || !alreadySubtracted) { - rr.onError(GENERIC_FAILURE); + rr.onError(GENERIC_FAILURE, null); rr.release(); } } @@ -535,7 +546,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { synchronized (mRequestsList) { for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) { RILRequest rr = mRequestsList.get(i); - rr.onError(RADIO_NOT_AVAILABLE); + rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); } @@ -562,18 +573,22 @@ public final class RIL extends BaseCommands implements CommandsInterface { mNetworkMode = networkMode; //At startup mPhoneType is first set from networkMode switch(networkMode) { + case RILConstants.NETWORK_MODE_WCDMA_PREF: + case RILConstants.NETWORK_MODE_GSM_ONLY: + case RILConstants.NETWORK_MODE_WCDMA_ONLY: + case RILConstants.NETWORK_MODE_GSM_UMTS: + mPhoneType = RILConstants.GSM_PHONE; + break; case RILConstants.NETWORK_MODE_CDMA: case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: + mPhoneType = RILConstants.CDMA_PHONE; + break; case RILConstants.NETWORK_MODE_GLOBAL: mPhoneType = RILConstants.CDMA_PHONE; break; - case RILConstants.NETWORK_MODE_WCDMA_PREF: - case RILConstants.NETWORK_MODE_GSM_ONLY: - case RILConstants.NETWORK_MODE_WCDMA_ONLY: - case RILConstants.NETWORK_MODE_GSM_UMTS: default: - mPhoneType = RILConstants.GSM_PHONE; + mPhoneType = RILConstants.CDMA_PHONE; } PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -1052,6 +1067,17 @@ public final class RIL extends BaseCommands implements CommandsInterface { send(rr); } + public void + sendBurstDtmf(String dtmfString, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BURST_DTMF, result); + + rr.mp.writeString(dtmfString); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + dtmfString); + + send(rr); + } public void sendSMS (String smscPDU, String pdu, Message result) { @@ -1234,13 +1260,19 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result); - rr.mp.writeInt(5); + rr.mp.writeInt(6); rr.mp.writeString(radioTechnology); rr.mp.writeString(profile); rr.mp.writeString(apn); rr.mp.writeString(user); rr.mp.writeString(password); + //TODO(): Add to the APN database, AuthType is set to CHAP/PAP + // 0 => Neither PAP nor CHAP will be performed, 3 => PAP / CHAP will be performed. + if (user != null) + rr.mp.writeString("3"); + else + rr.mp.writeString("0"); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + apn); @@ -1314,28 +1346,31 @@ public final class RIL extends BaseCommands implements CommandsInterface { } public void - acknowledgeLastIncomingSMS(boolean success, Message result) { + acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result); - rr.mp.writeInt(1); + rr.mp.writeInt(2); rr.mp.writeInt(success ? 1 : 0); + rr.mp.writeInt(cause); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + success + " " + cause); send(rr); } public void - acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result); rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass // cause code according to X.S004-550E - rr.mp.writeInt(39); //39 means other terminal problem; is not interpreted for success. + rr.mp.writeInt(cause); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + success + " " + cause); send(rr); } @@ -1361,6 +1396,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest) + " 0x" + Integer.toHexString(command) + " 0x" + Integer.toHexString(fileid) + " " + + " path: " + path + "," + p1 + "," + p2 + "," + p3); send(rr); @@ -1777,7 +1813,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { public void setSmscAddress(String address, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_SMSC_ADDRESS, result); - rr.mp.writeInt(1); rr.mp.writeString(address); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) @@ -1786,6 +1821,84 @@ public final class RIL extends BaseCommands implements CommandsInterface { send(rr); } + /** + * {@inheritDoc} + */ + public void reportSmsMemoryStatus(boolean available, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result); + rr.mp.writeInt(1); + rr.mp.writeInt(available ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + ": " + available); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void reportStkServiceIsRunning(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void getGsmBroadcastConfig(Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, response); + + int numOfConfig = config.length; + rr.mp.writeInt(numOfConfig); + + for(int i = 0; i < numOfConfig; i++) { + rr.mp.writeInt(config[i].getFromServiceId()); + rr.mp.writeInt(config[i].getToServiceId()); + rr.mp.writeInt(config[i].getFromCodeScheme()); + rr.mp.writeInt(config[i].getToCodeScheme()); + rr.mp.writeInt(config[i].isSelected() ? 1 : 0); + } + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " with " + numOfConfig + "configs : "); + for (int i = 0; i < numOfConfig; i++) { + riljLog(config[i].toString()); + } + } + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setGsmBroadcastActivation(boolean activate, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(activate ? 0 : 1); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + //***** Private Methods private void sendScreenState(boolean on) { @@ -1793,7 +1906,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { rr.mp.writeInt(1); rr.mp.writeInt(on ? 1 : 0); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + on); + if (RILJ_LOGD) riljLog(rr.serialString() + + "> " + requestToString(rr.mRequest) + ": " + on); send(rr); } @@ -1838,7 +1952,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { * and/or radio knowing. */ if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF"); - setRadioPower(false, null); + setRadioPower(false, null); } else { if (DBG) Log.d(LOG_TAG, "Radio OFF @ init"); setRadioState(newState); @@ -1941,28 +2055,24 @@ public final class RIL extends BaseCommands implements CommandsInterface { return; } - if (error != 0) { - rr.onError(error); - rr.release(); - return; - } + Object ret = null; - Object ret; - - try {switch (rr.mRequest) { -/* + if (error == 0 || p.dataAvail() > 0) { + // either command succeeds or command fails but with data payload + try {switch (rr.mRequest) { + /* cat libs/telephony/ril_commands.h \ | egrep "^ *{RIL_" \ | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/' -*/ + */ case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break; - case RIL_REQUEST_ENTER_SIM_PIN: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PUK: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseVoid(p); break; - case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseVoid(p); break; - case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseVoid(p); break; + case RIL_REQUEST_ENTER_SIM_PIN: ret = responseInts(p); break; + case RIL_REQUEST_ENTER_SIM_PUK: ret = responseInts(p); break; + case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseInts(p); break; + case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseInts(p); break; + case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseInts(p); break; + case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseInts(p); break; + case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseInts(p); break; case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break; case RIL_REQUEST_DIAL: ret = responseVoid(p); break; case RIL_REQUEST_GET_IMSI: ret = responseString(p); break; @@ -1973,7 +2083,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break; case RIL_REQUEST_UDUB: ret = responseVoid(p); break; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break; - case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_REQUEST_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_GPRS_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break; @@ -1997,7 +2107,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_ANSWER: ret = responseVoid(p); break; case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_FACILITY_LOCK: ret = responseInts(p); break; - case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseVoid(p); break; + case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseInts(p); break; case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break; case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break; @@ -2042,33 +2152,42 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_CDMA_BURST_DTMF: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SEND_SMS: ret = responseSMS(p); break; case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; - case RIL_REQUEST_GET_BROADCAST_CONFIG: ret = responseBR_SMS_CNF(p); break; - case RIL_REQUEST_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; - case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret = responseCDMA_BR_CNF(p); break; + case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: ret = responseGmsBroadcastConfig(p); break; + case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; + case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret = responseCdmaBroadcastConfig(p); break; case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; - case RIL_REQUEST_BROADCAST_ACTIVATION: ret = responseVoid(p); break; - case RIL_REQUEST_CDMA_VALIDATE_AKEY: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_VALIDATE_AKEY: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SUBSCRIPTION: ret = responseStrings(p); break; case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: ret = responseInts(p); break; case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: ret = responseVoid(p); break; case RIL_REQUEST_DEVICE_IDENTITY: ret = responseStrings(p); break; case RIL_REQUEST_GET_SMSC_ADDRESS: ret = responseString(p); break; case RIL_REQUEST_SET_SMSC_ADDRESS: ret = responseVoid(p); break; + case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; + case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: ret = responseVoid(p); break; default: throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest); //break; - }} catch (Throwable tr) { - // Exceptions here usually mean invalid RIL responses + }} catch (Throwable tr) { + // Exceptions here usually mean invalid RIL responses - Log.w(LOG_TAG, rr.serialString() + "< " - + requestToString(rr.mRequest) - + " exception, possible invalid RIL response", tr); + Log.w(LOG_TAG, rr.serialString() + "< " + + requestToString(rr.mRequest) + + " exception, possible invalid RIL response", tr); - if (rr.mResult != null) { - AsyncResult.forMessage(rr.mResult, null, tr); - rr.mResult.sendToTarget(); + if (rr.mResult != null) { + AsyncResult.forMessage(rr.mResult, null, tr); + rr.mResult.sendToTarget(); + } + rr.release(); + return; } + } + + if (error != 0) { + rr.onError(error, ret); rr.release(); return; } @@ -2167,7 +2286,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break; - case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break; case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break; case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break; @@ -2176,13 +2295,18 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break; case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; case RIL_UNSOL_SIM_REFRESH: ret = responseInts(p); break; - case RIL_UNSOL_CALL_RING: ret = responseVoid(p); break; + case RIL_UNSOL_CALL_RING: ret = responseCallRing(p); break; case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseString(p); break; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; + case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; + case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break; + case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break; + case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break; case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break; + default: throw new RuntimeException("Unrecognized unsol response: " + response); //break; (implied) @@ -2366,10 +2490,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { break; case RIL_UNSOL_CALL_RING: - if (RILJ_LOGD) unsljLog(response); + if (RILJ_LOGD) unsljLogRet(response, ret); if (mRingRegistrant != null) { - mRingRegistrant.notifyRegistrant(); + mRingRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); } break; @@ -2381,12 +2506,16 @@ public final class RIL extends BaseCommands implements CommandsInterface { } case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: + if (RILJ_LOGD) unsljLog(response); + if (mIccStatusChangedRegistrants != null) { mIccStatusChangedRegistrants.notifyRegistrants(); } break; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: + if (RILJ_LOGD) unsljLog(response); + SmsMessage sms = (SmsMessage) ret; if (mSMSRegistrant != null) { @@ -2396,19 +2525,64 @@ public final class RIL extends BaseCommands implements CommandsInterface { break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: - // TODO T: waiting for SMS BC feature + if (RILJ_LOGD) unsljLog(response); + + if (mGsmBroadcastSmsRegistrant != null) { + mGsmBroadcastSmsRegistrant + .notifyRegistrant(new AsyncResult(null, ret, null)); + } break; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: - if (Config.LOGD) { - if (RILJ_LOGD) riljLog("[UNSL]< RUIM_SMS_STORAGE_FULL"); - } + if (RILJ_LOGD) unsljLog(response); if (mIccSmsFullRegistrant != null) { mIccSmsFullRegistrant.notifyRegistrant(); } break; + case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: + if (RILJ_LOGD) unsljLog(response); + + if (mEmergencyCallbackModeRegistrant != null) { + mEmergencyCallbackModeRegistrant.notifyRegistrant(); + } + break; + + case RIL_UNSOL_CDMA_CALL_WAITING: + if (RILJ_LOGD) unsljLog(response); + + if (mCallWaitingInfoRegistrants != null) { + mCallWaitingInfoRegistrants.notifyRegistrants( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mOtaProvisionRegistrants != null) { + mOtaProvisionRegistrants.notifyRegistrants( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_CDMA_INFO_REC: + ArrayList<CdmaInformationRecords> listInfoRecs; + + try { + listInfoRecs = (ArrayList<CdmaInformationRecords>)ret; + } catch (ClassCastException e) { + Log.e(LOG_TAG, "Unexpected exception casting to listInfoRecs", e); + break; + } + + for (CdmaInformationRecords rec : listInfoRecs) { + if (RILJ_LOGD) unsljLogRet(response, rec); + notifyRegistrantsCdmaInfoRec(rec); + } + break; + case RIL_UNSOL_OEM_HOOK_RAW: if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret)); if (mUnsolOemHookRawRegistrant != null) { @@ -2524,13 +2698,14 @@ public final class RIL extends BaseCommands implements CommandsInterface { private Object responseSMS(Parcel p) { - int messageRef; + int messageRef, errorCode; String ackPDU; messageRef = p.readInt(); ackPDU = p.readString(); + errorCode = p.readInt(); - SmsResponse response = new SmsResponse(messageRef, ackPDU); + SmsResponse response = new SmsResponse(messageRef, ackPDU, errorCode); return response; } @@ -2547,6 +2722,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { String s = p.readString(); + if (RILJ_LOGD) riljLog("< iccIO: " + + " 0x" + Integer.toHexString(sw1) + + " 0x" + Integer.toHexString(sw2) + " " + + s); + return new IccIoResult(sw1, sw2, s); } @@ -2658,33 +2838,24 @@ public final class RIL extends BaseCommands implements CommandsInterface { dc.als = p.readInt(); voiceSettings = p.readInt(); dc.isVoice = (0 == voiceSettings) ? false : true; - - //dc.isVoicePrivacy = (0 != p.readInt()); - int voicePrivacy = p.readInt(); - dc.isVoicePrivacy = (0 != voicePrivacy); - + dc.isVoicePrivacy = (0 != p.readInt()); dc.number = p.readString(); int np = p.readInt(); dc.numberPresentation = DriverCall.presentationFromCLIP(np); dc.name = p.readString(); dc.namePresentation = p.readInt(); - // Make sure there's a leading + on addresses with a TOA - // of 145 - - dc.number = PhoneNumberUtils.stringFromStringAndTOA( - dc.number, dc.TOA); + // Make sure there's a leading + on addresses with a TOA of 145 + dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA); response.add(dc); - if ( RILConstants.CDMA_VOICE_PRIVACY == voiceSettings ) { + if (dc.isVoicePrivacy) { mVoicePrivacyOnRegistrants.notifyRegistrants(); - Log.d(LOG_TAG, "InCall VoicePrivacy is enabled: " + - Integer.toString(voiceSettings)); + Log.d(LOG_TAG, "InCall VoicePrivacy is enabled"); } else { mVoicePrivacyOffRegistrants.notifyRegistrants(); - Log.d(LOG_TAG, "InCall VoicePrivacy is disabled: " + - Integer.toString(voiceSettings)); + Log.d(LOG_TAG, "InCall VoicePrivacy is disabled"); } } @@ -2696,21 +2867,21 @@ public final class RIL extends BaseCommands implements CommandsInterface { private Object responseDataCallList(Parcel p) { int num; - ArrayList<PDPContextState> response; + ArrayList<DataCallState> response; num = p.readInt(); - response = new ArrayList<PDPContextState>(num); + response = new ArrayList<DataCallState>(num); for (int i = 0; i < num; i++) { - PDPContextState pdp = new PDPContextState(); + DataCallState dataCall = new DataCallState(); - pdp.cid = p.readInt(); - pdp.active = p.readInt(); - pdp.type = p.readString(); - pdp.apn = p.readString(); - pdp.address = p.readString(); + dataCall.cid = p.readInt(); + dataCall.active = p.readInt(); + dataCall.type = p.readString(); + dataCall.apn = p.readString(); + dataCall.address = p.readString(); - response.add(pdp); + response.add(dataCall); } return response; @@ -2751,51 +2922,66 @@ public final class RIL extends BaseCommands implements CommandsInterface { response = new ArrayList<NeighboringCellInfo>(num); for (int i = 0 ; i < num ; i++) { - try { - int rssi = p.readInt(); - int cid = Integer.valueOf(p.readString(), 16); - cell = new NeighboringCellInfo(rssi, cid); - response.add(cell); - } catch ( Exception e) { - } + int rssi = p.readInt(); + int cid = Integer.valueOf(p.readString(), 16); + cell = new NeighboringCellInfo(rssi, cid); + response.add(cell); } return response; } - private Object - responseBR_SMS_CNF(Parcel p) { - // TODO - return null; + private Object responseGmsBroadcastConfig(Parcel p) { + int num; + ArrayList<SmsBroadcastConfigInfo> response; + SmsBroadcastConfigInfo info; + + num = p.readInt(); + response = new ArrayList<SmsBroadcastConfigInfo>(num); + + for (int i = 0; i < num; i++) { + int fromId = p.readInt(); + int toId = p.readInt(); + int fromScheme = p.readInt(); + int toScheme = p.readInt(); + boolean selected = (p.readInt() == 1); + + info = new SmsBroadcastConfigInfo(fromId, toId, fromScheme, + toScheme, selected); + response.add(info); + } + return response; } private Object - responseCDMA_BR_CNF(Parcel p) { + responseCdmaBroadcastConfig(Parcel p) { int numServiceCategories; int response[]; numServiceCategories = p.readInt(); if (numServiceCategories == 0) { + // TODO(Teleca) TODO(Moto): The logic of providing default + // values should not be done by this transport layer. And + // needs to be done by the vendor ril or application logic. + // TODO(Google): Remove ASAP int numInts; numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES * CDMA_BSI_NO_OF_INTS_STRUCT + 1; response = new int[numInts]; - // indicate that a zero length table was received - response[0] = 0; - //for all supported service categories set 'english' as default language - //and selection status to false - for (int i = 1, j = 1 - ; i <= (CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES - * CDMA_BSI_NO_OF_INTS_STRUCT) - ; i += CDMA_BSI_NO_OF_INTS_STRUCT, j++ ) { - response[i] = j; - response[i+1] = 1; - response[i+2] = 0; + // Faking a default record for all possible records. + response[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES; + + // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as + // default language and selection status to false for all. + for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT ) { + response[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT; + response[i + 1] = 1; + response[i + 2] = 0; } } else { int numInts; - numInts = numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT + 1; + numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1; response = new int[numInts]; response[0] = numServiceCategories; @@ -2807,6 +2993,116 @@ public final class RIL extends BaseCommands implements CommandsInterface { return response; } + private Object + responseSignalStrength(Parcel p) { + int numInts = 7; + int response[]; + + /* TODO: Add SignalStrength class to match RIL_SignalStrength */ + response = new int[numInts]; + for (int i = 0 ; i < numInts ; i++) { + response[i] = p.readInt(); + } + + return response; + } + + private ArrayList<CdmaInformationRecords> + responseCdmaInformationRecord(Parcel p) { + int numberOfInfoRecs; + ArrayList<CdmaInformationRecords> response; + + /** + * Loop through all of the information records unmarshalling them + * and converting them to Java Objects. + */ + numberOfInfoRecs = p.readInt(); + response = new ArrayList<CdmaInformationRecords>(numberOfInfoRecs); + + for (int i = 0; i < numberOfInfoRecs; i++) { + CdmaInformationRecords InfoRec = new CdmaInformationRecords(p); + response.add(InfoRec); + } + + return response; + } + + private Object + responseCdmaCallWaiting(Parcel p) { + CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification(); + + notification.number = p.readString(); + notification.numberPresentation = p.readInt(); + notification.name = p.readString(); + notification.namePresentation = notification.numberPresentation; + notification.isPresent = p.readInt(); + notification.signalType = p.readInt(); + notification.alertPitch = p.readInt(); + notification.signal = p.readInt(); + + return notification; + } + + private Object + responseCallRing(Parcel p){ + char response[] = new char[4]; + + response[0] = (char) p.readInt(); // isPresent + response[1] = (char) p.readInt(); // signalType + response[2] = (char) p.readInt(); // alertPitch + response[3] = (char) p.readInt(); // signal + + return response; + } + + private void + notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) { + int response = RIL_UNSOL_CDMA_INFO_REC; + if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) { + if (mDisplayInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mDisplayInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) { + if (mSignalInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mSignalInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) { + if (mNumberInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mNumberInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) { + if (mRedirNumInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mRedirNumInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) { + if (mLineControlInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mLineControlInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) { + if (mT53ClirInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mT53ClirInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) { + if (mT53AudCntrlInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.record); + mT53AudCntrlInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.record, null)); + } + } + } + static String requestToString(int request) { /* @@ -2902,11 +3198,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_CDMA_BURST_DTMF: return "RIL_REQUEST_CDMA_BURST_DTMF"; case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_REQUEST_CDMA_SEND_SMS"; case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE"; - case RIL_REQUEST_GET_BROADCAST_CONFIG: return "RIL_REQUEST_GET_BROADCAST_CONFIG"; - case RIL_REQUEST_SET_BROADCAST_CONFIG: return "RIL_REQUEST_SET_BROADCAST_CONFIG"; + case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG"; + case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG"; case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG"; case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG"; - case RIL_REQUEST_BROADCAST_ACTIVATION: return "RIL_REQUEST_BROADCAST_ACTIVATION"; + case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION"; case RIL_REQUEST_CDMA_VALIDATE_AKEY: return "RIL_REQUEST_CDMA_VALIDATE_AKEY"; case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION"; case RIL_REQUEST_CDMA_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SUBSCRIPTION"; @@ -2915,6 +3211,9 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_DEVICE_IDENTITY: return "RIL_REQUEST_DEVICE_IDENTITY"; case RIL_REQUEST_GET_SMSC_ADDRESS: return "RIL_REQUEST_GET_SMSC_ADDRESS"; case RIL_REQUEST_SET_SMSC_ADDRESS: return "RIL_REQUEST_SET_SMSC_ADDRESS"; + case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE"; + case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS"; + case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING"; default: return "<unknown request>"; } } @@ -2947,8 +3246,16 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL"; case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; - case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "RIL_UNSOL_RESTRICTED_STATE_CHANGED"; - case RIL_UNSOL_OEM_HOOK_RAW: return "RIL_UNSOL_OEM_HOOK_RAW"; + case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED"; + case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_RESPONSE_CDMA_NEW_SMS"; + case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_RESPONSE_NEW_BROADCAST_SMS"; + case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL"; + case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED"; + case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE"; + case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING"; + case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS"; + case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC"; + case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW"; default: return "<unknown reponse>"; } } @@ -3048,7 +3355,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { /** * {@inheritDoc} */ - public void queryTTYModeEnabled(Message response) { + public void queryTTYMode(Message response) { RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response); @@ -3058,12 +3365,12 @@ public final class RIL extends BaseCommands implements CommandsInterface { /** * {@inheritDoc} */ - public void setTTYModeEnabled(boolean enable, Message response) { + public void setTTYMode(int ttyMode, Message response) { RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_SET_TTY_MODE, response); rr.mp.writeInt(1); - rr.mp.writeInt(enable ? 1 : 0); + rr.mp.writeInt(ttyMode); send(rr); } @@ -3075,7 +3382,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { sendCDMAFeatureCode(String FeatureCode, Message response) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_FLASH, response); - rr.mp.writeInt(1); rr.mp.writeString(FeatureCode); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) @@ -3090,11 +3396,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { send(rr); } + // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, response); - rr.mp.writeInt(configValuesArray[0]); - for(int i = 1; i <= (configValuesArray[0] * 3); i++) { + for(int i = 0; i < configValuesArray.length; i++) { rr.mp.writeInt(configValuesArray[i]); } @@ -3103,11 +3409,22 @@ public final class RIL extends BaseCommands implements CommandsInterface { send(rr); } - public void activateCdmaBroadcastSms(int activate, Message response) { + public void setCdmaBroadcastActivation(boolean activate, Message response) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, response); rr.mp.writeInt(1); - rr.mp.writeInt(activate); + rr.mp.writeInt(activate ? 0 :1); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void exitEmergencyCallbackMode(Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, response); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index bcf51417aad8..b2e16c74ec3a 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -16,6 +16,13 @@ package com.android.internal.telephony; +/** + * TODO: This should probably not be an interface see + * http://www.javaworld.com/javaworld/javaqa/2001-06/01-qa-0608-constants.html and google with + * http://www.google.com/search?q=interface+constants&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a + * + * Also they should all probably be static final. + */ /** * {@hide} @@ -51,7 +58,7 @@ public interface RILConstants { int NETWORK_MODE_EVDO_NO_CDMA = 6; /* EvDo only */ int NETWORK_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL) AVAILABLE Application Settings menu*/ - int PREFERRED_NETWORK_MODE = NETWORK_MODE_GSM_ONLY; + int PREFERRED_NETWORK_MODE = NETWORK_MODE_WCDMA_PREF; /* CDMA subscription source. See ril.h RIL_REQUEST_CDMA_SET_SUBSCRIPTION */ int SUBSCRIPTION_FROM_RUIM = 0; /* CDMA subscription from RUIM when available */ @@ -67,8 +74,9 @@ public interface RILConstants { int CDM_TTY_MODE_DISABLED = 0; int CDM_TTY_MODE_ENABLED = 1; - byte CDMA_VOICE_PRIVACY = 0x70; /* "p" value used in Ril_Call.isVoice if Privacy - is active */ + int CDM_TTY_FULL_MODE = 1; + int CDM_TTY_HCO_MODE = 2; + int CDM_TTY_VCO_MODE = 3; /* cat include/telephony/ril.h | \ @@ -198,9 +206,9 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_CDMA_VALIDATE_AKEY = 86; int RIL_REQUEST_CDMA_SEND_SMS = 87; int RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE = 88; - int RIL_REQUEST_GET_BROADCAST_CONFIG = 89; - int RIL_REQUEST_SET_BROADCAST_CONFIG = 90; - int RIL_REQUEST_BROADCAST_ACTIVATION = 91; + int RIL_REQUEST_GSM_GET_BROADCAST_CONFIG = 89; + int RIL_REQUEST_GSM_SET_BROADCAST_CONFIG = 90; + int RIL_REQUEST_GSM_BROADCAST_ACTIVATION = 91; int RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG = 92; int RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG = 93; int RIL_REQUEST_CDMA_BROADCAST_ACTIVATION = 94; @@ -208,8 +216,11 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM = 96; int RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM = 97; int RIL_REQUEST_DEVICE_IDENTITY = 98; + int RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE = 99; int RIL_REQUEST_GET_SMSC_ADDRESS = 100; int RIL_REQUEST_SET_SMSC_ADDRESS = 101; + int RIL_REQUEST_REPORT_SMS_MEMORY_STATUS = 102; + int RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING = 103; int RIL_UNSOL_RESPONSE_BASE = 1000; int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index f2bd361ac1bb..890ea6352b78 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -20,11 +20,13 @@ import android.app.Activity; import android.app.PendingIntent; import android.app.AlertDialog; import android.app.PendingIntent.CanceledException; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.DialogInterface; +import android.content.IntentFilter; import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; @@ -32,6 +34,7 @@ import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.provider.Settings; @@ -75,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; @@ -113,7 +117,7 @@ public abstract class SMSDispatcher extends Handler { /** Maximum number of times to retry sending a failed SMS. */ private static final int MAX_SEND_RETRIES = 3; /** Delay before next send attempt on a failed SMS, in milliseconds. */ - private static final int SEND_RETRY_DELAY = 2000; + private static final int SEND_RETRY_DELAY = 2000; /** single part SMS */ private static final int SINGLE_PART_SMS = 1; @@ -122,15 +126,30 @@ public abstract class SMSDispatcher extends Handler { * CONCATENATED_16_BIT_REFERENCE message set. Should be * incremented for each set of concatenated messages. */ - protected static int sConcatenatedRef; + private static int sConcatenatedRef; private SmsCounter mCounter; private SmsTracker mSTracker; + /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ + private PowerManager.WakeLock mWakeLock; + + /** + * Hold the wake lock for 5 seconds, which should be enough time for + * any receiver(s) to grab its own wake lock. + */ + private final int WAKE_LOCK_TIMEOUT = 5000; + private static SmsMessage mSmsMessage; private static SmsMessageBase mSmsMessageBase; private SmsMessageBase.SubmitPduBase mSubmitPduBase; + private boolean mStorageAvailable = true; + + protected static int getNextConcatenatedRef() { + sConcatenatedRef += 1; + return sConcatenatedRef; + } /** * Implement the per-application based SMS control, which only allows @@ -155,11 +174,11 @@ public abstract class SMSDispatcher extends Handler { /** * Check to see if an application allow to send new SMS messages - * + * * @param appName is the application sending sms * @param smsWaiting is the number of new sms wants to be sent - * @return true if application is allowed to send the requested number - * of new sms messages + * @return true if application is allowed to send the requested number + * of new sms messages */ boolean check(String appName, int smsWaiting) { if (!mSmsStamp.containsKey(appName)) { @@ -178,7 +197,7 @@ public abstract class SMSDispatcher extends Handler { sent.remove(0); } - + if ( (sent.size() + smsWaiting) <= mMaxAllowed) { for (int i = 0; i < smsWaiting; i++ ) { sent.add(ct); @@ -191,14 +210,16 @@ public abstract class SMSDispatcher extends Handler { protected SMSDispatcher(PhoneBase phone) { mPhone = phone; - mWapPush = new WapPushOverSms(phone); + mWapPush = new WapPushOverSms(phone, this); mContext = phone.getContext(); mResolver = mContext.getContentResolver(); mCm = phone.mCM; mSTracker = null; + createWakelock(); + int check_period = Settings.Gservices.getInt(mResolver, - Settings.Gservices.SMS_OUTGOING_CEHCK_INTERVAL_MS, + Settings.Gservices.SMS_OUTGOING_CHECK_INTERVAL_MS, DEFAULT_SMS_CHECK_PERIOD); int max_count = Settings.Gservices.getInt(mResolver, Settings.Gservices.SMS_OUTGOING_CEHCK_MAX_COUNT, @@ -211,6 +232,15 @@ public abstract class SMSDispatcher extends Handler { // Don't always start message ref at 0. sConcatenatedRef = new Random().nextInt(256); + + // Register for device storage intents. Use these to notify the RIL + // that storage for SMS is or is not available. + // TODO: Revisit this for a later release. Storage reporting should + // rely more on application indication. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); + mContext.registerReceiver(mResultReceiver, filter); } public void dispose() { @@ -252,16 +282,27 @@ public abstract class SMSDispatcher extends Handler { ar = (AsyncResult) msg.obj; - // FIXME only acknowledge on store - acknowledgeLastIncomingSms(true, null); - if (ar.exception != null) { Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); return; } sms = (SmsMessage) ar.result; - dispatchMessage(sms.mWrappedSmsMessage); + try { + if (mStorageAvailable) { + 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); + } + } catch (RuntimeException ex) { + acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); + } break; @@ -298,13 +339,35 @@ public abstract class SMSDispatcher extends Handler { sendMultipartSms(mSTracker); } else { sendSms(mSTracker); - } + } mSTracker = null; } break; } } + private void createWakelock() { + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); + mWakeLock.setReferenceCounted(true); + } + + /** + * Grabs a wake lock and sends intent as an ordered broadcast. + * The resultReceiver will check for errors and ACK/NACK back + * to the RIL. + * + * @param intent intent to broadcast + * @param permission Receivers are required to have this permission + */ + void dispatch(Intent intent, String permission) { + // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any + // receivers time to take their own wake locks. + mWakeLock.acquire(WAKE_LOCK_TIMEOUT); + mContext.sendOrderedBroadcast(intent, permission, mResultReceiver, + this, Activity.RESULT_OK, null, null); + } + /** * Called when SIM_FULL message is received from the RIL. Notifies interested * parties that SIM storage for SMS messages is full. @@ -312,7 +375,8 @@ public abstract class SMSDispatcher extends Handler { private void handleIccFull(){ // broadcast SIM_FULL intent Intent intent = new Intent(Intents.SIM_FULL_ACTION); - mPhone.getContext().sendBroadcast(intent, "android.permission.RECEIVE_SMS"); + mWakeLock.acquire(WAKE_LOCK_TIMEOUT); + mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); } /** @@ -412,19 +476,28 @@ 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); /** * If this is the last part send the parts out to the application, otherwise * 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, int referenceNumber, - int sequence, int count, int destinationPort) { + protected int processMessagePart(SmsMessageBase sms, + SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) { + // Lookup all other related parts StringBuilder where = new StringBuilder("reference_number ="); - where.append(referenceNumber); + where.append(concatRef.refNumber); where.append(" AND address = ?"); String[] whereArgs = new String[] {sms.getOriginatingAddress()}; @@ -433,28 +506,27 @@ public abstract class SMSDispatcher extends Handler { try { cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); int cursorCount = cursor.getCount(); - if (cursorCount != count - 1) { + if (cursorCount != concatRef.msgCount - 1) { // We don't have all the parts yet, store this one away ContentValues values = new ContentValues(); values.put("date", new Long(sms.getTimestampMillis())); values.put("pdu", HexDump.toHexString(sms.getPdu())); values.put("address", sms.getOriginatingAddress()); - values.put("reference_number", referenceNumber); - values.put("count", count); - values.put("sequence", sequence); - if (destinationPort != -1) { - values.put("destination_port", destinationPort); + values.put("reference_number", concatRef.refNumber); + values.put("count", concatRef.msgCount); + values.put("sequence", concatRef.seqNumber); + if (portAddrs != null) { + values.put("destination_port", portAddrs.destPort); } mResolver.insert(mRawUri, values); - - return; + return Intents.RESULT_SMS_HANDLED; } // All the parts are in place, deal with them int pduColumn = cursor.getColumnIndex("pdu"); int sequenceColumn = cursor.getColumnIndex("sequence"); - pdus = new byte[count][]; + pdus = new byte[concatRef.msgCount][]; for (int i = 0; i < cursorCount; i++) { cursor.moveToNext(); int cursorSequence = (int)cursor.getLong(sequenceColumn); @@ -462,43 +534,48 @@ public abstract class SMSDispatcher extends Handler { cursor.getString(pduColumn)); } // This one isn't in the DB, so add it - pdus[sequence - 1] = sms.getPdu(); + pdus[concatRef.seqNumber - 1] = sms.getPdu(); // Remove the parts from the database 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. + // TODO: Would OUT_OF_MEMORY be more appropriate? + return Intents.RESULT_SMS_GENERIC_ERROR; } finally { if (cursor != null) cursor.close(); } + /** + * TODO(cleanup): The following code has duplicated logic with + * the radio-specific dispatchMessage code, which is fragile, + * in addition to being redundant. Instead, if this method + * maybe returned the reassembled message (or just contents), + * the following code (which is not really related to + * reconstruction) could be better consolidated. + */ + // Dispatch the PDUs to applications - switch (destinationPort) { - case SmsHeader.PORT_WAP_PUSH: { - // Build up the data stream - ByteArrayOutputStream output = new ByteArrayOutputStream(); - for (int i = 0; i < count; i++) { - SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); - byte[] data = msg.getUserData(); - output.write(data, 0, data.length); + if (portAddrs != null) { + if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { + // Build up the data stream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + for (int i = 0; i < concatRef.msgCount; i++) { + SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); + byte[] data = msg.getUserData(); + output.write(data, 0, data.length); + } + // Handle the PUSH + return mWapPush.dispatchWapPdu(output.toByteArray()); + } else { + // The messages were sent to a port, so concoct a URI for it + dispatchPortAddressedPdus(pdus, portAddrs.destPort); } - - // Handle the PUSH - mWapPush.dispatchWapPdu(output.toByteArray()); - break; - } - - case -1: + } else { // The messages were not sent to a port dispatchPdus(pdus); - break; - - default: - // The messages were sent to a port, so concoct a URI for it - dispatchPortAddressedPdus(pdus, destinationPort); - break; } + return Activity.RESULT_OK; } /** @@ -509,8 +586,7 @@ public abstract class SMSDispatcher extends Handler { protected void dispatchPdus(byte[][] pdus) { Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); intent.putExtra("pdus", pdus); - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_SMS"); + dispatch(intent, "android.permission.RECEIVE_SMS"); } /** @@ -523,8 +599,7 @@ public abstract class SMSDispatcher extends Handler { Uri uri = Uri.parse("sms://localhost:" + port); Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); intent.putExtra("pdus", pdus); - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_SMS"); + dispatch(intent, "android.permission.RECEIVE_SMS"); } @@ -649,7 +724,7 @@ public abstract class SMSDispatcher extends Handler { /** * Send the multi-part SMS based on multipart Sms tracker - * + * * @param tracker holds the multipart Sms tracker ready to be sent */ protected abstract void sendMultipartSms (SmsTracker tracker); @@ -688,13 +763,15 @@ public abstract class SMSDispatcher extends Handler { /** * Send an acknowledge message. * @param success indicates that last message was successfully received. + * @param result result code indicating any error * @param response callback message sent when operation completes. */ - protected abstract void acknowledgeLastIncomingSms(boolean success, Message response); + protected abstract void acknowledgeLastIncomingSms(boolean success, + int result, Message response); /** * Check if a SmsTracker holds multi-part Sms - * + * * @param tracker a SmsTracker could hold a multi-part Sms * @return true for tracker holds Multi-parts Sms */ @@ -725,7 +802,7 @@ public abstract class SMSDispatcher extends Handler { mRetryCount = 0; } } - + protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent, PendingIntent deliveryIntent) { return new SmsTracker(data, sentIntent, deliveryIntent); @@ -741,4 +818,28 @@ public abstract class SMSDispatcher extends Handler { } } }; + + private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) { + mStorageAvailable = false; + mCm.reportSmsMemoryStatus(false, null); + } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) { + mStorageAvailable = true; + mCm.reportSmsMemoryStatus(true, null); + } else { + // Assume the intent is one of the SMS receive intents that + // was sent as an ordered broadcast. Check result and ACK. + int rc = getResultCode(); + boolean success = (rc == Activity.RESULT_OK) + || (rc == Intents.RESULT_SMS_HANDLED); + + // For a multi-part message, this only ACKs the last part. + // Previous parts were ACK'd as they were received. + acknowledgeLastIncomingSms(success, rc, null); + } + } + + }; } diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java index 7274e9972308..bdcf3f7099f2 100644 --- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java @@ -22,6 +22,7 @@ import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; import android.telephony.ServiceState; +import android.telephony.SignalStrength; /** * {@hide} @@ -50,6 +51,8 @@ public abstract class ServiceStateTracker extends Handler { public ServiceState ss; protected ServiceState newSS; + public SignalStrength mSignalStrength; + // Used as a unique identifier to track requests associated with a poll // and ignore stale responses.The value is a count-down of expected responses // in this pollingContext @@ -104,13 +107,15 @@ public abstract class ServiceStateTracker extends Handler { protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25; protected static final int EVENT_RUIM_READY = 26; protected static final int EVENT_RUIM_RECORDS_LOADED = 27; - protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA = 28; - protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 29; - protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 30; - protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 31; - protected static final int EVENT_GET_LOC_DONE_CDMA = 32; - protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA = 33; - protected static final int EVENT_NV_LOADED = 34; + protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 28; + protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 29; + protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 30; + protected static final int EVENT_GET_LOC_DONE_CDMA = 31; + protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA = 32; + protected static final int EVENT_NV_LOADED = 33; + protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION = 34; + protected static final int EVENT_NV_READY = 35; + protected static final int EVENT_ERI_FILE_LOADED = 36; //***** Time Zones protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; @@ -142,12 +147,18 @@ public abstract class ServiceStateTracker extends Handler { "uk", // U.K }; + //***** Registration denied reason + protected static final String REGISTRATION_DENIED_GEN = "General"; + protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure"; //***** Constructors public ServiceStateTracker() { } + public boolean getDesiredPowerState() { + return mDesiredPowerState; + } /** * Registration point for combined roaming on diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java index 64b884efa0c5..7872eec1ad20 100644 --- a/telephony/java/com/android/internal/telephony/SmsHeader.java +++ b/telephony/java/com/android/internal/telephony/SmsHeader.java @@ -16,227 +16,242 @@ package com.android.internal.telephony; +import android.telephony.SmsMessage; + import com.android.internal.util.HexDump; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + import java.util.ArrayList; /** - * This class represents a SMS user data header. - * + * SMS user data header, as specified in TS 23.040 9.2.3.24. */ public class SmsHeader { - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int CONCATENATED_8_BIT_REFERENCE = 0x00; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int SPECIAL_SMS_MESSAGE_INDICATION = 0x01; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int APPLICATION_PORT_ADDRESSING_8_BIT = 0x04; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int APPLICATION_PORT_ADDRESSING_16_BIT= 0x05; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int CONCATENATED_16_BIT_REFERENCE = 0x08; - public static final int PORT_WAP_PUSH = 2948; - public static final int PORT_WAP_WSP = 9200; + // TODO(cleanup): this datastructure is generally referred to as + // the 'user data header' or UDH, and so the class name should + // change to reflect this... - private byte[] m_data; - private ArrayList<Element> m_elements = new ArrayList<Element>(); - public int nbrOfHeaders; - - /** - * Creates an SmsHeader object from raw user data header bytes. - * - * @param data is user data header bytes - * @return an SmsHeader object + /** SMS user data header information element identifiers. + * (see TS 23.040 9.2.3.24) */ - public static SmsHeader parse(byte[] data) { - SmsHeader header = new SmsHeader(); - header.m_data = data; - - int index = 0; - header.nbrOfHeaders = 0; - while (index < data.length) { - int id = data[index++] & 0xff; - int length = data[index++] & 0xff; - byte[] elementData = new byte[length]; - System.arraycopy(data, index, elementData, 0, length); - header.add(new Element(id, elementData)); - index += length; - header.nbrOfHeaders++; - } + public static final int ELT_ID_CONCATENATED_8_BIT_REFERENCE = 0x00; + public static final int ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION = 0x01; + public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT = 0x04; + public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT = 0x05; + public static final int ELT_ID_SMSC_CONTROL_PARAMS = 0x06; + public static final int ELT_ID_UDH_SOURCE_INDICATION = 0x07; + public static final int ELT_ID_CONCATENATED_16_BIT_REFERENCE = 0x08; + public static final int ELT_ID_WIRELESS_CTRL_MSG_PROTOCOL = 0x09; + public static final int ELT_ID_TEXT_FORMATTING = 0x0A; + public static final int ELT_ID_PREDEFINED_SOUND = 0x0B; + public static final int ELT_ID_USER_DEFINED_SOUND = 0x0C; + public static final int ELT_ID_PREDEFINED_ANIMATION = 0x0D; + public static final int ELT_ID_LARGE_ANIMATION = 0x0E; + public static final int ELT_ID_SMALL_ANIMATION = 0x0F; + public static final int ELT_ID_LARGE_PICTURE = 0x10; + public static final int ELT_ID_SMALL_PICTURE = 0x11; + public static final int ELT_ID_VARIABLE_PICTURE = 0x12; + public static final int ELT_ID_USER_PROMPT_INDICATOR = 0x13; + public static final int ELT_ID_EXTENDED_OBJECT = 0x14; + public static final int ELT_ID_REUSED_EXTENDED_OBJECT = 0x15; + public static final int ELT_ID_COMPRESSION_CONTROL = 0x16; + public static final int ELT_ID_OBJECT_DISTR_INDICATOR = 0x17; + public static final int ELT_ID_STANDARD_WVG_OBJECT = 0x18; + public static final int ELT_ID_CHARACTER_SIZE_WVG_OBJECT = 0x19; + public static final int ELT_ID_EXTENDED_OBJECT_DATA_REQUEST_CMD = 0x1A; + public static final int ELT_ID_RFC_822_EMAIL_HEADER = 0x20; + public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT = 0x21; + public static final int ELT_ID_REPLY_ADDRESS_ELEMENT = 0x22; + public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION = 0x23; - return header; - } + public static final int PORT_WAP_PUSH = 2948; + public static final int PORT_WAP_WSP = 9200; - public SmsHeader() { } + public static class PortAddrs { + public int destPort; + public int origPort; + public boolean areEightBits; + } - /** - * Returns the list of SmsHeader Elements that make up the header. - * - * @return the list of SmsHeader Elements. - */ - public ArrayList<Element> getElements() { - return m_elements; + public static class ConcatRef { + public int refNumber; + public int seqNumber; + public int msgCount; + public boolean isEightBits; } /** - * Add an element to the SmsHeader. - * - * @param element to add. + * A header element that is not explicitly parsed, meaning not + * PortAddrs or ConcatRef. */ - public void add(Element element) { - m_elements.add(element); + public static class MiscElt { + public int id; + public byte[] data; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("UDH LENGTH: " + m_data.length + " octets"); - builder.append("UDH: "); - builder.append(HexDump.toHexString(m_data)); - builder.append("\n"); - - for (Element e : getElements()) { - builder.append(" 0x" + HexDump.toHexString((byte)e.getID()) + " - "); - switch (e.getID()) { - case CONCATENATED_8_BIT_REFERENCE: { - builder.append("Concatenated Short Message 8bit ref\n"); - byte[] data = e.getData(); - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length) - + ") Bytes - Information Element\n"); - builder.append(" " + data[0] + " : SM reference number\n"); - builder.append(" " + data[1] + " : number of messages\n"); - builder.append(" " + data[2] + " : this SM sequence number\n"); - break; - } - - case CONCATENATED_16_BIT_REFERENCE: { - builder.append("Concatenated Short Message 16bit ref\n"); - byte[] data = e.getData(); - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length) - + ") Bytes - Information Element\n"); - builder.append(" " + (data[0] & 0xff) * 256 + (data[1] & 0xff) - + " : SM reference number\n"); - builder.append(" " + data[2] + " : number of messages\n"); - builder.append(" " + data[3] + " : this SM sequence number\n"); - break; - } - - case APPLICATION_PORT_ADDRESSING_8_BIT: - { - builder.append("Application port addressing 8bit\n"); - byte[] data = e.getData(); - - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString( - (byte)data.length) + ") Bytes - Information Element\n"); - - int source = (data[0] & 0xff); - builder.append(" " + source + " : DESTINATION port\n"); - - int dest = (data[1] & 0xff); - builder.append(" " + dest + " : SOURCE port\n"); - break; - } - - case APPLICATION_PORT_ADDRESSING_16_BIT: { - builder.append("Application port addressing 16bit\n"); - byte[] data = e.getData(); - - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length) - + ") Bytes - Information Element\n"); + public PortAddrs portAddrs; + public ConcatRef concatRef; + public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>(); - int source = (data[0] & 0xff) << 8; - source |= (data[1] & 0xff); - builder.append(" " + source + " : DESTINATION port\n"); + public SmsHeader() {} - int dest = (data[2] & 0xff) << 8; - dest |= (data[3] & 0xff); - builder.append(" " + dest + " : SOURCE port\n"); - break; + /** + * Create structured SmsHeader object from serialized byte array representation. + * (see TS 23.040 9.2.3.24) + * @param data is user data header bytes + * @return SmsHeader object + */ + public static SmsHeader fromByteArray(byte[] data) { + ByteArrayInputStream inStream = new ByteArrayInputStream(data); + SmsHeader smsHeader = new SmsHeader(); + while (inStream.available() > 0) { + /** + * NOTE: as defined in the spec, ConcatRef and PortAddr + * fields should not reoccur, but if they do the last + * occurrence is to be used. Also, for ConcatRef + * elements, if the count is zero, sequence is zero, or + * sequence is larger than count, the entire element is to + * be ignored. + */ + int id = inStream.read(); + int length = inStream.read(); + ConcatRef concatRef; + PortAddrs portAddrs; + switch (id) { + case ELT_ID_CONCATENATED_8_BIT_REFERENCE: + concatRef = new ConcatRef(); + concatRef.refNumber = inStream.read(); + concatRef.msgCount = inStream.read(); + concatRef.seqNumber = inStream.read(); + concatRef.isEightBits = true; + if (concatRef.msgCount != 0 && concatRef.seqNumber != 0 && + concatRef.seqNumber <= concatRef.msgCount) { + smsHeader.concatRef = concatRef; } - - default: { - builder.append("Unknown element\n"); - break; + break; + case ELT_ID_CONCATENATED_16_BIT_REFERENCE: + concatRef = new ConcatRef(); + concatRef.refNumber = (inStream.read() << 8) | inStream.read(); + concatRef.msgCount = inStream.read(); + concatRef.seqNumber = inStream.read(); + concatRef.isEightBits = false; + if (concatRef.msgCount != 0 && concatRef.seqNumber != 0 && + concatRef.seqNumber <= concatRef.msgCount) { + smsHeader.concatRef = concatRef; } + break; + case ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT: + portAddrs = new PortAddrs(); + portAddrs.destPort = inStream.read(); + portAddrs.origPort = inStream.read(); + portAddrs.areEightBits = true; + smsHeader.portAddrs = portAddrs; + break; + case ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT: + portAddrs = new PortAddrs(); + portAddrs.destPort = (inStream.read() << 8) | inStream.read(); + portAddrs.origPort = (inStream.read() << 8) | inStream.read(); + portAddrs.areEightBits = false; + smsHeader.portAddrs = portAddrs; + break; + default: + MiscElt miscElt = new MiscElt(); + miscElt.id = id; + miscElt.data = new byte[length]; + inStream.read(miscElt.data, 0, length); + smsHeader.miscEltList.add(miscElt); } } - - return builder.toString(); - } - - private int calcSize() { - int size = 1; // +1 for the UDHL field - for (Element e : m_elements) { - size += e.getData().length; - size += 2; // 1 byte ID, 1 byte length - } - - return size; + return smsHeader; } /** - * Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24. + * Create serialized byte array representation from structured SmsHeader object. + * (see TS 23.040 9.2.3.24) * @return Byte array representing the SmsHeader */ - public byte[] toByteArray() { - if (m_elements.size() == 0) return null; - - if (m_data == null) { - int size = calcSize(); - int cur = 1; - m_data = new byte[size]; - - m_data[0] = (byte) (size-1); // UDHL does not include itself - - for (Element e : m_elements) { - int length = e.getData().length; - m_data[cur++] = (byte) e.getID(); - m_data[cur++] = (byte) length; - System.arraycopy(e.getData(), 0, m_data, cur, length); - cur += length; - } + public static byte[] toByteArray(SmsHeader smsHeader) { + if ((smsHeader.portAddrs == null) && + (smsHeader.concatRef == null) && + (smsHeader.miscEltList.size() == 0)) { + return null; } - return m_data; + ByteArrayOutputStream outStream = new ByteArrayOutputStream(SmsMessage.MAX_USER_DATA_BYTES); + ConcatRef concatRef = smsHeader.concatRef; + if (concatRef != null) { + if (concatRef.isEightBits) { + outStream.write(ELT_ID_CONCATENATED_8_BIT_REFERENCE); + outStream.write(3); + outStream.write(concatRef.refNumber); + } else { + outStream.write(ELT_ID_CONCATENATED_16_BIT_REFERENCE); + outStream.write(4); + outStream.write(concatRef.refNumber >>> 8); + outStream.write(concatRef.refNumber & 0x00FF); + } + outStream.write(concatRef.msgCount); + outStream.write(concatRef.seqNumber); + } + PortAddrs portAddrs = smsHeader.portAddrs; + if (portAddrs != null) { + if (portAddrs.areEightBits) { + outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT); + outStream.write(2); + outStream.write(portAddrs.destPort); + outStream.write(portAddrs.origPort); + } else { + outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT); + outStream.write(4); + outStream.write(portAddrs.destPort >>> 8); + outStream.write(portAddrs.destPort & 0x00FF); + outStream.write(portAddrs.origPort >>> 8); + outStream.write(portAddrs.origPort & 0x00FF); + } + } + for (MiscElt miscElt : smsHeader.miscEltList) { + outStream.write(miscElt.id); + outStream.write(miscElt.data.length); + outStream.write(miscElt.data, 0, miscElt.data.length); + } + return outStream.toByteArray(); } - /** - * A single Element in the SMS User Data Header. - * - * See TS 23.040 9.2.3.24. - * - */ - public static class Element { - private byte[] m_data; - private int m_id; - - public Element(int id, byte[] data) { - m_id = id; - m_data = data; + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("UserDataHeader "); + builder.append("{ ConcatRef "); + if (concatRef == null) { + builder.append("unset"); + } else { + builder.append("{ refNumber=" + concatRef.refNumber); + builder.append(", msgCount=" + concatRef.msgCount); + builder.append(", seqNumber=" + concatRef.seqNumber); + builder.append(", isEightBits=" + concatRef.isEightBits); + builder.append(" }"); } - - /** - * Returns the Information Element Identifier for this element. - * - * @return the IE identifier. - */ - public int getID() { - return m_id; + builder.append(", PortAddrs "); + if (portAddrs == null) { + builder.append("unset"); + } else { + builder.append("{ destPort=" + portAddrs.destPort); + builder.append(", origPort=" + portAddrs.origPort); + builder.append(", areEightBits=" + portAddrs.areEightBits); + builder.append(" }"); } - - /** - * Returns the data portion of this element. - * - * @return element data. - */ - public byte[] getData() { - return m_data; + for (MiscElt miscElt : miscEltList) { + builder.append(", MiscElt "); + builder.append("{ id=" + miscElt.id); + builder.append(", length=" + miscElt.data.length); + builder.append(", data=" + HexDump.toHexString(miscElt.data)); + builder.append(" }"); } + builder.append(" }"); + return builder.toString(); } + } diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java index 1aad38d2aa82..3c7dd458f94b 100644 --- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java +++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java @@ -86,6 +86,38 @@ public abstract class SmsMessageBase { /** TP-Message-Reference - Message Reference of sent message. @hide */ public int messageRef; + /** + * For a specific text string, this object describes protocol + * properties of encoding it for transmission as message user + * data. + */ + public static class TextEncodingDetails { + /** + *The number of SMS's required to encode the text. + */ + public int msgCount; + + /** + * The number of code units consumed so far, where code units + * are basically characters in the encoding -- for example, + * septets for the standard ASCII and GSM encodings, and 16 + * bits for Unicode. + */ + public int codeUnitCount; + + /** + * How many code units are still available without spilling + * into an additional message. + */ + public int codeUnitsRemaining; + + /** + * The encoding code unit size (specified using + * android.telephony.SmsMessage ENCODING_*). + */ + public int codeUnitSize; + } + public static abstract class SubmitPduBase { public byte[] encodedScAddress; // Null if not applicable. public byte[] encodedMessage; @@ -245,8 +277,6 @@ public abstract class SmsMessageBase { /** * Returns an object representing the user data header * - * @return an object representing the user data header - * * {@hide} */ public SmsHeader getUserDataHeader() { @@ -254,9 +284,14 @@ public abstract class SmsMessageBase { } /** + * TODO(cleanup): The term PDU is used in a seemingly non-unique + * manner -- for example, what is the difference between this byte + * array and the contents of SubmitPdu objects. Maybe a more + * illustrative term would be appropriate. + */ + + /** * Returns the raw PDU for the message. - * - * @return the raw PDU for the message. */ public byte[] getPdu() { return mPdu; @@ -309,7 +344,9 @@ public abstract class SmsMessageBase { } protected void parseMessageBody() { - if (originatingAddress.couldBeEmailGateway()) { + // originatingAddress could be null if this message is from a status + // report. + if (originatingAddress != null && originatingAddress.couldBeEmailGateway()) { extractEmailAddressFromMessageBody(); } } diff --git a/telephony/java/com/android/internal/telephony/SmsResponse.java b/telephony/java/com/android/internal/telephony/SmsResponse.java index 3c4df563000e..bd79e02da8f5 100644 --- a/telephony/java/com/android/internal/telephony/SmsResponse.java +++ b/telephony/java/com/android/internal/telephony/SmsResponse.java @@ -26,9 +26,15 @@ public class SmsResponse { int messageRef; /** ackPdu for the just-sent SMS. */ String ackPdu; + /** + * errorCode: See 3GPP 27.005, 3.2.5 for GSM/UMTS, + * 3GPP2 N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable. + */ + int errorCode; - public SmsResponse(int messageRef, String ackPdu) { + public SmsResponse(int messageRef, String ackPdu, int errorCode) { this.messageRef = messageRef; this.ackPdu = ackPdu; + this.errorCode = errorCode; } } diff --git a/telephony/java/com/android/internal/telephony/TelephonyEventLog.java b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java index 97f9d7d450cf..cdce488d5d24 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyEventLog.java +++ b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java @@ -30,4 +30,6 @@ public final class TelephonyEventLog { public static final int EVENT_LOG_CGREG_FAIL = 50107; public static final int EVENT_LOG_DATA_STATE_RADIO_OFF = 50108; public static final int EVENT_LOG_PDP_NETWORK_DROP = 50109; + public static final int EVENT_LOG_CDMA_DATA_SETUP_FAILED = 50110; + public static final int EVENT_LOG_CDMA_DATA_DROP = 50111; } diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index c3422339c0db..9152211849d6 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -51,9 +51,24 @@ public class TelephonyIntents { * <p class="note"> * Requires no permission. */ - public static final String ACTION_RADIO_TECHNOLOGY_CHANGED + public static final String ACTION_RADIO_TECHNOLOGY_CHANGED = "android.intent.action.RADIO_TECHNOLOGY"; - + /** + * <p>Broadcast Action: The emergency callback mode is changed. + * <ul> + * <li><em>phoneinECMState</em> - A boolean value,true=phone in ECM, false=ECM off</li> + * </ul> + * <p class="note"> + * You can <em>not</em> receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, + * android.content.IntentFilter) Context.registerReceiver()}. + * + * <p class="note"> + * Requires no permission. + */ + public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED + = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; /** * Broadcast Action: The phone's signal strength has changed. The intent will have the * following extra values:</p> @@ -166,4 +181,34 @@ public class TelephonyIntents { */ public static final String ACTION_NETWORK_SET_TIMEZONE = "android.intent.action.NETWORK_SET_TIMEZONE"; + + /** + * <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms + * <p class="note">. + * This is to pop up a notice to show user that the phone is in emergency callback mode + * and atacalls and outgoing sms are blocked. + */ + public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS + = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS"; + + /** + * Broadcast Action: The MDN changed during the CDMA OTA Process + * The intent will have the following extra values:</p> + * <ul> + * <li><em>mdn</em> - An Integer of the updated MDN number.</li> + * </ul> + * + * <p class="note"> + */ + // TODO(Moto): Generally broadcast intents are for use to allow entities which + // may not know about each other to "communicate". This seems quite specific + // and maybe using the registrant style would be better. + + // Moto: Since this is used for apps not in the same process of phone, can the + // registrant style be used? (Ling Li says: Maybe the "app" can request rather + // than save the MDN each time and this intent would not be necessary?) + // Moto response: Moto internal discussion is on-going. + public static final String ACTION_CDMA_OTA_MDN_CHANGED + = "android.intent.action.ACTION_MDN_STATE_CHANGED"; + } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 396b42d6f95d..290e1fc24b71 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -44,6 +44,7 @@ public interface TelephonyProperties * Availability: when registered to a network */ static final String PROPERTY_OPERATOR_ALPHA = "gsm.operator.alpha"; + //TODO: most of these proprieties are generic, substitute gsm. with phone. bug 1856959 /** Numeric name (MCC+MNC) of current registered operator. * Availability: when registered to a network @@ -68,6 +69,8 @@ public interface TelephonyProperties */ static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country"; + static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type"; + //****** SIM Card /** * One of <code>"UNKNOWN"</code> <code>"ABSENT"</code> <code>"PIN_REQUIRED"</code> @@ -95,4 +98,12 @@ public interface TelephonyProperties */ static String PROPERTY_DATA_NETWORK_TYPE = "gsm.network.type"; + /** Indicate if phone is in emergency callback mode */ + static final String PROPERTY_INECM_MODE = "ril.cdma.inecmmode"; + + /** Indicate the timer value for exiting emergency callback mode */ + static final String PROPERTY_ECM_EXIT_TIMER = "ro.cdma.ecmexittimer"; + + /** The international dialing prefix conversion string */ + static final String PROPERTY_IDP_STRING = "ro.cdma.idpstring"; } diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index 98899c903684..99709406b828 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -16,9 +16,10 @@ package com.android.internal.telephony; +import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.os.PowerManager; +import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; @@ -34,18 +35,17 @@ public class WapPushOverSms { private final Context mContext; private WspTypeDecoder pduDecoder; - private PowerManager.WakeLock mWakeLock; + private SMSDispatcher mSmsDispatcher; /** - * Hold the wake lock for 5 seconds, which should be enough time for + * Hold the wake lock for 5 seconds, which should be enough time for * any receiver(s) to grab its own wake lock. */ private final int WAKE_LOCK_TIMEOUT = 5000; - public WapPushOverSms(Phone phone) { - + public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) { + mSmsDispatcher = smsDispatcher; mContext = phone.getContext(); - createWakelock(); } /** @@ -53,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)); @@ -66,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); @@ -79,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(); @@ -100,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(); @@ -130,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)) { @@ -147,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(); @@ -169,6 +172,7 @@ public class WapPushOverSms { if (dispatchedByApplication == false) { dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex); } + return Activity.RESULT_OK; } private void dispatchWapPdu_default( @@ -184,7 +188,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", data); - sendBroadcast(intent, "android.permission.RECEIVE_WAP_PUSH"); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); } private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType) { @@ -194,7 +198,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", pdu); - sendBroadcast(intent, "android.permission.RECEIVE_WAP_PUSH"); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); } private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType, int dataIndex) { @@ -209,20 +213,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", data); - sendBroadcast(intent, "android.permission.RECEIVE_MMS"); - } - - private void createWakelock() { - PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WapPushOverSms"); - mWakeLock.setReferenceCounted(true); - } - - private void sendBroadcast(Intent intent, String permission) { - // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any - // receivers time to take their own wake locks. - mWakeLock.acquire(WAKE_LOCK_TIMEOUT); - mContext.sendBroadcast(intent, permission); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS"); } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 8ffb7ecc44c7..3362de83e954 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -16,7 +16,10 @@ 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; @@ -24,20 +27,22 @@ 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; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.text.TextUtils; import android.util.Log; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; - import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.DataConnection; 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; @@ -48,10 +53,12 @@ import com.android.internal.telephony.PhoneNotifier; import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; -import java.util.ArrayList; import java.util.List; - +import java.util.Timer; +import java.util.TimerTask; /** * {@hide} */ @@ -59,6 +66,12 @@ public class CDMAPhone extends PhoneBase { static final String LOG_TAG = "CDMA"; private static final boolean LOCAL_DEBUG = true; + // 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; CdmaSMSDispatcher mSMS; @@ -71,11 +84,29 @@ public class CDMAPhone extends PhoneBase { RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager; RuimSmsInterfaceManager mRuimSmsInterfaceManager; PhoneSubInfo mSubInfo; + EriManager mEriManager; - protected RegistrantList mNvLoadedRegistrants = new RegistrantList(); + // mNvLoadedRegistrants are informed after the EVENT_NV_READY + private RegistrantList mNvLoadedRegistrants = new RegistrantList(); + + // mEriFileLoadedRegistrants are informed after the ERI text has been loaded + private RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); + + // mECMExitRespRegistrant is informed after the phone has been exited + //the emergency callback mode + //keep track of if phone is in emergency callback mode + private boolean mIsPhoneInECMState; + private Registrant mECMExitRespRegistrant; private String mEsn; private String mMeid; + // A runnable which is used to automatically exit from ECM after a period of time. + private Runnable mExitEcmRunnable = new Runnable() { + public void run() { + exitEmergencyCallbackMode(); + } + }; + Registrant mPostDialHandler; @@ -102,6 +133,7 @@ public class CDMAPhone extends PhoneBase { mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this); mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this); mSubInfo = new PhoneSubInfo(this); + mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML); mCM.registerForAvailable(h, EVENT_RADIO_AVAILABLE, null); mRuimRecords.registerForRecordsLoaded(h, EVENT_RUIM_RECORDS_LOADED, null); @@ -111,10 +143,19 @@ public class CDMAPhone extends PhoneBase { mCM.setOnCallRing(h, EVENT_CALL_RING, null); mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null); mCM.registerForNVReady(h, EVENT_NV_READY, null); + mCM.setEmergencyCallbackMode(h, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null); + //Change the system setting - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.CDMA_PHONE); + SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, + new Integer(RILConstants.CDMA_PHONE).toString()); + + // 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() { @@ -130,6 +171,7 @@ public class CDMAPhone extends PhoneBase { mCM.unSetOnSuppServiceNotification(h); mCM.unSetOnCallRing(h); + //Force all referenced classes to unregister their former registered events mCT.dispose(); mDataConnection.dispose(); @@ -141,6 +183,7 @@ public class CDMAPhone extends PhoneBase { mRuimPhoneBookInterfaceManager.dispose(); mRuimSmsInterfaceManager.dispose(); mSubInfo.dispose(); + mEriManager.dispose(); } } @@ -155,6 +198,7 @@ public class CDMAPhone extends PhoneBase { this.mDataConnection = null; this.mCT = null; this.mSST = null; + this.mEriManager = null; } protected void finalize() { @@ -215,7 +259,7 @@ public class CDMAPhone extends PhoneBase { public DataActivityState getDataActivityState() { DataActivityState ret = DataActivityState.NONE; - if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.RADIO_TECHNOLOGY_UNKNOWN) { + if (mSST.getCurrentCdmaDataConnectionState() == ServiceState.STATE_IN_SERVICE) { switch (mDataConnection.getActivity()) { case DATAIN: @@ -229,6 +273,10 @@ public class CDMAPhone extends PhoneBase { case DATAINANDOUT: ret = DataActivityState.DATAINANDOUT; break; + + case DORMANT: + ret = DataActivityState.DORMANT; + break; } } return ret; @@ -249,26 +297,19 @@ public class CDMAPhone extends PhoneBase { if (fc != null) { //mMmiRegistrants.notifyRegistrants(new AsyncResult(null, fc, null)); fc.processCode(); - } else { - FeatureCode digits = new FeatureCode(this); - // use dial number as poundString - digits.poundString = newDialString; - digits.processCode(); + return null; } - return null; - } else { - return mCT.dial(newDialString); } + return mCT.dial(newDialString); } - - public int getSignalStrengthASU() { - return mSST.rssi == 99 ? -1 : mSST.rssi; + public SignalStrength getSignalStrength() { + return mSST.mSignalStrength; } public boolean getMessageWaitingIndicator() { - return mRuimRecords.getVoiceMessageWaiting(); + return (getVoiceMessageCount() > 0); } public List<? extends MmiCode> @@ -347,7 +388,15 @@ public class CDMAPhone extends PhoneBase { } public String getLine1Number() { - return mRuimRecords.getMdnNumber(); + return mSST.getMdnNumber(); + } + + public String getCdmaPrlVersion(){ + return mRuimRecords.getPrlVersion(); + } + + public String getCdmaMIN() { + return mSST.getCdmaMin(); } public void getCallWaiting(Message onComplete) { @@ -378,8 +427,13 @@ public class CDMAPhone extends PhoneBase { } public String getSubscriberId() { - Log.e(LOG_TAG, "method getSubscriberId for IMSI is NOT supported in CDMA!"); - return null; + // Subscriber ID is the combination of MCC+MNC+MIN as CDMA IMSI + // TODO(Moto): Replace with call to mRuimRecords.getIMSI_M() when implemented. + if ((getServiceState().getOperatorNumeric() != null) && (getCdmaMIN() != null)) { + return (getServiceState().getOperatorNumeric() + getCdmaMIN()); + } else { + return null; + } } public boolean canConference() { @@ -410,7 +464,7 @@ public class CDMAPhone extends PhoneBase { } public void setOnPostDialCharacter(Handler h, int what, Object obj) { - Log.e(LOG_TAG, "setOnPostDialCharacter: not possible in CDMA"); + mPostDialHandler = new Registrant(h, what, obj); } public boolean handlePinMmi(String dialString) { @@ -454,14 +508,50 @@ public class CDMAPhone extends PhoneBase { mDataConnection.setDataOnRoamingEnabled(enable); } + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + mCM.registerForCdmaOtaProvision(h, what, obj); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + mCM.unregisterForCdmaOtaProvision(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { + mECMExitRespRegistrant = new Registrant (h, what, obj); + } + + public void unsetOnEcbModeExitResponse(Handler h) { + mECMExitRespRegistrant.clear(); + } + + public void registerForCallWaiting(Handler h, int what, Object obj) { + mCT.registerForCallWaiting(h, what, obj); + } + + public void unregisterForCallWaiting(Handler h) { + mCT.unregisterForCallWaiting(h); + } + public String getIpAddress(String apnType) { return mDataConnection.getIpAddress(); } public void getNeighboringCids(Message response) { - // WINK:TODO: implement after Cupcake merge - mCM.getNeighboringCids(response); // workaround. + /* + * This is currently not implemented. At least as of June + * 2009, there is no neighbor cell information available for + * CDMA because some party is resisting making this + * information readily available. Consequently, calling this + * function can have no useful effect. This situation may + * (and hopefully will) change in the future. + */ + if (response != null) { + CommandException ce = new CommandException( + CommandException.Error.REQUEST_NOT_SUPPORTED); + AsyncResult.forMessage(response).exception = ce; + response.sendToTarget(); + } } public DataState getDataConnectionState() { @@ -476,12 +566,11 @@ public class CDMAPhone extends PhoneBase { ret = DataState.CONNECTED; } else if (mSST == null) { - // Radio Technology Change is ongoning, dispose() and removeReferences() have - // already been called + // Radio Technology Change is ongoning, dispose() and removeReferences() have + // already been called - ret = DataState.DISCONNECTED; - } else if (mSST.getCurrentCdmaDataConnectionState() - == ServiceState.RADIO_TECHNOLOGY_UNKNOWN) { + ret = DataState.DISCONNECTED; + } else if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.STATE_IN_SERVICE) { // If we're out of service, open TCP sockets may still work // but no data will flow ret = DataState.DISCONNECTED; @@ -541,6 +630,21 @@ public class CDMAPhone extends PhoneBase { mCM.stopDtmf(null); } + public void sendBurstDtmf(String dtmfString, Message onComplete) { + boolean check = true; + for (int itr = 0;itr < dtmfString.length(); itr++) { + if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) { + Log.e(LOG_TAG, + "sendDtmf called with invalid character '" + dtmfString.charAt(itr)+ "'"); + check = false; + break; + } + } + if ((mCT.state == Phone.State.OFFHOOK)&&(check)) { + mCM.sendBurstDtmf(dtmfString, onComplete); + } + } + public void getAvailableNetworks(Message response) { Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); } @@ -554,7 +658,7 @@ public class CDMAPhone extends PhoneBase { } public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { - Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); + Log.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA"); } public void enableLocationUpdates() { @@ -583,15 +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 "12345"; + 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 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() { @@ -609,7 +731,15 @@ public class CDMAPhone extends PhoneBase { } public boolean enableDataConnectivity() { - return mDataConnection.setDataEnabled(true); + + // block data activities when phone is in emergency callback mode + if (mIsPhoneInECMState) { + Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS); + ActivityManagerNative.broadcastStickyIntent(intent, null); + return false; + } else { + return mDataConnection.setDataEnabled(true); + } } public void disableLocationUpdates() { @@ -652,7 +782,7 @@ public class CDMAPhone extends PhoneBase { return null; } - /** + /** * Notify any interested party of a Phone state change. */ /*package*/ void notifyPhoneStateChanged() { @@ -697,15 +827,23 @@ public class CDMAPhone extends PhoneBase { mUnknownConnectionRegistrants.notifyResult(this); } + void sendEmergencyCallbackModeChange(){ + //Send an Intent + Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); + intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInECMState); + ActivityManagerNative.broadcastStickyIntent(intent,null); + } + /*package*/ void updateMessageWaitingIndicator(boolean mwi) { // this also calls notifyMessageWaitingIndicator() 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); } /** @@ -722,6 +860,51 @@ public class CDMAPhone extends PhoneBase { mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, fc, null)); } + + @Override + public void exitEmergencyCallbackMode() { + // Send a message which will invoke handleExitEmergencyCallbackMode + mCM.exitEmergencyCallbackMode(h.obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); + } + + private void handleEnterEmergencyCallbackMode(Message msg) { + Log.d(LOG_TAG, "Event EVENT_EMERGENCY_CALLBACK_MODE Received"); + // if phone is not in ECM mode, and it's changed to ECM mode + if (mIsPhoneInECMState == false) { + mIsPhoneInECMState = true; + // notify change + sendEmergencyCallbackModeChange(); + setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); + + // Post this runnable so we will automatically exit + // if no one invokes exitEmergencyCallbackMode() directly. + long delayInMillis = SystemProperties.getLong( + TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); + h.postDelayed(mExitEcmRunnable, delayInMillis); + } + } + + private void handleExitEmergencyCallbackMode(Message msg) { + Log.d(LOG_TAG, "Event EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE Received"); + AsyncResult ar = (AsyncResult)msg.obj; + + // Remove pending exit ECM runnable, if any + h.removeCallbacks(mExitEcmRunnable); + + if (mECMExitRespRegistrant != null) { + mECMExitRespRegistrant.notifyRegistrant(ar); + } + // if exiting ecm success + if (ar.exception == null) { + if (mIsPhoneInECMState) { + mIsPhoneInECMState = false; + setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); + } + // send an Intent + sendEmergencyCallbackModeChange(); + } + } + //***** Inner Classes class MyHandler extends Handler { MyHandler() { @@ -731,6 +914,7 @@ public class CDMAPhone extends PhoneBase { super(l); } + @Override public void handleMessage(Message msg) { AsyncResult ar; Message onComplete; @@ -751,7 +935,7 @@ public class CDMAPhone extends PhoneBase { } if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result); - setSystemProperty(PROPERTY_BASEBAND_VERSION, (String)ar.result); + setSystemProperty(TelephonyProperties.PROPERTY_BASEBAND_VERSION, (String)ar.result); } break; @@ -767,6 +951,16 @@ public class CDMAPhone extends PhoneBase { } break; + case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{ + handleEnterEmergencyCallbackMode(msg); + } + break; + + case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{ + handleExitEmergencyCallbackMode(msg); + } + break; + case EVENT_RUIM_RECORDS_LOADED:{ Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received"); } @@ -800,10 +994,30 @@ public class CDMAPhone extends PhoneBase { case EVENT_NV_READY:{ Log.d(LOG_TAG, "Event EVENT_NV_READY Received"); //Inform the Service State Tracker + mEriManager.loadEriFile(); mNvLoadedRegistrants.notifyRegistrants(); + if(mEriManager.isEriFileLoaded()) { + // when the ERI file is loaded + Log.d(LOG_TAG, "ERI read, notify registrants"); + mEriFileLoadedRegistrants.notifyRegistrants(); + } + setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE,"false"); } 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"); } @@ -811,26 +1025,26 @@ public class CDMAPhone extends PhoneBase { } } - /** - * Retrieves the PhoneSubInfo of the CDMAPhone - */ - public PhoneSubInfo getPhoneSubInfo(){ + /** + * Retrieves the PhoneSubInfo of the CDMAPhone + */ + public PhoneSubInfo getPhoneSubInfo() { return mSubInfo; - } + } - /** - * Retrieves the IccSmsInterfaceManager of the CDMAPhone - */ - public IccSmsInterfaceManager getIccSmsInterfaceManager(){ - return mRuimSmsInterfaceManager; - } + /** + * Retrieves the IccSmsInterfaceManager of the CDMAPhone + */ + public IccSmsInterfaceManager getIccSmsInterfaceManager() { + return mRuimSmsInterfaceManager; + } - /** - * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone - */ - public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ - return mRuimPhoneBookInterfaceManager; - } + /** + * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone + */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager() { + return mRuimPhoneBookInterfaceManager; + } public void registerForNvLoaded(Handler h, int what, Object obj) { Registrant r = new Registrant (h, what, obj); @@ -841,71 +1055,193 @@ public class CDMAPhone extends PhoneBase { mNvLoadedRegistrants.remove(h); } - // override for allowing access from other classes of this package - /** - * {@inheritDoc} - */ - public final void setSystemProperty(String property, String value) { - super.setSystemProperty(property, value); - } + public void registerForEriFileLoaded(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mEriFileLoadedRegistrants.add(r); + } - /** - * {@inheritDoc} - */ - public Handler getHandler(){ - return h; - } + public void unregisterForEriFileLoaded(Handler h) { + mEriFileLoadedRegistrants.remove(h); + } - /** - * {@inheritDoc} - */ - public IccFileHandler getIccFileHandler(){ - return this.mIccFileHandler; - } + // override for allowing access from other classes of this package + /** + * {@inheritDoc} + */ + public final void setSystemProperty(String property, String value) { + super.setSystemProperty(property, value); + } - /** - * Set the TTY mode of the CDMAPhone - */ - public void setTTYModeEnabled(boolean enable, Message onComplete) { - this.mCM.setTTYModeEnabled(enable, onComplete); -} + /** + * {@inheritDoc} + */ + public Handler getHandler() { + return h; + } - /** - * Queries the TTY mode of the CDMAPhone - */ - public void queryTTYModeEnabled(Message onComplete) { - this.mCM.queryTTYModeEnabled(onComplete); - } + /** + * {@inheritDoc} + */ + public IccFileHandler getIccFileHandler() { + return this.mIccFileHandler; + } - /** - * Activate or deactivate cell broadcast SMS. - * - * @param activate - * 0 = activate, 1 = deactivate - * @param response - * Callback message is empty on completion - */ - public void activateCellBroadcastSms(int activate, Message response) { - mSMS.activateCellBroadcastSms(activate, response); - } + /** + * Set the TTY mode of the CDMAPhone + */ + public void setTTYMode(int ttyMode, Message onComplete) { + this.mCM.setTTYMode(ttyMode, onComplete); + } - /** - * Query the current configuration of cdma cell broadcast SMS. - * - * @param response - * Callback message is empty on completion - */ - public void getCellBroadcastSmsConfig(Message response){ - mSMS.getCellBroadcastSmsConfig(response); - } + /** + * Queries the TTY mode of the CDMAPhone + */ + public void queryTTYMode(Message onComplete) { + this.mCM.queryTTYMode(onComplete); + } + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate 0 = activate, 1 = deactivate + * @param response Callback message is empty on completion + */ + public void activateCellBroadcastSms(int activate, Message response) { + mSMS.activateCellBroadcastSms(activate, response); + } + + /** + * Query the current configuration of cdma cell broadcast SMS. + * + * @param response Callback message is empty on completion + */ + public void getCellBroadcastSmsConfig(Message response) { + mSMS.getCellBroadcastSmsConfig(response); + } + + /** + * Configure cdma cell broadcast SMS. + * + * @param response Callback message is empty on completion + */ + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { + mSMS.setCellBroadcastConfig(configValuesArray, response); + } + + public static final String IS683A_FEATURE_CODE = "*228" ; + public static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4 ; + public static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2 ; + public static final int IS683A_SYS_SEL_CODE_OFFSET = 4; + + private static final int IS683_CONST_800MHZ_A_BAND = 0; + private static final int IS683_CONST_800MHZ_B_BAND = 1; + private static final int IS683_CONST_1900MHZ_A_BLOCK = 2; + private static final int IS683_CONST_1900MHZ_B_BLOCK = 3; + private static final int IS683_CONST_1900MHZ_C_BLOCK = 4; + private static final int IS683_CONST_1900MHZ_D_BLOCK = 5; + private static final int IS683_CONST_1900MHZ_E_BLOCK = 6; + private static final int IS683_CONST_1900MHZ_F_BLOCK = 7; + + private boolean isIs683OtaSpDialStr(String dialStr) { + int sysSelCodeInt; + boolean isOtaspDialString = false; + int dialStrLen = dialStr.length(); + + if (dialStrLen == IS683A_FEATURE_CODE_NUM_DIGITS) { + if (dialStr.equals(IS683A_FEATURE_CODE)) { + isOtaspDialString = true; + } + } else if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, 0, + IS683A_FEATURE_CODE_NUM_DIGITS) == true) + && (dialStrLen >= + (IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS))) { + StringBuilder sb = new StringBuilder(dialStr); + // Separate the System Selection Code into its own string + char[] sysSel = new char[2]; + sb.delete(0, IS683A_SYS_SEL_CODE_OFFSET); + sb.getChars(0, IS683A_SYS_SEL_CODE_NUM_DIGITS, sysSel, 0); + + if ((PhoneNumberUtils.isISODigit(sysSel[0])) + && (PhoneNumberUtils.isISODigit(sysSel[1]))) { + String sysSelCode = new String(sysSel); + sysSelCodeInt = Integer.parseInt((String)sysSelCode); + switch (sysSelCodeInt) { + case IS683_CONST_800MHZ_A_BAND: + case IS683_CONST_800MHZ_B_BAND: + case IS683_CONST_1900MHZ_A_BLOCK: + case IS683_CONST_1900MHZ_B_BLOCK: + case IS683_CONST_1900MHZ_C_BLOCK: + case IS683_CONST_1900MHZ_D_BLOCK: + case IS683_CONST_1900MHZ_E_BLOCK: + case IS683_CONST_1900MHZ_F_BLOCK: + isOtaspDialString = true; + break; + + default: + break; + } + } + } + return isOtaspDialString; + } /** - * Configure cdma cell broadcast SMS. + * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier + * OTASP dial string. * - * @param response - * Callback message is empty on completion + * @param dialStr the number to look up. + * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string */ - public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){ - mSMS.setCellBroadcastConfig(configValuesArray, response); + @Override + public boolean isOtaSpNumber(String dialStr){ + boolean isOtaSpNum = false; + if(dialStr != null){ + isOtaSpNum=isIs683OtaSpDialStr(dialStr); + if(isOtaSpNum == false){ + //TO DO:Add carrier specific OTASP number detection here. + } + } + return isOtaSpNum; } + + @Override + public int getCdmaEriIconIndex() { + int roamInd = getServiceState().getCdmaRoamingIndicator(); + int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); + return mEriManager.getCdmaEriIconIndex(roamInd, defRoamInd); + } + + /** + * Returns the CDMA ERI icon mode, + * 0 - ON + * 1 - FLASHING + */ + @Override + public int getCdmaEriIconMode() { + int roamInd = getServiceState().getCdmaRoamingIndicator(); + int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); + return mEriManager.getCdmaEriIconMode(roamInd, defRoamInd); + } + + /** + * Returns the CDMA ERI text, + */ + @Override + public String getCdmaEriText() { + int roamInd = getServiceState().getCdmaRoamingIndicator(); + 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/CallFailCause.java b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java index ea557b2068e1..fb5f0fa82627 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java +++ b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java @@ -25,26 +25,31 @@ package com.android.internal.telephony.cdma; * */ public interface CallFailCause { - static final int NORMAL_CLEARING = 16; + static final int NORMAL_CLEARING = 16; // Busy Tone - static final int USER_BUSY = 17; - -// // No Tone -// static final int NUMBER_CHANGED = 22; -// static final int STATUS_ENQUIRY = 30; - static final int NORMAL_UNSPECIFIED = 31; -// -// // Congestion Tone -// static final int NO_CIRCUIT_AVAIL = 34; -// static final int TEMPORARY_FAILURE = 41; -// static final int SWITCHING_CONGESTION = 42; -// static final int CHANNEL_NOT_AVAIL = 44; -// static final int QOS_NOT_AVAIL = 49; -// static final int BEARER_NOT_AVAIL = 58; -// -// // others -// static final int ACM_LIMIT_EXCEEDED = 68; -// static final int CALL_BARRED = 240; -// static final int FDN_BLOCKED = 241; + static final int USER_BUSY = 17; + + static final int NORMAL_UNSPECIFIED = 31; + + // Congestion Tone + static final int NO_CIRCUIT_AVAIL = 34; + + // others + static final int ACM_LIMIT_EXCEEDED = 68; + static final int CALL_BARRED = 240; + static final int FDN_BLOCKED = 241; + + static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; + static final int CDMA_DROP = 1001; + static final int CDMA_INTERCEPT = 1002; + static final int CDMA_REORDER = 1003; + static final int CDMA_SO_REJECT = 1004; + static final int CDMA_RETRY_ORDER = 1005; + static final int CDMA_ACCESS_FAILURE = 1006; + static final int CDMA_PREEMPTED = 1007; + + // For non-emergency number dialed while in emergency callback mode. + static final int CDMA_NOT_EMERGENCY = 1008; + static final int ERROR_UNSPECIFIED = 0xffff; } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java index 34514d928284..e8724c2cd640 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java @@ -181,9 +181,7 @@ public final class CdmaCall extends Call { */ void onHangupLocal() { - for (int i = 0, s = connections.size() - ; i < s; i++ - ) { + for (int i = 0, s = connections.size(); i < s; i++) { CdmaConnection cn = (CdmaConnection)connections.get(i); cn.onHangupLocal(); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index a1d362fb600f..ed2ea90d3f23 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -24,6 +24,7 @@ import android.os.RegistrantList; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.util.Log; +import android.os.SystemProperties; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CallTracker; @@ -31,11 +32,12 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.DriverCall; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; import java.util.List; + /** * {@hide} */ @@ -56,6 +58,7 @@ public final class CdmaCallTracker extends CallTracker { CdmaConnection connections[] = new CdmaConnection[MAX_CONNECTIONS]; RegistrantList voiceCallEndedRegistrants = new RegistrantList(); RegistrantList voiceCallStartedRegistrants = new RegistrantList(); + RegistrantList callWaitingRegistrants = new RegistrantList(); // connections dropped durin last poll @@ -69,11 +72,12 @@ public final class CdmaCallTracker extends CallTracker { CdmaConnection pendingMO; boolean hangupPendingMO; - + boolean pendingCallInECM=false; CDMAPhone phone; boolean desiredMute = false; // false = mute off + int pendingCallClirMode; Phone.State state = Phone.State.IDLE; @@ -90,13 +94,15 @@ public final class CdmaCallTracker extends CallTracker { cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); + cm.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null); + foregroundCall.setGeneric(false); } public void dispose() { cm.unregisterForCallStateChanged(this); cm.unregisterForOn(this); cm.unregisterForNotAvailable(this); - + cm.unregisterForCallWaitingInfo(this); for(CdmaConnection c : connections) { try { if(c != null) hangup(c); @@ -115,6 +121,7 @@ public final class CdmaCallTracker extends CallTracker { } + @Override protected void finalize() { Log.d(LOG_TAG, "CdmaCallTracker finalized"); } @@ -139,6 +146,15 @@ public final class CdmaCallTracker extends CallTracker { voiceCallEndedRegistrants.remove(h); } + public void registerForCallWaiting(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + callWaitingRegistrants.add(r); + } + + public void unregisterForCallWaiting(Handler h) { + callWaitingRegistrants.remove(h); + } + private void fakeHoldForegroundBeforeDial() { List<Connection> connCopy; @@ -166,34 +182,24 @@ public final class CdmaCallTracker extends CallTracker { throw new CallStateException("cannot dial in current state"); } + + // We are initiating a call therefore even if we previously + // didn't know the state (i.e. Generic was true) we now know + // and therefore can set Generic to false. + foregroundCall.setGeneric(false); + // The new call must be assigned to the foreground call. // That call must be idle, so place anything that's // there on hold if (foregroundCall.getState() == CdmaCall.State.ACTIVE) { - // this will probably be done by the radio anyway - // but the dial might fail before this happens - // and we need to make sure the foreground call is clear - // for the newly dialed connection - switchWaitingOrHoldingAndActive(); - - // Fake local state so that - // a) foregroundCall is empty for the newly dialed connection - // b) hasNonHangupStateChanged remains false in the - // next poll, so that we don't clear a failed dialing call - fakeHoldForegroundBeforeDial(); - } - - if (foregroundCall.getState() != CdmaCall.State.IDLE) { - //we should have failed in !canDial() above before we get here - throw new CallStateException("cannot dial in current state"); + return dialThreeWay(dialString); } pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall); hangupPendingMO = false; if (pendingMO.address == null || pendingMO.address.length() == 0 - || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0 - ) { + || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0) { // Phone number is invalid pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; @@ -204,7 +210,15 @@ public final class CdmaCallTracker extends CallTracker { // Always unmute when initiating a new call setMute(false); - cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); + if(inEcm.equals("false")) { + cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + } else { + phone.exitEmergencyCallbackMode(); + phone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); + pendingCallClirMode=clirMode; + pendingCallInECM=true; + } } updatePhoneState(); @@ -219,19 +233,35 @@ public final class CdmaCallTracker extends CallTracker { return dial(dialString, CommandsInterface.CLIR_DEFAULT); } - void - acceptCall () throws CallStateException { - // FIXME if SWITCH fails, should retry with ANSWER - // in case the active/holding call disappeared and this - // is no longer call waiting + private Connection + dialThreeWay (String dialString) { + if (!foregroundCall.isIdle()) { + // Attach the new connection to foregroundCall + pendingMO = new CdmaConnection(phone.getContext(), + dialString, this, foregroundCall); + cm.sendCDMAFeatureCode(pendingMO.address, + obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); + return pendingMO; + } + return null; + } + void + acceptCall() throws CallStateException { if (ringingCall.getState() == CdmaCall.State.INCOMING) { Log.i("phone", "acceptCall: incoming..."); // Always unmute when answering a new call setMute(false); cm.acceptCall(obtainCompleteMessage()); } else if (ringingCall.getState() == CdmaCall.State.WAITING) { - setMute(false); + CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection()); + + // Since there is no network response for supplimentary + // service for CDMA, we assume call waiting is answered. + // ringing Call state change to idle is in CdmaCall.detach + // triggered by updateParent. + cwConn.updateParent(ringingCall, foregroundCall); + cwConn.onConnectedInOrOut(); switchWaitingOrHoldingAndActive(); } else { throw new CallStateException("phone not ringing"); @@ -255,14 +285,14 @@ public final class CdmaCallTracker extends CallTracker { if (ringingCall.getState() == CdmaCall.State.INCOMING) { throw new CallStateException("cannot be in the incoming state"); } else { - cm.sendCDMAFeatureCode("", obtainCompleteMessage(EVENT_SWITCH_RESULT)); + flashAndSetGenericTrue(); } } void conference() throws CallStateException { - // three way calls in CDMA will be handled by feature codes - Log.e(LOG_TAG, "conference: not possible in CDMA"); + // Should we be checking state? + flashAndSetGenericTrue(); } void @@ -295,6 +325,7 @@ public final class CdmaCallTracker extends CallTracker { pendingMO == null && !ringingCall.isRinging() && (!foregroundCall.getState().isAlive() + || (foregroundCall.getState() == CdmaCall.State.ACTIVE) || !backgroundCall.getState().isAlive()); return ret; @@ -483,9 +514,20 @@ public final class CdmaCallTracker extends CallTracker { } hasNonHangupStateChanged = true; } else if (conn != null && dc == null) { - // Connection missing in CLCC response that we were - // tracking. - droppedDuringPoll.add(conn); + int count = foregroundCall.connections.size(); + if (count == 0) { + // Handle an unanswered MO/MT call, there is no + // foregroundCall connections at this time. + droppedDuringPoll.add(conn); + } else { + // Loop through foreground call connections as + // it contains the known logical connections. + for (int n = 0; n < count; n++) { + CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n); + droppedDuringPoll.add(cn); + } + } + foregroundCall.setGeneric(false); // Dropped connections are removed from the CallTracker // list but kept in the Call list connections[i] = null; @@ -536,6 +578,9 @@ public final class CdmaCallTracker extends CallTracker { droppedDuringPoll.add(pendingMO); pendingMO = null; hangupPendingMO = false; + if( pendingCallInECM) { + pendingCallInECM = false; + } } if (newRinging != null) { @@ -619,6 +664,22 @@ public final class CdmaCallTracker extends CallTracker { if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); hangupPendingMO = true; + } else if ((conn.getCall() == ringingCall) + && (ringingCall.getState() == CdmaCall.State.WAITING)) { + // Handle call waiting hang up case. + // + // The ringingCall state will change to IDLE in CdmaCall.detach + // if the ringing call connection size is 0. We don't specifically + // 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. + // + // 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 { cm.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage()); @@ -753,6 +814,16 @@ public final class CdmaCallTracker extends CallTracker { return null; } + private void flashAndSetGenericTrue() throws CallStateException { + cm.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); + + // Set generic to true because in CDMA it is not known what + // the status of the call is after a call waiting is answered, + // 3 way call merged or a switch between calls. + foregroundCall.setGeneric(true); + phone.notifyCallStateChanged(); + } + private Phone.SuppService getFailedService(int what) { switch (what) { case EVENT_SWITCH_RESULT: @@ -774,6 +845,30 @@ public final class CdmaCallTracker extends CallTracker { pollCallsWhenSafe(); } + private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { + if (callWaitingRegistrants != null) { + callWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); + } + } + + private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) { + // Check how many connections in foregroundCall. + // If the connection in foregroundCall is more + // than one, then the connection information is + // not reliable anymore since it means either + // call waiting is connected or 3 way call is + // dialed before, so set generic. + if (foregroundCall.connections.size() > 1 ) { + foregroundCall.setGeneric(true); + } + + // Create a new CdmaConnection which attaches itself to ringingCall. + ringingCall.setGeneric(false); + new CdmaConnection(phone.getContext(), cw, this, ringingCall); + + // Finally notify application + notifyCallWaitingInfo(cw); + } //****** Overridden from Handler public void @@ -796,13 +891,13 @@ public final class CdmaCallTracker extends CallTracker { break; case EVENT_OPERATION_COMPLETE: - ar = (AsyncResult)msg.obj; operationComplete(); break; case EVENT_SWITCH_RESULT: - ar = (AsyncResult)msg.obj; - operationComplete(); + // In GSM call operationComplete() here which gets the + // current call list. But in CDMA there is no list so + // there is nothing to do. break; case EVENT_GET_LAST_CALL_FAIL_CAUSE: @@ -835,6 +930,7 @@ public final class CdmaCallTracker extends CallTracker { droppedDuringPoll.clear(); break; + case EVENT_REPOLL_AFTER_DELAY: case EVENT_CALL_STATE_CHANGE: pollCallsWhenSafe(); break; @@ -847,8 +943,33 @@ public final class CdmaCallTracker extends CallTracker { handleRadioNotAvailable(); break; + case EVENT_EXIT_ECM_RESPONSE_CDMA: + //no matter the result, we still do the same here + if (pendingCallInECM) { + cm.dial(pendingMO.address, pendingCallClirMode, obtainCompleteMessage()); + pendingCallInECM = false; + } + phone.unsetOnEcbModeExitResponse(this); + break; + + case EVENT_CALL_WAITING_INFO_CDMA: + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); + Log.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); + } + break; + + case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + // Assume 3 way call is connected + pendingMO.onConnectedInOrOut(); + } + break; + default:{ - throw new RuntimeException("unexpected event not handled"); + throw new RuntimeException("unexpected event not handled"); } } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java new file mode 100644 index 000000000000..54dec48e8435 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java @@ -0,0 +1,48 @@ +/* + * 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.telephony.cdma; + +/** + * Represents a Supplementary Service Notification received from the network. + * + * {@hide} + */ +public class CdmaCallWaitingNotification { + public String number =null; + public int numberPresentation = 0; + public String name = null; + public int namePresentation = 0; + public int isPresent = 0; + public int signalType = 0; + public int alertPitch = 0; + public int signal = 0; + + + public String toString() + { + return super.toString() + "Call Waiting Notification " + + " number: " + number + + " numberPresentation: " + numberPresentation + + " name: " + name + + " namePresentation: " + namePresentation + + " isPresent: " + isPresent + + " signalType: " + signalType + + " alertPitch: " + alertPitch + + " signal: " + signal ; + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index cdad4a75d6ea..025382da3e32 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -25,11 +25,14 @@ import android.os.Message; import android.os.PowerManager; import android.os.Registrant; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Config; import android.util.Log; +import android.text.TextUtils; + import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; - +import com.android.internal.telephony.TelephonyProperties; /** * {@hide} @@ -48,7 +51,7 @@ public class CdmaConnection extends Connection { String postDialString; // outgoing calls only boolean isIncoming; boolean disconnected; - + String cnapName; int index; // index in CdmaCallTracker.connections[], -1 if unassigned /* @@ -74,6 +77,8 @@ public class CdmaConnection extends Connection { DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; PostDialState postDialState = PostDialState.NOT_STARTED; int numberPresentation = Connection.PRESENTATION_ALLOWED; + int cnapNamePresentation = Connection.PRESENTATION_ALLOWED; + Handler h; @@ -84,11 +89,10 @@ public class CdmaConnection extends Connection { static final int EVENT_PAUSE_DONE = 2; static final int EVENT_NEXT_POST_DIAL = 3; static final int EVENT_WAKE_LOCK_TIMEOUT = 4; - + //***** Constants - static final int PAUSE_DELAY_FIRST_MILLIS = 100; - static final int PAUSE_DELAY_MILLIS = 3 * 1000; static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; + static final int PAUSE_DELAY_MILLIS = 2 * 1000; //***** Inner Classes @@ -126,6 +130,8 @@ public class CdmaConnection extends Connection { isIncoming = dc.isMT; createTime = System.currentTimeMillis(); + cnapName = dc.name; + cnapNamePresentation = dc.namePresentation; numberPresentation = dc.numberPresentation; this.index = index; @@ -134,16 +140,19 @@ public class CdmaConnection extends Connection { parent.attach(this, dc); } - /** This is an MO call, created when dialing */ + /** This is an MO call/three way call, created when dialing */ /*package*/ - CdmaConnection (Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) { + CdmaConnection(Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) { createWakeLock(context); acquireWakeLock(); - + owner = ct; h = new MyHandler(owner.getLooper()); this.dialString = dialString; + Log.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString); + dialString = formatDialString(dialString); + Log.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString); this.address = PhoneNumberUtils.extractNetworkPortion(dialString); this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); @@ -151,10 +160,41 @@ public class CdmaConnection extends Connection { index = -1; isIncoming = false; + cnapName = null; + cnapNamePresentation = 0; + numberPresentation = 0; createTime = System.currentTimeMillis(); + if (parent != null) { + this.parent = parent; + + //for the three way call case, not change parent state + if (parent.state == CdmaCall.State.ACTIVE) { + parent.attachFake(this, CdmaCall.State.ACTIVE); + } else { + parent.attachFake(this, CdmaCall.State.DIALING); + } + } + } + + /** This is a Call waiting call*/ + CdmaConnection(Context context, CdmaCallWaitingNotification cw, CdmaCallTracker ct, + CdmaCall parent) { + createWakeLock(context); + acquireWakeLock(); + + owner = ct; + h = new MyHandler(owner.getLooper()); + address = cw.number; + numberPresentation = cw.numberPresentation; + cnapName = cw.name; + cnapNamePresentation = cw.namePresentation; + index = -1; + isIncoming = true; + createTime = System.currentTimeMillis(); + connectTime = 0; this.parent = parent; - parent.attachFake(this, CdmaCall.State.DIALING); + parent.attachFake(this, CdmaCall.State.WAITING); } public void dispose() { @@ -186,10 +226,22 @@ public class CdmaConnection extends Connection { return (isIncoming ? "incoming" : "outgoing"); } + public String getOrigDialString(){ + return dialString; + } + public String getAddress() { return address; } + public String getCnapName() { + return cnapName; + } + + public int getCnapNamePresentation() { + return cnapNamePresentation; + } + public CdmaCall getCall() { return parent; } @@ -344,6 +396,32 @@ public class CdmaConnection extends Connection { switch (causeCode) { case CallFailCause.USER_BUSY: return DisconnectCause.BUSY; + case CallFailCause.NO_CIRCUIT_AVAIL: + return DisconnectCause.CONGESTION; + case CallFailCause.ACM_LIMIT_EXCEEDED: + return DisconnectCause.LIMIT_EXCEEDED; + case CallFailCause.CALL_BARRED: + return DisconnectCause.CALL_BARRED; + case CallFailCause.FDN_BLOCKED: + return DisconnectCause.FDN_BLOCKED; + case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: + return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE; + case CallFailCause.CDMA_DROP: + return DisconnectCause.CDMA_DROP; + case CallFailCause.CDMA_INTERCEPT: + return DisconnectCause.CDMA_INTERCEPT; + case CallFailCause.CDMA_REORDER: + return DisconnectCause.CDMA_REORDER; + case CallFailCause.CDMA_SO_REJECT: + return DisconnectCause.CDMA_SO_REJECT; + case CallFailCause.CDMA_RETRY_ORDER: + return DisconnectCause.CDMA_RETRY_ORDER; + case CallFailCause.CDMA_ACCESS_FAILURE: + return DisconnectCause.CDMA_ACCESS_FAILURE; + case CallFailCause.CDMA_PREEMPTED: + return DisconnectCause.CDMA_PREEMPTED; + case CallFailCause.CDMA_NOT_EMERGENCY: + return DisconnectCause.CDMA_NOT_EMERGENCY; case CallFailCause.ERROR_UNSPECIFIED: case CallFailCause.NORMAL_CLEARING: default: @@ -352,7 +430,7 @@ public class CdmaConnection extends Connection { if (serviceState == ServiceState.STATE_POWER_OFF) { return DisconnectCause.POWER_OFF; } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE - || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) { + || serviceState == ServiceState.STATE_EMERGENCY_ONLY) { return DisconnectCause.OUT_OF_SERVICE; } else if (phone.mCM.getRadioState() != CommandsInterface.RadioState.NV_READY && phone.getIccCard().getState() != RuimCard.State.READY) { @@ -374,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); @@ -392,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) { @@ -408,6 +496,21 @@ public class CdmaConnection extends Connection { changed = true; } + // A null cnapName should be the same as "" + if (TextUtils.isEmpty(dc.name)) { + if (!TextUtils.isEmpty(cnapName)) { + changed = true; + cnapName = ""; + } + } else if (!dc.name.equals(cnapName)) { + changed = true; + cnapName = dc.name; + } + + log("--dssds----"+cnapName); + cnapNamePresentation = dc.namePresentation; + numberPresentation = dc.numberPresentation; + if (newParent != parent) { if (parent != null) { parent.detach(this); @@ -494,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(); } @@ -507,25 +618,13 @@ public class CdmaConnection extends Connection { if (PhoneNumberUtils.is12Key(c)) { owner.cm.sendDtmf(c, h.obtainMessage(EVENT_DTMF_DONE)); } else if (c == PhoneNumberUtils.PAUSE) { - // From TS 22.101: - - // "The first occurrence of the "DTMF Control Digits Separator" - // shall be used by the ME to distinguish between the addressing - // digits (i.e. the phone number) and the DTMF digits...." + setPostDialState(PostDialState.PAUSE); - if (nextPostDialChar == 1) { - // The first occurrence. - // We don't need to pause here, but wait for just a bit anyway - h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), - PAUSE_DELAY_FIRST_MILLIS); - } else { - // It continues... - // "Upon subsequent occurrences of the separator, the UE shall - // pause again for 3 seconds (\u00B1 20 %) before sending any - // further DTMF digits." - h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), + // Upon occurrences of the separator, the UE shall + // pause again for 2 seconds before sending any + // further DTMF digits. + h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), PAUSE_DELAY_MILLIS); - } } else if (c == PhoneNumberUtils.WAIT) { setPostDialState(PostDialState.WAIT); } else if (c == PhoneNumberUtils.WILD) { @@ -537,19 +636,38 @@ public class CdmaConnection extends Connection { return true; } - public String - getRemainingPostDialString() { + public String getRemainingPostDialString() { if (postDialState == PostDialState.CANCELLED - || postDialState == PostDialState.COMPLETE - || postDialString == null - || postDialString.length() <= nextPostDialChar - ) { + || postDialState == PostDialState.COMPLETE + || postDialString == null + || postDialString.length() <= nextPostDialChar) { return ""; } - return postDialString.substring(nextPostDialChar); + String subStr = postDialString.substring(nextPostDialChar); + if (subStr != null) { + int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT); + int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE); + + if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) { + subStr = subStr.substring(0, wIndex); + } else if (pIndex > 0) { + subStr = subStr.substring(0, pIndex); + } + } + return subStr; } - + + public void updateParent(CdmaCall oldParent, CdmaCall newParent){ + if (newParent != oldParent) { + if (oldParent != null) { + oldParent.detach(this); + } + newParent.attachFake(this, CdmaCall.State.ACTIVE); + parent = newParent; + } + } + @Override protected void finalize() { @@ -565,8 +683,7 @@ public class CdmaConnection extends Connection { releaseWakeLock(); } - private void - processNextPostDialChar() { + void processNextPostDialChar() { char c = 0; Registrant postDialHandler; @@ -583,7 +700,7 @@ public class CdmaConnection extends Connection { c = 0; } else { boolean isValid; - + setPostDialState(PostDialState.STARTED); c = postDialString.charAt(nextPostDialChar++); @@ -653,47 +770,164 @@ public class CdmaConnection extends Connection { } /** - * Set post dial state and acquire wake lock while switching to "started" - * state, the wake lock will be released if state switches out of "started" - * state or after WAKE_LOCK_TIMEOUT_MILLIS. + * Set post dial state and acquire wake lock while switching to "started" + * state, the wake lock will be released if state switches out of "started" + * state or after WAKE_LOCK_TIMEOUT_MILLIS. * @param s new PostDialState */ private void setPostDialState(PostDialState s) { - if (postDialState != PostDialState.STARTED + if (postDialState != PostDialState.STARTED && s == PostDialState.STARTED) { acquireWakeLock(); Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); - } else if (postDialState == PostDialState.STARTED + } else if (postDialState == PostDialState.STARTED && s != PostDialState.STARTED) { h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); releaseWakeLock(); } postDialState = s; } - - private void - createWakeLock(Context context) { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + + private void createWakeLock(Context context) { + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); } - - private void - acquireWakeLock() { + + private void acquireWakeLock() { log("acquireWakeLock"); mPartialWakeLock.acquire(); } - private void - releaseWakeLock() { - synchronized(mPartialWakeLock) { + private void releaseWakeLock() { + synchronized (mPartialWakeLock) { if (mPartialWakeLock.isHeld()) { log("releaseWakeLock"); mPartialWakeLock.release(); } } } - + + private static boolean isPause(char c) { + return c == PhoneNumberUtils.PAUSE; + } + + private static boolean isWait(char c) { + return c == PhoneNumberUtils.WAIT; + } + + + + + // This function is to find the next PAUSE character index if + // multiple pauses in a row. Otherwise it finds the next non PAUSE or + // non WAIT character index. + private static int + findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) { + boolean wMatched = false; + int index = currIndex + 1; + int length = phoneNumber.length(); + while (index < length) { + char cNext = phoneNumber.charAt(index); + // if there is any W inside P/W sequence,mark it + if (isWait(cNext)) { + wMatched = true; + } + // if any characters other than P/W chars after P/W sequence + // we break out the loop and append the correct + if (!isWait(cNext) && !isPause(cNext)) { + break; + } + index++; + } + + // It means the PAUSE character(s) is in the middle of dial string + // and it needs to be handled one by one. + if ((index < length) && (index > (currIndex + 1)) && + ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) { + return (currIndex + 1); + } + return index; + } + + // This function returns either PAUSE or WAIT character to append. + // It is based on the next non PAUSE/WAIT character in the phoneNumber and the + // index for the current PAUSE/WAIT character + private static char + findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) { + char c = phoneNumber.charAt(currPwIndex); + char ret; + + // Append the PW char + ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT; + + // if there is a PAUSE in at the begining of PW character sequences, and this + // PW character sequences has more than 2 PAUSE and WAIT Characters,skip P, append W + if (isPause(c) && (nextNonPwCharIndex > (currPwIndex + 1))) { + ret = PhoneNumberUtils.WAIT; + } + return ret; + } + + + /** + * format orignal dial string + * 1) convert international dialing prefix "+" to + * string specified per region + * + * 2) handle corner cases for PAUSE/WAIT dialing: + * + * If PAUSE/WAIT sequence at the end, ignore them. + * + * If consecutive PAUSE/WAIT sequence in the middle of the string, + * and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT. + */ + public static String formatDialString(String phoneNumber) { + if (phoneNumber == null) { + return null; + } + int length = phoneNumber.length(); + StringBuilder ret = new StringBuilder(); + char c; + int currIndex = 0; + while (currIndex < length) { + c = phoneNumber.charAt(currIndex); + if (PhoneNumberUtils.isDialable(c)) { + if (c == '+') { + String ps = null; + SystemProperties.get(TelephonyProperties.PROPERTY_IDP_STRING, ps); + if (TextUtils.isEmpty(ps)) { + ps = "011"; + } + ret.append(ps); + } else { + ret.append(c); + } + } else if (isPause(c) || isWait(c)) { + if (currIndex < length - 1) { + // if PW not at the end + int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex); + // If there is non PW char following PW sequence + if (nextIndex < length) { + char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex); + ret.append(pC); + // If PW char is immediately followed by non-PW char + if (nextIndex > (currIndex + 1)) { + currIndex = nextIndex - 1; + } + } else if (nextIndex == length) { + // It means PW characters at the end, ignore + currIndex = length - 1; + } + } + } else { + ret.append(c); + } + currIndex++; + } + return ret.toString(); + } + private void log(String msg) { Log.d(LOG_TAG, "[CDMAConn] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java index 2cbad78d12a8..f2b07a880356 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java @@ -146,12 +146,16 @@ public class CdmaDataConnection extends DataConnection { null, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE)); } + private void tearDownData(Message msg) { + if (phone.mCM.getRadioState().isOn()) { + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); + } + } + protected void disconnect(Message msg) { onDisconnect = msg; if (state == State.ACTIVE) { - if (phone.mCM.getRadioState().isOn()) { - phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); - } + tearDownData(msg); } else if (state == State.ACTIVATING) { receivedDisconnectReq = true; } else { @@ -229,7 +233,7 @@ public class CdmaDataConnection extends DataConnection { switch (rilCause) { case PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING: - cause = FailCause.BARRED; + cause = FailCause.OPERATOR_BARRED; break; case PS_NET_DOWN_REASON_AUTH_FAILED: cause = FailCause.USER_AUTHENTICATION; @@ -280,7 +284,7 @@ public class CdmaDataConnection extends DataConnection { // Don't bother reporting success if there's already a // pending disconnect request, since DataConnectionTracker // has already updated its state. - disconnect(onDisconnect); + tearDownData(onDisconnect); } else { String[] response = ((String[]) ar.result); cid = Integer.parseInt(response[0]); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 651c5051f495..c3818f584d44 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -26,38 +26,31 @@ import android.content.SharedPreferences; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.AsyncResult; -import android.os.Handler; import android.os.INetStatService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Checkin; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.telephony.ServiceState; import android.telephony.TelephonyManager; -import android.util.EventLog; +import android.telephony.cdma.CdmaCellLocation; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.DataConnection.FailCause; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.TelephonyEventLog; import java.util.ArrayList; /** - * WINK:TODO: In GsmDataConnectionTracker there are - * EventLog's used quite a few places maybe - * more need to be added in this file? - * * {@hide} */ public final class CdmaDataConnectionTracker extends DataConnectionTracker { @@ -69,6 +62,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // Indicates baseband will not auto-attach private boolean noAutoAttach = false; long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + private boolean mReregisterOnReconnectFailure = false; private boolean mIsScreenOn = true; //useful for debugging @@ -98,6 +92,14 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { "com.android.internal.telephony.cdma-reconnect"; private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; + /** + * Constants for the data connection activity: + * physical link down/up + */ + private static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0; + private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1; + private static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2; + // Possibly promoate to base class, the only difference is // the INTENT_RECONNECT_ALARM action is a different string. // Do consider technology changes if it is promoted. @@ -258,14 +260,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } /** - * Simply tear down data connections due to radio off - * and don't setup again. - */ - public void cleanConnectionBeforeRadioOff() { - cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); - } - - /** * The data connection is expected to be setup while device * 1. has ruim card or non-volatile data store * 2. registered to data connection service @@ -304,13 +298,14 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); if (!isEnabled && enable) { setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, true); - return trySetupData(Phone.REASON_DATA_ENABLED); + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); } else if (!enable) { setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, false); - cleanUpConnection(true, Phone.REASON_DATA_DISABLED); - return true; - } else // isEnabled && enable - + Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true + msg.obj = Phone.REASON_DATA_DISABLED; + sendMessage(msg); + } return true; } @@ -355,16 +350,16 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState(); boolean roaming = phone.getServiceState().getRoaming(); + boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState(); if ((state == State.IDLE || state == State.SCANNING) - && (psState == ServiceState.RADIO_TECHNOLOGY_1xRTT || - psState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 || - psState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) + && (psState == ServiceState.STATE_IN_SERVICE) && ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) || mCdmaPhone.mRuimRecords.getRecordsLoaded()) && (mCdmaPhone.mSST.isConcurrentVoiceAndData() || phone.getState() == Phone.State.IDLE ) - && isDataAllowed()) { + && isDataAllowed() + && desiredPowerState) { return setupData(reason); @@ -379,7 +374,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { " phoneState=" + phone.getState() + " dataEnabled=" + getAnyDataEnabled() + " roaming=" + roaming + - " dataOnRoamingEnable=" + getDataOnRoamingEnabled()); + " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + + " desiredPowerState=" + desiredPowerState); } return false; } @@ -405,6 +401,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { mReconnectIntent = null; } + setState(State.DISCONNECTING); + for (DataConnection connBase : dataConnectionList) { CdmaDataConnection conn = (CdmaDataConnection) connBase; @@ -420,24 +418,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { stopNetStatPoll(); - /* - * If we've been asked to tear down the connection, - * set the state to DISCONNECTING. However, there's - * a race that can occur if for some reason we were - * already in the IDLE state. In that case, the call - * to conn.disconnect() above will immediately post - * a message to the handler thread that the disconnect - * is done, and if the handler runs before the code - * below does, the handler will have set the state to - * IDLE before the code below runs. If we didn't check - * for that, future calls to trySetupData would fail, - * and we would never get out of the DISCONNECTING state. - */ if (!tearDown) { setState(State.IDLE); phone.notifyDataConnection(reason); - } else if (state != State.IDLE) { - setState(State.DISCONNECTING); } } @@ -478,6 +461,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { startNetStatPoll(); // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } private void resetPollStats() { @@ -515,7 +499,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { * override it with an unconditional power on. */ } - + private Runnable mPollNetStat = new Runnable() { public void run() { @@ -570,7 +554,14 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } if (sentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) { - // we already have NUMBER_SENT_PACKETS sent without ack + // Packets sent without ack exceeded threshold. + + if (mNoRecvPollCount == 0) { + EventLog.writeEvent( + TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, + sentSinceLastRecv); + } + if (mNoRecvPollCount < NO_RECV_POLL_LIMIT) { mNoRecvPollCount++; // Slow down the poll interval to let things happen @@ -582,6 +573,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { netStatPollEnabled = false; stopNetStatPoll(); restartRadio(); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET, + NO_RECV_POLL_LIMIT); } } else { mNoRecvPollCount = 0; @@ -608,22 +601,37 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { /** * Return true if data connection need to be setup after disconnected due to * reason. - * + * * @param reason the reason why data is disconnected - * @return true if try setup data connection is need for this reason + * @return true if try setup data connection is need for this reason */ private boolean retryAfterDisconnected(String reason) { boolean retry = true; - + if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || - Phone.REASON_DATA_DISABLED.equals(reason) ) { + Phone.REASON_DATA_DISABLED.equals(reason) ) { retry = false; } return retry; - } + } private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + if (mReregisterOnReconnectFailure) { + // We have already tried to re-register to the network. + // This might be a problem with the data network. + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } else { + // Try to Re-register to the network. + Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); + mReregisterOnReconnectFailure = true; + mCdmaPhone.mSST.reRegisterNetwork(null); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + return; + } + } + Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); @@ -639,9 +647,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // double it for next time nextReconnectDelay *= 2; - if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { - nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; - } if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification " @@ -660,7 +665,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { if (state == State.FAILED) { cleanUpConnection(false, null); } - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); } protected void onNVReady() { @@ -673,8 +678,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { /** * @override com.android.internal.telephony.DataConnectionTracker */ - protected void onTrySetupData() { - trySetupData(null); + protected void onTrySetupData(String reason) { + trySetupData(reason); } /** @@ -721,6 +726,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // Make sure our reconnect delay starts at the initial value // next time the radio comes on nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator @@ -751,13 +757,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); + return; } - - if (tryAgain(cause)) { - trySetupData(reason); - } else { - startDelayedRetry(cause, reason); - } + startDelayedRetry(cause, reason); } } @@ -800,17 +802,19 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { resetPollStats(); } } else { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } } - private boolean tryAgain(FailCause cause) { - return (cause != FailCause.RADIO_NOT_AVAILABLE) - && (cause != FailCause.RADIO_OFF) - && (cause != FailCause.RADIO_ERROR_RETRY) - && (cause != FailCause.NO_SIGNAL) - && (cause != FailCause.SIM_LOCKED); + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onCleanUpConnection(boolean tearDown, String reason) { + cleanUpConnection(tearDown, reason); } private void createAllDataConnectionList() { @@ -829,7 +833,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } } - private void onCdmaDataAttached() { + private void onCdmaDataDetached() { if (state == State.CONNECTED) { startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED); @@ -837,12 +841,29 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { if (state == State.FAILED) { cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED); nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + + CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation()); + int bsid = (loc != null) ? loc.getBaseStationId() : -1; + + EventLog.List val = new EventLog.List(bsid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_SETUP_FAILED, val); } trySetupData(Phone.REASON_CDMA_DATA_DETACHED); } } + private void writeEventLogCdmaDataDrop() { + CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation()); + int bsid = (loc != null) ? loc.getBaseStationId() : -1; + EventLog.List val = new EventLog.List(bsid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_DROP, val); + } + protected void onDataStateChanged (AsyncResult ar) { + ArrayList<DataCallState> dataCallStates = (ArrayList<DataCallState>)(ar.result); + if (ar.exception != null) { // This is probably "radio not available" or something // of that sort. If so, the whole connection is going @@ -851,16 +872,37 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } if (state == State.CONNECTED) { - Log.i(LOG_TAG, "Data connection has changed."); - - int cid = -1; - EventLog.List val = new EventLog.List(cid, - TelephonyManager.getDefault().getNetworkType()); - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); - - cleanUpConnection(true, null); + if (dataCallStates.size() >= 1) { + switch (dataCallStates.get(0).active) { + case DATA_CONNECTION_ACTIVE_PH_LINK_UP: + Log.v(LOG_TAG, "onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore"); + activity = Activity.NONE; + phone.notifyDataActivity(); + break; + case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE: + Log.v(LOG_TAG, + "onDataStateChanged active=LINK_INACTIVE && CONNECTED, disconnecting/cleanup"); + writeEventLogCdmaDataDrop(); + cleanUpConnection(true, null); + break; + case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN: + Log.v(LOG_TAG, "onDataStateChanged active=LINK_DOWN && CONNECTED, dormant"); + activity = Activity.DORMANT; + phone.notifyDataActivity(); + break; + default: + Log.v(LOG_TAG, "onDataStateChanged: IGNORE unexpected DataCallState.active=" + + dataCallStates.get(0).active); + } + } else { + Log.v(LOG_TAG, "onDataStateChanged: network disconnected, clean up"); + writeEventLogCdmaDataDrop(); + cleanUpConnection(true, null); + } + } else { + // TODO: Do we need to do anything? + Log.i(LOG_TAG, "onDataStateChanged: not connected, state=" + state + " ignoring"); } - Log.i(LOG_TAG, "Data connection has changed."); } String getInterfaceName() { @@ -912,7 +954,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { break; case EVENT_CDMA_DATA_DETACHED: - onCdmaDataAttached(); + onCdmaDataDetached(); break; case EVENT_DATA_STATE_CHANGED: diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java new file mode 100644 index 000000000000..7402769cee40 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java @@ -0,0 +1,263 @@ +/* + * 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.telephony.cdma; +import static com.android.internal.telephony.RILConstants.*; +import android.os.Parcel; + +public final class CdmaInformationRecords { + public Object record; + + /** + * Record type identifier + */ + public static final int RIL_CDMA_DISPLAY_INFO_REC = 0; + public static final int RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC = 1; + public static final int RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC = 2; + public static final int RIL_CDMA_CONNECTED_NUMBER_INFO_REC = 3; + public static final int RIL_CDMA_SIGNAL_INFO_REC = 4; + public static final int RIL_CDMA_REDIRECTING_NUMBER_INFO_REC = 5; + public static final int RIL_CDMA_LINE_CONTROL_INFO_REC = 6; + public static final int RIL_CDMA_EXTENDED_DISPLAY_INFO_REC = 7; + public static final int RIL_CDMA_T53_CLIR_INFO_REC = 8; + public static final int RIL_CDMA_T53_RELEASE_INFO_REC = 9; + public static final int RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC = 10; + + public CdmaInformationRecords(Parcel p) { + int id = p.readInt(); + switch (id) { + case RIL_CDMA_DISPLAY_INFO_REC: + case RIL_CDMA_EXTENDED_DISPLAY_INFO_REC: + record = new CdmaDisplayInfoRec(id, p.readString()); + break; + + case RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC: + case RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC: + case RIL_CDMA_CONNECTED_NUMBER_INFO_REC: + record = new CdmaNumberInfoRec(id, p.readString(), p.readInt(), p.readInt(), + p.readInt(), p.readInt()); + break; + + case RIL_CDMA_SIGNAL_INFO_REC: + record = new CdmaSignalInfoRec(p.readInt(), p.readInt(), p.readInt(), p.readInt()); + break; + + case RIL_CDMA_REDIRECTING_NUMBER_INFO_REC: + record = new CdmaRedirectingNumberInfoRec(p.readString(), p.readInt(), p.readInt(), + p.readInt(), p.readInt(), p.readInt()); + break; + + case RIL_CDMA_LINE_CONTROL_INFO_REC: + record = new CdmaLineControlInfoRec(p.readInt(), p.readInt(), p.readInt(), + p.readInt()); + break; + + case RIL_CDMA_T53_CLIR_INFO_REC: + record = new CdmaT53ClirInfoRec(p.readInt()); + break; + + case RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC: + record = new CdmaT53AudioControlInfoRec(p.readInt(), p.readInt()); + break; + + case RIL_CDMA_T53_RELEASE_INFO_REC: + // TODO(Moto): WHAT to do, for now fall through and throw exception + default: + throw new RuntimeException("RIL_UNSOL_CDMA_INFO_REC: unsupported record. Got " + + CdmaInformationRecords.idToString(id) + " "); + + } + } + + public static String idToString(int id) { + switch(id) { + case RIL_CDMA_DISPLAY_INFO_REC: return "RIL_CDMA_DISPLAY_INFO_REC"; + case RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC: return "RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC"; + case RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC: return "RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC"; + case RIL_CDMA_CONNECTED_NUMBER_INFO_REC: return "RIL_CDMA_CONNECTED_NUMBER_INFO_REC"; + case RIL_CDMA_SIGNAL_INFO_REC: return "RIL_CDMA_SIGNAL_INFO_REC"; + case RIL_CDMA_REDIRECTING_NUMBER_INFO_REC: return "RIL_CDMA_REDIRECTING_NUMBER_INFO_REC"; + case RIL_CDMA_LINE_CONTROL_INFO_REC: return "RIL_CDMA_LINE_CONTROL_INFO_REC"; + case RIL_CDMA_EXTENDED_DISPLAY_INFO_REC: return "RIL_CDMA_EXTENDED_DISPLAY_INFO_REC"; + case RIL_CDMA_T53_CLIR_INFO_REC: return "RIL_CDMA_T53_CLIR_INFO_REC"; + case RIL_CDMA_T53_RELEASE_INFO_REC: return "RIL_CDMA_T53_RELEASE_INFO_REC"; + case RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC: return "RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC"; + default: return "<unknown record>"; + } + } + + /** + * Signal Information record from 3GPP2 C.S005 3.7.5.5 + */ + public static class CdmaSignalInfoRec { + public boolean isPresent; /* non-zero if signal information record is present */ + public int signalType; + public int alertPitch; + public int signal; + + public CdmaSignalInfoRec() {} + + public CdmaSignalInfoRec(int isPresent, int signalType, int alertPitch, int signal) { + this.isPresent = isPresent != 0; + this.signalType = signalType; + this.alertPitch = alertPitch; + this.signal = signal; + } + + @Override + public String toString() { + return "CdmaSignalInfo: {" + + " isPresent: " + isPresent + + ", signalType: " + signalType + + ", alertPitch: " + alertPitch + + ", signal: " + signal + + " }"; + } + } + + public static class CdmaDisplayInfoRec { + public int id; + public String alpha; + + public CdmaDisplayInfoRec(int id, String alpha) { + this.id = id; + this.alpha = alpha; + } + + @Override + public String toString() { + return "CdmaDisplayInfoRec: {" + + " id: " + CdmaInformationRecords.idToString(id) + + ", alpha: " + alpha + + " }"; + } + } + + public static class CdmaNumberInfoRec { + public int id; + public String number; + public byte numberType; + public byte numberPlan; + public byte pi; + public byte si; + + public CdmaNumberInfoRec(int id, String number, int numberType, int numberPlan, int pi, + int si) { + this.number = number; + this.numberType = (byte)numberType; + this.numberPlan = (byte)numberPlan; + this.pi = (byte)pi; + this.si = (byte)si; + } + + @Override + public String toString() { + return "CdmaNumberInfoRec: {" + + " id: " + CdmaInformationRecords.idToString(id) + + ", number: " + number + + ", numberType: " + numberType + + ", numberPlan: " + numberPlan + + ", pi: " + pi + + ", si: " + si + + " }"; + } + } + + public static class CdmaRedirectingNumberInfoRec { + public static final int REASON_UNKNOWN = 0; + public static final int REASON_CALL_FORWARDING_BUSY = 1; + public static final int REASON_CALL_FORWARDING_NO_REPLY = 2; + public static final int REASON_CALLED_DTE_OUT_OF_ORDER = 9; + public static final int REASON_CALL_FORWARDING_BY_THE_CALLED_DTE = 10; + public static final int REASON_CALL_FORWARDING_UNCONDITIONAL = 15; + + public CdmaNumberInfoRec numberInfoRec; + public int redirectingReason; + + public CdmaRedirectingNumberInfoRec(String number, int numberType, int numberPlan, + int pi, int si, int reason) { + numberInfoRec = new CdmaNumberInfoRec(RIL_CDMA_REDIRECTING_NUMBER_INFO_REC, + number, numberType, numberPlan, pi, si); + redirectingReason = reason; + } + + @Override + public String toString() { + return "CdmaNumberInfoRec: {" + + " numberInfoRec: " + numberInfoRec + + ", redirectingReason: " + redirectingReason + + " }"; + } + } + + public static class CdmaLineControlInfoRec { + public byte lineCtrlPolarityIncluded; + public byte lineCtrlToggle; + public byte lineCtrlReverse; + public byte lineCtrlPowerDenial; + + public CdmaLineControlInfoRec(int lineCtrlPolarityIncluded, int lineCtrlToggle, + int lineCtrlReverse, int lineCtrlPowerDenial) { + this.lineCtrlPolarityIncluded = (byte)lineCtrlPolarityIncluded; + this.lineCtrlToggle = (byte)lineCtrlToggle; + this.lineCtrlReverse = (byte)lineCtrlReverse; + this.lineCtrlPowerDenial = (byte)lineCtrlPowerDenial; + } + + @Override + public String toString() { + return "CdmaLineControlInfoRec: {" + + " lineCtrlPolarityIncluded: " + lineCtrlPolarityIncluded + + " lineCtrlToggle: " + lineCtrlToggle + + " lineCtrlReverse: " + lineCtrlReverse + + " lineCtrlPowerDenial: " + lineCtrlPowerDenial + + " }"; + } + } + + public static class CdmaT53ClirInfoRec { + public byte cause; + + public CdmaT53ClirInfoRec(int cause) { + this.cause = (byte)cause; + } + + @Override + public String toString() { + return "CdmaT53ClirInfoRec: {" + + " cause: " + cause + + " }"; + } + } + + public static class CdmaT53AudioControlInfoRec { + public byte uplink; + public byte downlink; + + public CdmaT53AudioControlInfoRec(int uplink, int downlink) { + this.uplink = (byte) uplink; + this.downlink = (byte) downlink; + } + + @Override + public String toString() { + return "CdmaT53AudioControlInfoRec: {" + + " uplink: " + uplink + + " downlink: " + downlink + + " }"; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index 42c058333536..ecdc8f643b0d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -17,21 +17,27 @@ 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; +import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SMSDispatcher; -//import com.android.internal.telephony.SMSDispatcher.SmsTracker; import com.android.internal.telephony.cdma.SmsMessage; import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; @@ -42,8 +48,11 @@ import java.util.HashMap; final class CdmaSMSDispatcher extends SMSDispatcher { private static final String TAG = "CDMA"; + private CDMAPhone mCdmaPhone; + CdmaSMSDispatcher(CDMAPhone phone) { super(phone); + mCdmaPhone = phone; } /** @@ -58,147 +67,88 @@ 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; } - SmsMessage sms = (SmsMessage) smsb; - int teleService; - boolean handled = false; // Decode BD stream and set sms variables. + SmsMessage sms = (SmsMessage) smsb; sms.parseSms(); - teleService = sms.getTeleService(); - - // Teleservices W(E)MT and VMN are handled together: - if ((SmsEnvelope.TELESERVICE_WMT == teleService) - ||(SmsEnvelope.TELESERVICE_WEMT == teleService) - ||(SmsEnvelope.TELESERVICE_VMN == teleService)){ - // From here on we need decoded BD. - // Special case the message waiting indicator messages - if (sms.isMWISetMessage()) { - ((CDMAPhone) mPhone).updateMessageWaitingIndicator(true); - - if (sms.isMwiDontStore()) { - handled = true; - } - - if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator set SMS shouldStore=" + !handled); - } - } else if (sms.isMWIClearMessage()) { - ((CDMAPhone) mPhone).updateMessageWaitingIndicator(false); - - if (sms.isMwiDontStore()) { - handled = true; - } - - if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator clear SMS shouldStore=" + !handled); - } - } - } + int teleService = sms.getTeleService(); + boolean handled = false; - if (null == sms.getUserData()){ - handled = true; + if (sms.getUserData() == null) { if (Config.LOGD) { Log.d(TAG, "Received SMS without user data"); } + handled = true; } - if (handled) return; - - if (SmsEnvelope.TELESERVICE_WAP == teleService){ - processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress()); - return; + if (handled) { + return Intents.RESULT_SMS_HANDLED; } - // Parse the headers to see if this is partial, or port addressed - int referenceNumber = -1; - int count = 0; - int sequence = 0; - int destPort = -1; - // From here on we need BD distributed to SMS member variables. - - SmsHeader header = sms.getUserDataHeader(); - if (header != null) { - for (SmsHeader.Element element : header.getElements()) { - try { - switch (element.getID()) { - case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = data[0] & 0xff; - count = data[1] & 0xff; - sequence = data[2] & 0xff; - - // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); - count = data[2] & 0xff; - sequence = data[3] & 0xff; - - // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { - byte[] data = element.getData(); - - destPort = (data[0] & 0xff) << 8; - destPort |= (data[1] & 0xff); - - break; - } - } - } catch (ArrayIndexOutOfBoundsException e) { - Log.e(TAG, "Bad element in header", e); - return; // TODO: NACK the message or something, don't just discard. - } - } + if (SmsEnvelope.TELESERVICE_WAP == teleService){ + 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; } - if (referenceNumber == -1) { - // notify everyone of the message if it isn't partial + /** + * TODO(cleanup): Why are we using a getter method for this + * (and for so many other sms fields)? Trivial getters and + * setters like this are direct violations of the style guide. + * If the purpose is to protect agaist writes (by not + * providing a setter) then any protection is illusory (and + * hence bad) for cases where the values are not primitives, + * such as this call for the header. Since this is an issue + * with the public API it cannot be changed easily, but maybe + * something can be done eventually. + */ + SmsHeader smsHeader = sms.getUserDataHeader(); + + /** + * TODO(cleanup): Since both CDMA and GSM use the same header + * format, this dispatch processing is naturally identical, + * and code should probably not be replicated explicitly. + */ + // See if message is partial or port addressed. + if ((smsHeader == null) || (smsHeader.concatRef == null)) { + // Message is not partial (not part of concatenated sequence). byte[][] pdus = new byte[1][]; pdus[0] = sms.getPdu(); - if (destPort != -1) {// GSM-style WAP indication - if (destPort == SmsHeader.PORT_WAP_PUSH) { - mWapPush.dispatchWapPdu(sms.getUserData()); + if (smsHeader != null && smsHeader.portAddrs != null) { + if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { + // GSM-style WAP indication + 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, destPort); } else { - // It's a normal message, dispatch it + // Normal short and non-port-addressed message, dispatch it. dispatchPdus(pdus); } + return Activity.RESULT_OK; } else { - // Process the message part - processMessagePart(sms, referenceNumber, sequence, count, destPort); + // Process the message part. + return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); } } @@ -208,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 ="); @@ -260,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 @@ -271,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)); } @@ -280,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(); } @@ -300,55 +261,66 @@ 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; } } } /** {@inheritDoc} */ - protected void sendMultipartText(String destinationAddress, String scAddress, + protected void sendMultipartText(String destAddr, String scAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { - int ref = ++sConcatenatedRef & 0xff; + /** + * TODO(cleanup): There is no real code difference between + * this and the GSM version, and hence it should be moved to + * the base class or consolidated somehow, provided calling + * the proper submitpdu stuff can be arranged. + */ - for (int i = 0, count = parts.size(); i < count; i++) { - // build SmsHeader data - byte[] data = new byte[5]; - data[0] = (byte) SmsHeader.CONCATENATED_8_BIT_REFERENCE; - data[1] = (byte) 3; // 3 bytes follow - data[2] = (byte) ref; // reference #, unique per message - data[3] = (byte) count; // total part count - data[4] = (byte) (i + 1); // 1-based sequence + int refNumber = getNextConcatenatedRef() & 0x00FF; - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; + for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = i + 1; // 1-based sequence + concatRef.msgCount = msgCount; + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } + + PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } - SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, data); + UserData uData = new UserData(); + uData.payloadStr = parts.get(i); + uData.userDataHeader = smsHeader; + + SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr, + uData, deliveryIntent != null); - sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); + sendSubmitPdu(submitPdu, sentIntent, deliveryIntent); } } - protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + protected void sendSubmitPdu(SmsMessage.SubmitPdu submitPdu, PendingIntent sentIntent, PendingIntent deliveryIntent) { - super.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); + sendRawPdu(submitPdu.encodedScAddress, submitPdu.encodedMessage, + sentIntent, deliveryIntent); } /** {@inheritDoc} */ @@ -369,16 +341,16 @@ final class CdmaSMSDispatcher extends SMSDispatcher { } /** {@inheritDoc} */ - protected void acknowledgeLastIncomingSms(boolean success, Message response){ + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ // FIXME unit test leaves cm == null. this should change if (mCm != null) { - mCm.acknowledgeLastIncomingCdmaSms(success, response); + mCm.acknowledgeLastIncomingCdmaSms(success, resultToCause(result), response); } } /** {@inheritDoc} */ protected void activateCellBroadcastSms(int activate, Message response) { - mCm.activateCdmaBroadcastSms(activate, response); + mCm.setCdmaBroadcastActivation((activate == 0), response); } /** {@inheritDoc} */ @@ -391,4 +363,17 @@ final class CdmaSMSDispatcher extends SMSDispatcher { mCm.setCdmaBroadcastConfig(configValuesArray, response); } + private int resultToCause(int rc) { + switch (rc) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + // Cause code is ignored on success. + return 0; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM; + } + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index d5cad1ca9cee..e75a3331bb64 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -19,11 +19,15 @@ package com.android.internal.telephony.cdma; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; +import android.content.ContentValues; import android.content.Intent; import android.database.ContentObserver; +import android.database.SQLException; +import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; @@ -31,13 +35,17 @@ import android.os.SystemProperties; import android.provider.Checkin; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.provider.Telephony; import android.provider.Telephony.Intents; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.cdma.CdmaCellLocation; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.util.Config; import android.util.TimeUtils; +import java.util.Calendar; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; @@ -65,14 +73,12 @@ import java.util.TimeZone; * {@hide} */ final class CdmaServiceStateTracker extends ServiceStateTracker { + //***** Instance Variables CDMAPhone phone; CdmaCellLocation cellLoc; CdmaCellLocation newCellLoc; - int rssi = 99; // signal strength 0-31, 99=unknown - // That's "received signal strength indication" fyi - /** * The access technology currently in use: DATA_ACCESS_ */ @@ -80,35 +86,66 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { private int newNetworkType = 0; private boolean mCdmaRoaming = false; + private int mRoamingIndicator; + private boolean mIsInPrl; + private int mDefaultRoamingIndicator; - private int cdmaDataConnectionState = -1;//Initial we assume no data connection - private int newCdmaDataConnectionState = -1;//Initial we assume no data connection + // Initially we assume no data connection + private int cdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE; + private int newCdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE; private int mRegistrationState = -1; private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList(); private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList(); + // Sometimes we get the NITZ time before we know what country we are in. + // Keep the time zone information from the NITZ string so we can fix + // the time zone once know the country. + private boolean mNeedFixZone = false; + private int mZoneOffset; + private boolean mZoneDst; + private long mZoneTime; private boolean mGotCountryCode = false; + String mSavedTimeZone; + long mSavedTime; + long mSavedAtTime; // We can't register for SIM_RECORDS_LOADED immediately because the // SIMRecords object may not be instantiated yet. - private boolean mNeedToRegForRuimLoaded; + private boolean mNeedToRegForRuimLoaded = false; + + // Wake lock used while setting time of day. + private PowerManager.WakeLock mWakeLock; + private static final String WAKELOCK_TAG = "ServiceStateTracker"; // Keep track of SPN display rules, so we only broadcast intent if something changes. private String curSpn = null; - private String curPlmn = null; + private String curPlmn = null; // it contains the name of the registered network in CDMA can + // be the ONS or ERI text private int curSpnRule = 0; + private String mMdn; + private int mHomeSystemId; + private int mHomeNetworkId; + private String mMin; + + private boolean isEriTextLoaded = false; + private boolean isSubscriptionFromRuim = false; + + // Registration Denied Reason, General/Authentication Failure, used only for debugging purposes + private String mRegistrationDeniedReason; + //***** Constants static final String LOG_TAG = "CDMA"; - static final String TMUK = "23430"; private ContentResolver cr; + private String currentCarrier = null; private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { Log.i("CdmaServiceStateTracker", "Auto time state called ..."); - //NOTE in CDMA NITZ is not used + revertToNitz(); + } }; @@ -124,16 +161,23 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { newSS = new ServiceState(); cellLoc = new CdmaCellLocation(); newCellLoc = new CdmaCellLocation(); + mSignalStrength = new SignalStrength(); + + PowerManager powerManager = + (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null); + cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); cm.registerForRUIMReady(this, EVENT_RUIM_READY, null); - phone.registerForNvLoaded(this, EVENT_NV_LOADED,null); + cm.registerForNVReady(this, EVENT_NV_READY, null); + phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null); // system setting property AIRPLANE_MODE_ON is set in Settings. int airplaneMode = Settings.System.getInt( @@ -145,7 +189,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { cr.registerContentObserver( Settings.System.getUriFor(Settings.System.AUTO_TIME), true, mAutoTimeObserver); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mNeedToRegForRuimLoaded = true; } @@ -156,14 +200,17 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { cm.unregisterForRadioStateChanged(this); cm.unregisterForNetworkStateChanged(this); cm.unregisterForRUIMReady(this); - phone.unregisterForNvLoaded(this); + cm.unregisterForNVReady(this); + phone.unregisterForEriFileLoaded(this); phone.mRuimRecords.unregisterForRecordsLoaded(this); cm.unSetOnSignalStrengthUpdate(this); + cm.unSetOnNITZTime(this); cr.unregisterContentObserver(this.mAutoTimeObserver); } + @Override protected void finalize() { - if(DBG) Log.d(LOG_TAG, "CdmaServiceStateTracker finalized"); + if (DBG) log("CdmaServiceStateTracker finalized"); } void registerForNetworkAttach(Handler h, int what, Object obj) { @@ -190,9 +237,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { Registrant r = new Registrant(h, what, obj); cdmaDataConnectionAttachedRegistrants.add(r); - if (cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT - || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 - || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) { + if (cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE) { r.notifyRegistrant(); } } @@ -211,9 +256,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { Registrant r = new Registrant(h, what, obj); cdmaDataConnectionDetachedRegistrants.add(r); - if (cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT - && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 - && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A) { + if (cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE) { r.notifyRegistrant(); } } @@ -228,10 +271,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { EVENT_GET_LOC_DONE_CDMA, onComplete)); } - - //***** Overridden from ServiceStateTracker - public void - handleMessage (Message msg) { + @Override + public void handleMessage (Message msg) { AsyncResult ar; int[] ints; String[] strings; @@ -246,13 +287,21 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { // The RUIM is now ready i.e if it was locked // it has been unlocked. At this stage, the radio is already // powered on. + isSubscriptionFromRuim = true; if (mNeedToRegForRuimLoaded) { phone.mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); mNeedToRegForRuimLoaded = false; } // restore the previous network selection. - phone.restoreSavedNetworkSelection(null); + pollState(); + + // Signal strength polling stops when radio is off + queueNextSignalStrengthPoll(); + break; + + case EVENT_NV_READY: + isSubscriptionFromRuim = false; pollState(); // Signal strength polling stops when radio is off queueNextSignalStrengthPoll(); @@ -328,9 +377,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } break; - case EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA: //Fall through - case EVENT_POLL_STATE_REGISTRATION_CDMA: //Fall through + case EVENT_POLL_STATE_REGISTRATION_CDMA: case EVENT_POLL_STATE_OPERATOR_CDMA: + case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: ar = (AsyncResult) msg.obj; handlePollStateResult(msg.what, ar); break; @@ -341,6 +390,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); break; + case EVENT_NITZ_TIME: + ar = (AsyncResult) msg.obj; + + String nitzString = (String)((Object[])ar.result)[0]; + long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); + + setTimeFromNITZString(nitzString, nitzReceiveTime); + break; + case EVENT_SIGNAL_STRENGTH_UPDATE: // This is a notification from // CommandsInterface.setOnSignalStrengthUpdate @@ -355,7 +413,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { break; case EVENT_RUIM_RECORDS_LOADED: - case EVENT_NV_LOADED: updateSpnDisplay(); break; @@ -367,6 +424,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } break; + case EVENT_ERI_FILE_LOADED: + // Repoll the state once the ERI file has been loaded + if (DBG) log("[CdmaServiceStateTracker] ERI file has been loaded, repolling."); + pollState(); + break; + default: Log.e(LOG_TAG, "Unhandled message with number: " + msg.what); break; @@ -375,8 +438,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { //***** Private Instance Methods - protected void setPowerStateToDesired() - { + @Override + protected void setPowerStateToDesired() { // If we want it on and it's off, turn it on if (mDesiredPowerState && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) { @@ -390,14 +453,18 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { (dcTracker.getAnyDataEnabled() ? 1 : 0) ); EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF, val); } - dcTracker.cleanConnectionBeforeRadioOff(); - - // poll data state up to 15 times, with a 100ms delay + Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true + msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF; + dcTracker.sendMessage(msg); + + // Poll data state up to 15 times, with a 100ms delay // totaling 1.5 sec. Normal data disable action will finish in 100ms. for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) { - if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED - && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) { - Log.d(LOG_TAG, "Data shutdown complete."); + DataConnectionTracker.State currentState = dcTracker.getState(); + if (currentState != DataConnectionTracker.State.CONNECTED + && currentState != DataConnectionTracker.State.DISCONNECTING) { + if (DBG) log("Data shutdown complete."); break; } SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS); @@ -407,20 +474,31 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } // Otherwise, we're in the desired state } + @Override protected void updateSpnDisplay() { + String spn = ""; + boolean showSpn = false; + String plmn = ""; + boolean showPlmn = false; + int rule = 0; + if (cm.getRadioState().isRUIMReady()) { + // TODO RUIM SPN is not implemnted, EF_SPN has to be read and Display Condition + // Character Encoding, Language Indicator and SPN has to be set + // rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric()); + // spn = phone.mSIMRecords.getServiceProvideName(); + plmn = ss.getOperatorAlphaLong(); // mOperatorAlphaLong contains the ONS + // showSpn = (rule & ... + showPlmn = true; // showPlmn = (rule & ... - // TODO Check this method again, because it is not sure at the moment how - // the RUIM handles the SIM stuff - - //int rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric()); - String spn = null; //phone.mRuimRecords.getServiceProviderName(); - String plmn = ss.getOperatorAlphaLong(); + } else { + // In this case there is no SPN available from RUIM, we show the ERI text + plmn = ss.getOperatorAlphaLong(); // mOperatorAlphaLong contains the ERI text + showPlmn = true; + } - if (!TextUtils.equals(this.curPlmn, plmn)) { - //TODO (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; - boolean showSpn = false; - //TODO (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; - boolean showPlmn = true; + if (rule != curSpnRule + || !TextUtils.equals(spn, curSpn) + || !TextUtils.equals(plmn, curPlmn)) { Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); intent.putExtra(Intents.EXTRA_SPN, spn); @@ -429,17 +507,17 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { phone.getContext().sendStickyBroadcast(intent); } - //curSpnRule = rule; - //curSpn = spn; - this.curPlmn = plmn; + curSpnRule = rule; + curSpn = spn; + curPlmn = plmn; } /** * Handle the result of one of the pollState()-related requests */ - protected void - handlePollStateResult (int what, AsyncResult ar) { + @Override + protected void handlePollStateResult (int what, AsyncResult ar) { int ints[]; String states[]; @@ -473,82 +551,129 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } } else try { switch (what) { - case EVENT_POLL_STATE_REGISTRATION_CDMA: - //offset, because we don't want the first 3 values in the int-array - final int offset = 3; + case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE. states = (String[])ar.result; - int responseValuesRegistrationState[] = { - -1, //[0] radioTechnology - -1, //[1] baseStationId - -1, //[2] baseStationLatitude - -1, //[3] baseStationLongitude - 0, //[4] cssIndicator; init with 0, because it is treated as a boolean - -1, //[5] systemId - -1, //[6] networkId - -1, //[7] TSB-58 Roaming indicator // NEWRIL:TODO UNUSED - -1, //[8] Indicates if current system is in PRL // NEWRIL:TODO UNUSED - -1, //[9] Is default roaming indicator from PRL // NEWRIL:TODO UNUSED - -1, //[10] If registration state is 3 this is reason for denial // NEWRIL:TODO UNUSED - }; - - if (states.length > 0) { + int registrationState = 4; //[0] registrationState + int radioTechnology = -1; //[3] radioTechnology + int baseStationId = -1; //[4] baseStationId + int baseStationLatitude = -1; //[5] baseStationLatitude + int baseStationLongitude = -1; //[6] baseStationLongitude + int cssIndicator = 0; //[7] init with 0, because it is treated as a boolean + int systemId = 0; //[8] systemId + int networkId = 0; //[9] networkId + int roamingIndicator = -1; //[10] Roaming indicator + int systemIsInPrl = 0; //[11] Indicates if current system is in PRL + int defaultRoamingIndicator = 0; //[12] Is default roaming indicator from PRL + int reasonForDenial = 0; //[13] Denial reason if registrationState = 3 + + if (states.length == 14) { try { - this.mRegistrationState = Integer.parseInt(states[0]); - if (states.length >= 10) { - for(int i = 0; i < states.length - offset; i++) { - if (states[i + offset] != null - && states[i + offset].length() > 0) { - try { - responseValuesRegistrationState[i] = - Integer.parseInt(states[i + offset], 16); - } - catch(NumberFormatException ex) { - Log.w(LOG_TAG, "Warning! There is an unexpected value" - + "returned as response from " - + "RIL_REQUEST_REGISTRATION_STATE."); - } - } - } - } - else { - Log.e(LOG_TAG, "Too less parameters returned from" - + " RIL_REQUEST_REGISTRATION_STATE"); - } - } catch (NumberFormatException ex) { + registrationState = Integer.parseInt(states[0]); + radioTechnology = Integer.parseInt(states[3]); + baseStationId = Integer.parseInt(states[4], 16); + baseStationLatitude = Integer.parseInt(states[5], 16); + baseStationLongitude = Integer.parseInt(states[6], 16); + cssIndicator = Integer.parseInt(states[7]); + systemId = Integer.parseInt(states[8]); + networkId = Integer.parseInt(states[9]); + roamingIndicator = Integer.parseInt(states[10]); + systemIsInPrl = Integer.parseInt(states[11]); + defaultRoamingIndicator = Integer.parseInt(states[12]); + reasonForDenial = Integer.parseInt(states[13]); + } + catch(NumberFormatException ex) { Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); } + } else { + throw new RuntimeException("Warning! Wrong number of parameters returned from " + + "RIL_REQUEST_REGISTRATION_STATE: expected 14 got " + + states.length); } - mCdmaRoaming = regCodeIsRoaming(this.mRegistrationState); - this.newCdmaDataConnectionState = - radioTechnologyToServiceState(responseValuesRegistrationState[0]); - newSS.setState (regCodeToServiceState(this.mRegistrationState)); - newSS.setRadioTechnology(responseValuesRegistrationState[0]); - newSS.setCssIndicator(responseValuesRegistrationState[4]); - newSS.setSystemAndNetworkId(responseValuesRegistrationState[5], - responseValuesRegistrationState[6]); + mRegistrationState = registrationState; + mCdmaRoaming = regCodeIsRoaming(registrationState); + newSS.setState (regCodeToServiceState(registrationState)); + + this.newCdmaDataConnectionState = radioTechnologyToDataServiceState(radioTechnology); + newSS.setRadioTechnology(radioTechnology); + newNetworkType = radioTechnology; + + newSS.setCssIndicator(cssIndicator); + newSS.setSystemAndNetworkId(systemId, networkId); + mRoamingIndicator = roamingIndicator; + mIsInPrl = (systemIsInPrl == 0) ? false : true; + mDefaultRoamingIndicator = defaultRoamingIndicator; - newNetworkType = responseValuesRegistrationState[0]; // values are -1 if not available - newCellLoc.setCellLocationData(responseValuesRegistrationState[1], - responseValuesRegistrationState[2], - responseValuesRegistrationState[3]); + newCellLoc.setCellLocationData(baseStationId, baseStationLatitude, + baseStationLongitude); + + if (reasonForDenial == 0) { + mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN; + } else if (reasonForDenial == 1) { + mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH; + } else { + mRegistrationDeniedReason = ""; + } + + if (mRegistrationState == 3) { + if (DBG) log("Registration denied, " + mRegistrationDeniedReason); + } break; - case EVENT_POLL_STATE_OPERATOR_CDMA: + case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR String opNames[] = (String[])ar.result; if (opNames != null && opNames.length >= 3) { - newSS.setOperatorName (opNames[0], opNames[1], opNames[2]); + if (cm.getRadioState().isNVReady()) { + // In CDMA in case on NV the ss.mOperatorAlphaLong is set later with the + // ERI text, so here it is ignored what is coming from the modem + newSS.setOperatorName(null, opNames[1], opNames[2]); + } else { + newSS.setOperatorName(opNames[0], opNames[1], opNames[2]); + } + + if (!(opNames[2].equals(currentCarrier))) { + // TODO(Moto): jsh asks, "This uses the MCC+MNC of the current registered + // network to set the "current" entry in the APN table. But the correct + // entry should be the MCC+MNC that matches the subscribed operator + // (eg, phone issuer). These can be different when roaming." + try { + // Set the current field of the telephony provider according to + // the CDMA's operator + Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); + ContentValues map = new ContentValues(); + map.put(Telephony.Carriers.NUMERIC, opNames[2]); + cr.insert(uri, map); + // save current carrier for the next time check + currentCarrier = opNames[2]; + } catch (SQLException e) { + Log.e(LOG_TAG, "Can't store current operator", e); + } + } else { + Log.i(LOG_TAG, "current carrier is not changed"); + } + } else { + Log.w(LOG_TAG, "error parsing opNames"); } break; - case EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA: - ints = (int[])ar.result; - newSS.setIsManualSelection(ints[0] == 1); + case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION + String cdmaSubscription[] = (String[])ar.result; + + if (cdmaSubscription != null && cdmaSubscription.length >= 4) { + mMdn = cdmaSubscription[0]; + mHomeSystemId = Integer.parseInt(cdmaSubscription[1], 16); + mHomeNetworkId = Integer.parseInt(cdmaSubscription[2], 16); + mMin = cdmaSubscription[3]; + + } else { + Log.w(LOG_TAG, "error parsing cdmaSubscription"); + } break; + default: Log.e(LOG_TAG, "RIL response handle in wrong phone!" + " Expected CDMA RIL request and get GSM RIL request."); @@ -563,29 +688,55 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { pollingContext[0]--; if (pollingContext[0] == 0) { - newSS.setRoaming(isRoamingBetweenOperators(mCdmaRoaming, newSS)); + boolean namMatch = false; + if ((mHomeSystemId != 0) && (mHomeSystemId == newSS.getSystemId()) ) { + namMatch = true; + } - switch(this.mRegistrationState) { - case ServiceState.REGISTRATION_STATE_HOME_NETWORK: - newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_HOME_NETWORK); - break; - case ServiceState.REGISTRATION_STATE_ROAMING: - newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_ROAMING); - break; - case ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE: - newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE); - break; - default: - Log.w(LOG_TAG, "Received a different registration state, " - + "but don't changed the extended cdma roaming mode."); + // Setting SS Roaming (general) + if (isSubscriptionFromRuim) { + newSS.setRoaming(isRoamingBetweenOperators(mCdmaRoaming, newSS)); + } else { + newSS.setRoaming(mCdmaRoaming); + } + + // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator + // TODO(Teleca): Validate this is correct. + if (mIsInPrl) { + if (namMatch && (mRoamingIndicator <= 2)) { + // System is acquired, prl match, nam match and mRoamingIndicator <= 2 + newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF); + } else { + // System is acquired, prl match, no nam match or mRoamingIndicator > 2 + newSS.setCdmaRoamingIndicator(mRoamingIndicator); + } + } else { + if (mRegistrationState == 5) { + // System is acquired but prl not loaded or no prl match + newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH); + } else { + // Use the default indicator + } + } + + newSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator); + + // NOTE: Some operator may require to override the mCdmaRoaming (set by the modem) + // depending on the mRoamingIndicator. + + if (DBG) { + log("Set CDMA Roaming Indicator to: " + newSS.getCdmaRoamingIndicator() + + ". mCdmaRoaming = " + mCdmaRoaming + ", namMatch = " + namMatch + + ", mIsInPrl = " + mIsInPrl + ", mRoamingIndicator = " + mRoamingIndicator + + ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator); } pollStateDone(); } } - private void setRssiDefaultValues() { - rssi = 99; + private void setSignalStrengthDefaultValues() { + mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, false); } /** @@ -606,7 +757,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case RADIO_UNAVAILABLE: newSS.setStateOutOfService(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; pollStateDone(); @@ -615,7 +766,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case RADIO_OFF: newSS.setStateOff(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; pollStateDone(); @@ -627,10 +778,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { log("Radio Technology Change ongoing, setting SS to off"); newSS.setStateOff(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; - pollStateDone(); + //NOTE: pollStateDone() is not needed in this case break; default: @@ -639,20 +790,21 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { // are allowed to arrive out-of-order pollingContext[0]++; - //RIL_REQUEST_OPERATOR is necessary for CDMA - cm.getOperator( - obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext)); - - pollingContext[0]++; - //RIL_REQUEST_REGISTRATION_STATE is necessary for CDMA - cm.getRegistrationState( - obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext)); - - pollingContext[0]++; - //RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE necessary for CDMA - cm.getNetworkSelectionMode( - obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA, pollingContext)); - break; + // RIL_REQUEST_CDMA_SUBSCRIPTION is necessary for CDMA + cm.getCDMASubscription( + obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION, pollingContext)); + + pollingContext[0]++; + // RIL_REQUEST_OPERATOR is necessary for CDMA + cm.getOperator( + obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext)); + + pollingContext[0]++; + // RIL_REQUEST_REGISTRATION_STATE is necessary for CDMA + cm.getRegistrationState( + obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext)); + + break; } } @@ -683,14 +835,45 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return ret; } - private void - pollStateDone() { - if (DBG) { - Log.d(LOG_TAG, "Poll ServiceState done: " + - " oldSS=[" + ss ); - Log.d(LOG_TAG, "Poll ServiceState done: " + - " newSS=[" + newSS); + private void fixTimeZone(String isoCountryCode) { + TimeZone zone = null; + // If the offset is (0, false) and the time zone property + // is set, use the time zone property rather than GMT. + String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); + if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null) + && (zoneName.length() > 0) + && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) { + // For NITZ string without time zone, + // need adjust time to reflect default time zone setting + zone = TimeZone.getDefault(); + long tzOffset; + tzOffset = zone.getOffset(System.currentTimeMillis()); + if (getAutoTime()) { + setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); + } else { + // Adjust the saved NITZ time to account for tzOffset. + mSavedTime = mSavedTime - tzOffset; + } + } else if (isoCountryCode.equals("")) { + // Country code not found. This is likely a test network. + // Get a TimeZone based only on the NITZ parameters (best guess). + zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); + } else { + zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode); + } + + mNeedFixZone = false; + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); } + } + + private void pollStateDone() { + if (DBG) log("Poll ServiceState done: oldSS=[" + ss + "] newSS=[" + newSS + "]"); boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE @@ -701,20 +884,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { && newSS.getState() != ServiceState.STATE_IN_SERVICE; boolean hasCdmaDataConnectionAttached = - (this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT - && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 - && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A) - && (this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT - || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 - || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A); + this.cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE + && this.newCdmaDataConnectionState == ServiceState.STATE_IN_SERVICE; boolean hasCdmaDataConnectionDetached = - (this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT - || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 - || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) - && (this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT - && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 - && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A); + this.cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE + && this.newCdmaDataConnectionState != ServiceState.STATE_IN_SERVICE; boolean hasCdmaDataConnectionChanged = cdmaDataConnectionState != newCdmaDataConnectionState; @@ -757,6 +932,20 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } if (hasChanged) { + if (cm.getRadioState().isNVReady()) { + String eriText; + // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text + if (ss.getState() == ServiceState.STATE_IN_SERVICE) { + eriText = phone.getCdmaEriText(); + } else { + // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for + // mRegistrationState 0,2,3 and 4 + eriText = phone.getContext().getText( + com.android.internal.R.string.roamingTextSearching).toString(); + } + ss.setCdmaEriText(eriText); + } + String operatorNumeric; phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA, @@ -768,9 +957,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { if (operatorNumeric == null) { phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); } else { - String iso = ""; + String isoCountryCode = ""; try{ - iso = MccTable.countryCodeForMcc(Integer.parseInt( + isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt( operatorNumeric.substring(0,3))); } catch ( NumberFormatException ex){ Log.w(LOG_TAG, "countryCodeForMcc error" + ex); @@ -778,14 +967,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { Log.w(LOG_TAG, "countryCodeForMcc error" + ex); } - phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, isoCountryCode); mGotCountryCode = true; + if (mNeedFixZone) { + fixTimeZone(isoCountryCode); + } } phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, ss.getRoaming() ? "true" : "false"); - phone.setSystemProperty(PROPERTY_OPERATOR_ISMANUAL, - ss.getIsManualSelection() ? "true" : "false"); updateSpnDisplay(); phone.notifyServiceStateChanged(ss); @@ -825,10 +1015,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { // Couldn't find a proper timezone. Perhaps the DST data is wrong. guess = findTimeZone(offset, !dst, when); } - if (DBG) { - Log.d(LOG_TAG, "getNitzTimeZone returning " - + (guess == null ? guess : guess.getID())); - } + if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID())); return guess; } @@ -852,6 +1039,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return guess; } + /** + * TODO: This code is exactly the same as in GsmServiceStateTracker + * and has a TODO to not poll signal strength if screen is off. + * This code should probably be hoisted to the base class so + * the fix, when added, works for both. + */ private void queueNextSignalStrengthPoll() { if (dontPollSignalStrength || (cm.getRadioState().isGsm())) { @@ -865,48 +1058,56 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { msg = obtainMessage(); msg.what = EVENT_POLL_SIGNAL_STRENGTH; - // TODO Done't poll signal strength if screen is off + // TODO Don't poll signal strength if screen is off sendMessageDelayed(msg, POLL_PERIOD_MILLIS); } /** - * send signal-strength-changed notification if rssi changed + * send signal-strength-changed notification if changed * Called both for solicited and unsolicited signal stength updates */ private void onSignalStrengthResult(AsyncResult ar) { - int oldRSSI = rssi; + SignalStrength oldSignalStrength = mSignalStrength; if (ar.exception != null) { - // 99 = unknown - // most likely radio is resetting/disconnected - rssi = 99; + // Most likely radio is resetting/disconnected change to default values. + setSignalStrengthDefaultValues(); } else { int[] ints = (int[])ar.result; - - // bug 658816 seems to be a case where the result is 0-length - if (ints.length != 0) { - rssi = ints[0]; - } else { - Log.e(LOG_TAG, "Bogus signal strength response"); - rssi = 99; + int offset = 2; + + int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -1; + int cdmaEcio = (ints[offset+1] > 0) ? -ints[offset+1] : -1; + + int evdoRssi = -1; + int evdoEcio = -1; + int evdoSnr = -1; + if ((networkType == ServiceState.RADIO_TECHNOLOGY_EVDO_0) + || (networkType == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) { + evdoRssi = (ints[offset+2] > 0) ? -ints[offset+2] : -1; + evdoEcio = (ints[offset+3] > 0) ? -ints[offset+3] : -1; + evdoSnr = ((ints[offset+4] > 0) && (ints[offset+4] <= 8)) ? ints[offset+4] : -1; } + + mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, + evdoRssi, evdoEcio, evdoSnr, false); } - if (rssi != oldRSSI) { + if (!mSignalStrength.equals(oldSignalStrength)) { try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after // POLL_PERIOD_MILLIS) during Radio Technology Change) phone.notifySignalStrength(); } catch (NullPointerException ex) { - log("onSignalStrengthResult() Phone already destroyed: " + ex - + "Signal Stranth not notified"); + log("onSignalStrengthResult() Phone already destroyed: " + ex + + "SignalStrength not notified"); } } } - private int radioTechnologyToServiceState(int code) { - int retVal = ServiceState.RADIO_TECHNOLOGY_UNKNOWN; + private int radioTechnologyToDataServiceState(int code) { + int retVal = ServiceState.STATE_OUT_OF_SERVICE; switch(code) { case 0: case 1: @@ -915,14 +1116,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case 4: case 5: break; - case 6: - retVal = ServiceState.RADIO_TECHNOLOGY_1xRTT; - break; - case 7: - retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_0; - break; - case 8: - retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_A; + case 6: // RADIO_TECHNOLOGY_1xRTT + case 7: // RADIO_TECHNOLOGY_EVDO_0 + case 8: // RADIO_TECHNOLOGY_EVDO_A + retVal = ServiceState.STATE_IN_SERVICE; break; default: Log.e(LOG_TAG, "Wrong radioTechnology code."); @@ -943,9 +1140,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { case 3: // 3 is "registration denied", fall through case 4: // 4 is "unknown" no vaild in current baseband return ServiceState.STATE_OUT_OF_SERVICE; - case 5:// fall through - case 6: - // Registered and: roaming (5) or roaming affiliates (6) + case 5:// 5 is "Registered, roaming" return ServiceState.STATE_IN_SERVICE; default: @@ -983,6 +1178,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) { String spn = SystemProperties.get(PROPERTY_ICC_OPERATOR_ALPHA, "empty"); + // NOTE: in case of RUIM we should completely ignore the ERI data file and + // mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS) String onsl = s.getOperatorAlphaLong(); String onss = s.getOperatorAlphaShort(); @@ -992,6 +1189,169 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return cdmaRoaming && !(equalsOnsl || equalsOnss); } + + /** + * nitzReceiveTime is time_t that the NITZ time was posted + */ + + private + void setTimeFromNITZString (String nitz, long nitzReceiveTime) + { + // "yy/mm/dd,hh:mm:ss(+/-)tz" + // tz is in number of quarter-hours + + long start = SystemClock.elapsedRealtime(); + Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + + " start=" + start + " delay=" + (start - nitzReceiveTime)); + + try { + /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone + * offset as well (which we won't worry about until later) */ + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + c.clear(); + c.set(Calendar.DST_OFFSET, 0); + + String[] nitzSubs = nitz.split("[/:,+-]"); + + int year = 2000 + Integer.parseInt(nitzSubs[0]); + c.set(Calendar.YEAR, year); + + // month is 0 based! + int month = Integer.parseInt(nitzSubs[1]) - 1; + c.set(Calendar.MONTH, month); + + int date = Integer.parseInt(nitzSubs[2]); + c.set(Calendar.DATE, date); + + int hour = Integer.parseInt(nitzSubs[3]); + c.set(Calendar.HOUR, hour); + + int minute = Integer.parseInt(nitzSubs[4]); + c.set(Calendar.MINUTE, minute); + + int second = Integer.parseInt(nitzSubs[5]); + c.set(Calendar.SECOND, second); + + boolean sign = (nitz.indexOf('-') == -1); + + int tzOffset = Integer.parseInt(nitzSubs[6]); + + int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) + : 0; + + // The zone offset received from NITZ is for current local time, + // so DST correction is already applied. Don't add it again. + // + // tzOffset += dst * 4; + // + // We could unapply it if we wanted the raw offset. + + tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; + + TimeZone zone = null; + + // As a special extension, the Android emulator appends the name of + // the host computer's timezone to the nitz string. this is zoneinfo + // timezone name of the form Area!Location or Area!Location!SubLocation + // so we need to convert the ! into / + if (nitzSubs.length >= 9) { + String tzname = nitzSubs[8].replace('!','/'); + zone = TimeZone.getTimeZone( tzname ); + } + + String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY); + + if (zone == null) { + + if (mGotCountryCode) { + if (iso != null && iso.length() > 0) { + zone = TimeUtils.getTimeZone(tzOffset, dst != 0, + c.getTimeInMillis(), + iso); + } else { + // We don't have a valid iso country code. This is + // most likely because we're on a test network that's + // using a bogus MCC (eg, "001"), so get a TimeZone + // based only on the NITZ parameters. + zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); + } + } + } + + if (zone == null) { + // We got the time before the country, so we don't know + // how to identify the DST rules yet. Save the information + // and hope to fix it up later. + + mNeedFixZone = true; + mZoneOffset = tzOffset; + mZoneDst = dst != 0; + mZoneTime = c.getTimeInMillis(); + } + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); + } + + String ignore = SystemProperties.get("gsm.ignore-nitz"); + if (ignore != null && ignore.equals("yes")) { + Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); + return; + } + + try { + mWakeLock.acquire(); + + if (getAutoTime()) { + long millisSinceNitzReceived + = SystemClock.elapsedRealtime() - nitzReceiveTime; + + if (millisSinceNitzReceived < 0) { + // Sanity check: something is wrong + Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " + + "backwards since NITZ time was received, " + + nitz); + return; + } + + if (millisSinceNitzReceived > Integer.MAX_VALUE) { + // If the time is this far off, something is wrong > 24 days! + Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " + + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) + + " days"); + return; + } + + // Note: with range checks above, cast to int is safe + c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); + + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + + " NITZ receive delay(ms): " + millisSinceNitzReceived + + " gained(ms): " + + (c.getTimeInMillis() - System.currentTimeMillis()) + + " from " + nitz); + + setAndBroadcastNetworkSetTime(c.getTimeInMillis()); + Log.i(LOG_TAG, "NITZ: after Setting time of day"); + } + SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); + saveNitzTime(c.getTimeInMillis()); + if (Config.LOGV) { + long end = SystemClock.elapsedRealtime(); + Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + } + } finally { + mWakeLock.release(); + } + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); + } + } + private boolean getAutoTime() { try { return Settings.System.getInt(phone.getContext().getContentResolver(), @@ -1001,6 +1361,58 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } } + private void saveNitzTimeZone(String zoneId) { + mSavedTimeZone = zoneId; + } + + private void saveNitzTime(long time) { + mSavedTime = time; + mSavedAtTime = SystemClock.elapsedRealtime(); + } + + /** + * Set the timezone and send out a sticky broadcast so the system can + * determine if the timezone was set by the carrier. + * + * @param zoneId timezone set by carrier + */ + private void setAndBroadcastNetworkSetTimeZone(String zoneId) { + AlarmManager alarm = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + alarm.setTimeZone(zoneId); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); + intent.putExtra("time-zone", zoneId); + phone.getContext().sendStickyBroadcast(intent); + } + + /** + * Set the time and Send out a sticky broadcast so the system can determine + * if the time was set by the carrier. + * + * @param time time set by network + */ + private void setAndBroadcastNetworkSetTime(long time) { + SystemClock.setCurrentTimeMillis(time); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); + intent.putExtra("time", time); + phone.getContext().sendStickyBroadcast(intent); + } + + private void revertToNitz() { + if (Settings.System.getInt(phone.getContext().getContentResolver(), + Settings.System.AUTO_TIME, 0) == 0) { + return; + } + Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone + + "' mSavedTime=" + mSavedTime + + " mSavedAtTime=" + mSavedAtTime); + if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { + setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); + setAndBroadcastNetworkSetTime(mSavedTime + + (SystemClock.elapsedRealtime() - mSavedAtTime)); + } + } + /** * @return true if phone is camping on a technology * that could support voice and data simultaneously. @@ -1017,4 +1429,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s); } + public String getMdnNumber() { + return mMdn; + } + + public String getCdmaMin() { + return mMin; + } + } diff --git a/telephony/java/com/android/internal/telephony/cdma/EriInfo.java b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java new file mode 100644 index 000000000000..5c8e23e81b4c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +public final class EriInfo { + + public static final int ROAMING_INDICATOR_ON = 0; + public static final int ROAMING_INDICATOR_OFF = 1; + public static final int ROAMING_INDICATOR_FLASH = 2; + + public static final int ROAMING_ICON_MODE_NORMAL = 0; + public static final int ROAMING_ICON_MODE_FLASH = 1; + + public int mRoamingIndicator; + public int mIconIndex; + public int mIconMode; + public String mEriText; + public int mCallPromptId; + public int mAlertId; + + public EriInfo (int roamingIndicator, int iconIndex, int iconMode, String eriText, + int callPromptId, int alertId) { + + this.mRoamingIndicator = roamingIndicator; + this.mIconIndex = iconIndex; + this.mIconMode = iconMode; + this.mEriText = eriText; + this.mCallPromptId = callPromptId; + this.mAlertId = alertId; + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/EriManager.java b/telephony/java/com/android/internal/telephony/cdma/EriManager.java new file mode 100644 index 000000000000..6c1384c1c601 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/EriManager.java @@ -0,0 +1,436 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.os.Message; +import android.util.Log; + +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneBase; + +import com.android.internal.util.XmlUtils; + +import java.util.HashMap; + +/** + * EriManager loads the ERI file definitions and manages the CDMA roaming information. + * + */ +public final class EriManager { + + class EriFile { + + public int mVersionNumber; // File version number + public int mNumberOfEriEntries; // Number of entries + public int mEriFileType; // Eri Phase 0/1 + //public int mNumberOfIconImages; // reserved for future use + //public int mIconImageType; // reserved for future use + public String[] mCallPromptId; // reserved for future use + public HashMap<Integer, EriInfo> mRoamIndTable; // Roaming Indicator Table + + public EriFile() { + this.mVersionNumber = -1; + this.mNumberOfEriEntries = 0; + this.mEriFileType = -1; + this.mCallPromptId = new String[] { "", "", "" }; + this.mRoamIndTable = new HashMap<Integer, EriInfo>(); + } + } + + class EriDisplayInformation { + public int mEriIconIndex; + public int mEriIconMode; + public String mEriIconText; + + public EriDisplayInformation(int eriIconIndex, int eriIconMode, String eriIconText) { + mEriIconIndex = eriIconIndex; + mEriIconMode = eriIconMode; + mEriIconText = eriIconText; + } + +// public void setParameters(int eriIconIndex, int eriIconMode, String eriIconText){ +// this.mEriIconIndex = eriIconIndex; +// this.mEriIconMode = eriIconMode; +// this.mEriIconText = eriIconText; +// } + + @Override + public String toString() { + return "EriDisplayInformation: {" + " IconIndex: " + mEriIconIndex + " EriIconMode: " + + mEriIconMode + " EriIconText: " + mEriIconText + " }"; + } + } + + static final String LOG_TAG = "CDMA"; + + public static final int ERI_FROM_XML = 0; + public static final int ERI_FROM_FILE_SYSTEM = 1; + public static final int ERI_FROM_MODEM = 2; + + private PhoneBase mPhone; + private Context mContext; + private int mEriFileSource = ERI_FROM_XML; + private boolean isEriFileLoaded; + private EriFile mEriFile; + + public EriManager(PhoneBase phone, Context context, int eriFileSource) { + this.mPhone = phone; + this.mContext = context; + this.mEriFileSource = eriFileSource; + this.mEriFile = new EriFile(); + } + + public void dispose() { + mEriFile = new EriFile(); + isEriFileLoaded = false; + } + + + public void loadEriFile() { + switch (mEriFileSource) { + case ERI_FROM_MODEM: + loadEriFileFromModem(); + break; + + case ERI_FROM_FILE_SYSTEM: + loadEriFileFromFileSystem(); + break; + + case ERI_FROM_XML: + default: + loadEriFileFromXml(); + break; + } + } + + /** + * Load the ERI file from the MODEM through chipset specific RIL_REQUEST_OEM_HOOK + * + * In this case the ERI file can be updated from the Phone Support Tool available + * from the Chipset vendor + */ + private void loadEriFileFromModem() { + // NOT IMPLEMENTED, Chipset vendor/Operator specific + } + + /** + * Load the ERI file from a File System file + * + * In this case the a Phone Support Tool to update the ERI file must be provided + * to the Operator + */ + private void loadEriFileFromFileSystem() { + // NOT IMPLEMENTED, Chipset vendor/Operator specific + } + + /** + * Load the ERI file from the application framework resources encoded in XML + * + */ + private void loadEriFileFromXml() { + Resources r = mContext.getResources(); + XmlResourceParser parser = r.getXml(com.android.internal.R.xml.eri); + try { + XmlUtils.beginDocument(parser, "EriFile"); + mEriFile.mVersionNumber = Integer.parseInt( + parser.getAttributeValue(null, "VersionNumber")); + mEriFile.mNumberOfEriEntries = Integer.parseInt( + parser.getAttributeValue(null, "NumberOfEriEntries")); + mEriFile.mEriFileType = Integer.parseInt( + parser.getAttributeValue(null, "EriFileType")); + + int parsedEriEntries = 0; + while(true) { + XmlUtils.nextElement(parser); + String name = parser.getName(); + if (name == null) { + if (parsedEriEntries != mEriFile.mNumberOfEriEntries) + Log.e(LOG_TAG, "Error Parsing ERI file: " + mEriFile.mNumberOfEriEntries + + " defined, " + parsedEriEntries + " parsed!"); + break; + } else if (name.equals("CallPromptId")) { + int id = Integer.parseInt(parser.getAttributeValue(null, "Id")); + String text = parser.getAttributeValue(null, "CallPromptText"); + if (id >= 0 && id <= 2) { + mEriFile.mCallPromptId[id] = text; + } else { + Log.e(LOG_TAG, "Error Parsing ERI file: found" + id + " CallPromptId"); + } + + } else if (name.equals("EriInfo")) { + int roamingIndicator = Integer.parseInt( + parser.getAttributeValue(null, "RoamingIndicator")); + int iconIndex = Integer.parseInt(parser.getAttributeValue(null, "IconIndex")); + int iconMode = Integer.parseInt(parser.getAttributeValue(null, "IconMode")); + String eriText = parser.getAttributeValue(null, "EriText"); + int callPromptId = Integer.parseInt( + parser.getAttributeValue(null, "CallPromptId")); + int alertId = Integer.parseInt(parser.getAttributeValue(null, "AlertId")); + parsedEriEntries++; + mEriFile.mRoamIndTable.put(roamingIndicator, new EriInfo (roamingIndicator, + iconIndex, iconMode, eriText, callPromptId, alertId)); + } + } + + isEriFileLoaded = true; + + } catch (Exception e) { + Log.e(LOG_TAG, "Got exception while loading ERI file.", e); + } finally { + parser.close(); + } + } + + /** + * Returns the version of the ERI file + * + */ + public int getEriFileVersion() { + return mEriFile.mVersionNumber; + } + + /** + * Returns the number of ERI entries parsed + * + */ + public int getEriNumberOfEntries() { + return mEriFile.mNumberOfEriEntries; + } + + /** + * Returns the ERI file type value ( 0 for Phase 0, 1 for Phase 1) + * + */ + public int getEriFileType() { + return mEriFile.mEriFileType; + } + + /** + * Returns if the ERI file has been loaded + * + */ + public boolean isEriFileLoaded() { + return isEriFileLoaded; + } + + /** + * Returns the EriInfo record associated with roamingIndicator + * or null if the entry is not found + */ + private EriInfo getEriInfo(int roamingIndicator) { + if (mEriFile.mRoamIndTable.containsKey(roamingIndicator)) { + return mEriFile.mRoamIndTable.get(roamingIndicator); + } else { + return null; + } + } + + private EriDisplayInformation getEriDisplayInformation(int roamInd, int defRoamInd){ + //int iconIndex = -1; + //int iconMode = -1; + //String iconText = "ERI text"; + EriDisplayInformation ret; + + switch (roamInd) { + // Handling the standard roaming indicator (non-ERI) + case EriInfo.ROAMING_INDICATOR_ON: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_ON, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText0).toString()); + break; + + case EriInfo.ROAMING_INDICATOR_OFF: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_OFF, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText1).toString()); + break; + + case EriInfo.ROAMING_INDICATOR_FLASH: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_FLASH, + EriInfo.ROAMING_ICON_MODE_FLASH, + mContext.getText(com.android.internal.R.string.roamingText2).toString()); + break; + + + // Handling the standard ERI + case 3: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText3).toString()); + break; + + case 4: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText4).toString()); + break; + + case 5: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText5).toString()); + break; + + case 6: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText6).toString()); + break; + + case 7: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText7).toString()); + break; + + case 8: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText8).toString()); + break; + + case 9: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText9).toString()); + break; + + case 10: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText10).toString()); + break; + + case 11: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText11).toString()); + break; + + case 12: + ret = new EriDisplayInformation( + roamInd, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal.R.string.roamingText12).toString()); + break; + + // Handling the non standard Enhanced Roaming Indicator (roamInd > 63) + default: + if (!isEriFileLoaded) { + // ERI file NOT loaded + Log.d(LOG_TAG, "ERI File not loaded"); + if(defRoamInd > 2) { + Log.d(LOG_TAG, "ERI defRoamInd > 2 ...flashing"); + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_FLASH, + EriInfo.ROAMING_ICON_MODE_FLASH, + mContext.getText(com.android.internal + .R.string.roamingText2).toString()); + } else { + Log.d(LOG_TAG, "ERI defRoamInd <= 2"); + switch (defRoamInd) { + case EriInfo.ROAMING_INDICATOR_ON: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_ON, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal + .R.string.roamingText0).toString()); + break; + + case EriInfo.ROAMING_INDICATOR_OFF: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_OFF, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal + .R.string.roamingText1).toString()); + break; + + case EriInfo.ROAMING_INDICATOR_FLASH: + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_FLASH, + EriInfo.ROAMING_ICON_MODE_FLASH, + mContext.getText(com.android.internal + .R.string.roamingText2).toString()); + break; + + default: + ret = new EriDisplayInformation(-1, -1, "ERI text"); + } + } + } else { + // ERI file loaded + Log.d(LOG_TAG, "ERI File loaded"); + EriInfo eriInfo = getEriInfo(roamInd); + EriInfo defEriInfo = getEriInfo(defRoamInd); + if (eriInfo == null) { + Log.d(LOG_TAG, "ERI roamInd " + roamInd + + " not found in ERI file ...using defRoamInd " + defRoamInd); + if(defEriInfo == null) { + Log.e(LOG_TAG, "ERI defRoamInd " + defRoamInd + + " not found in ERI file ...on"); + ret = new EriDisplayInformation( + EriInfo.ROAMING_INDICATOR_ON, + EriInfo.ROAMING_ICON_MODE_NORMAL, + mContext.getText(com.android.internal + .R.string.roamingText0).toString()); + + } else { + Log.d(LOG_TAG, "ERI defRoamInd " + defRoamInd + " found in ERI file"); + ret = new EriDisplayInformation( + defEriInfo.mIconIndex, + defEriInfo.mIconMode, + defEriInfo.mEriText); + } + } else { + Log.d(LOG_TAG, "ERI roamInd " + roamInd + " found in ERI file"); + ret = new EriDisplayInformation( + eriInfo.mIconIndex, + eriInfo.mIconMode, + eriInfo.mEriText); + } + } + break; + } + Log.d(LOG_TAG, "Displaying ERI " + ret.toString()); + return ret; + } + + public int getCdmaEriIconIndex(int roamInd, int defRoamInd){ + return getEriDisplayInformation(roamInd, defRoamInd).mEriIconIndex; + } + + public int getCdmaEriIconMode(int roamInd, int defRoamInd){ + return getEriDisplayInformation(roamInd, defRoamInd).mEriIconMode; + } + + public String getCdmaEriText(int roamInd, int defRoamInd){ + return getEriDisplayInformation(roamInd, defRoamInd).mEriIconText; + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java index c226b62eef43..23a4ac7d068a 100644 --- a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java +++ b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java @@ -84,7 +84,6 @@ public final class FeatureCode extends Handler implements MmiCode { CDMAPhone phone; Context context; - String action; // '*' in CDMA String sc; // Service Code String poundString; // Entire Flash string @@ -237,10 +236,9 @@ public final class FeatureCode extends Handler implements MmiCode { } /** Process a Flash Code...anything that isn't a dialing number */ - void processCode () { + void processCode() { Log.d(LOG_TAG, "send feature code..."); - phone.mCM.sendCDMAFeatureCode(this.poundString, - obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); + phone.mCM.sendCDMAFeatureCode(this.poundString, obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); } /** Called from CDMAPhone.handleMessage; not a Handler subclass */ diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java index 9de6c42f45d7..3e2a29be932b 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java @@ -68,7 +68,12 @@ public final class RuimFileHandler extends IccFileHandler { } protected String getEFPath(int efid) { - // TODO(): Implement for CDMA EFs. + switch(efid) { + case EF_SMS: + case EF_CST: + case EF_RUIM_SPN: + return MF_SIM + DF_CDMA; + } return getCommonIccEFPath(efid); } diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java index b18a3f18ffa9..c7e61da3f8d7 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java @@ -16,19 +16,17 @@ package com.android.internal.telephony.cdma; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.Registrant; import android.util.Log; -import static com.android.internal.telephony.TelephonyProperties.*; import com.android.internal.telephony.AdnRecord; import com.android.internal.telephony.AdnRecordCache; import com.android.internal.telephony.AdnRecordLoader; import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.cdma.RuimCard; import com.android.internal.telephony.gsm.MccTable; @@ -39,6 +37,10 @@ import com.android.internal.telephony.IccRecords; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyIntents; +import android.app.ActivityManagerNative; +import android.content.Intent; + /** * {@hide} @@ -47,15 +49,17 @@ public final class RuimRecords extends IccRecords { static final String LOG_TAG = "CDMA"; private static final boolean DBG = true; + private boolean m_ota_commited=false; //***** Instance Variables - String imsi_m; - String mdn = null; // My mobile number - String h_sid; - String h_nid; - String min2_min1; // 10 digit MIN value MIN2+MIN1 NEWRIL:TODO currently unused - // is not initialized + private String mImsi; + private String mMyMobileNumber; + private String mSid; + private String mNid; + private String mMin2Min1; + + private String mPrlVersion; //***** Event Constants @@ -63,6 +67,7 @@ public final class RuimRecords extends IccRecords { private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2; private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4; private static final int EVENT_GET_ICCID_DONE = 5; + private static final int EVENT_NV_READY = 9; private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10; private static final int EVENT_UPDATE_DONE = 14; private static final int EVENT_GET_SST_DONE = 17; @@ -73,8 +78,7 @@ public final class RuimRecords extends IccRecords { private static final int EVENT_GET_SMS_DONE = 22; private static final int EVENT_RUIM_REFRESH = 31; - - //***** Constructor + private static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 32; RuimRecords(CDMAPhone p) { super(p); @@ -88,12 +92,14 @@ public final class RuimRecords extends IccRecords { p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null); + p.mCM.registerForNVReady(this, EVENT_NV_READY, null); p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); // NOTE the EVENT_SMS_ON_RUIM is not registered p.mCM.setOnIccRefresh(this, EVENT_RUIM_REFRESH, null); // Start off by setting empty state onRadioOffOrNotAvailable(); + p.mCM.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null); } @@ -102,12 +108,16 @@ public final class RuimRecords extends IccRecords { phone.mCM.unregisterForRUIMReady(this); phone.mCM.unregisterForOffOrNotAvailable( this); phone.mCM.unSetOnIccRefresh(this); + phone.mCM.unregisterForNVReady(this); + phone.mCM.unregisterForCdmaOtaProvision(this); } + @Override protected void finalize() { if(DBG) Log.d(LOG_TAG, "RuimRecords finalized"); } + @Override protected void onRadioOffOrNotAvailable() { countVoiceMessages = 0; mncLength = 0; @@ -115,8 +125,8 @@ public final class RuimRecords extends IccRecords { adnCache.reset(); - phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null); - phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null); + phone.setSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, null); + phone.setSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null); // recordsRequested is set to false indicating that the SIM // read requests made so far are not valid. This is set to @@ -124,17 +134,26 @@ public final class RuimRecords extends IccRecords { recordsRequested = false; } - //***** Public Methods - /** Returns null if RUIM is not yet ready */ public String getIMSI_M() { - return imsi_m; + // TODO(Moto): mImsi is not initialized, fix. + return mImsi; } public String getMdnNumber() { - return mdn; + return mMyMobileNumber; + } + + public String getCdmaMin() { + return mMin2Min1; } + /** Returns null if RUIM is not yet ready */ + public String getPrlVersion() { + return mPrlVersion; + } + + @Override public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){ // In CDMA this is Operator/OEM dependent AsyncResult.forMessage((onComplete)).exception = @@ -148,6 +167,7 @@ public final class RuimRecords extends IccRecords { * @param fileChanged indicates whether any files changed * @param fileList if non-null, a list of EF files that changed */ + @Override public void onRefresh(boolean fileChanged, int[] fileList) { if (fileChanged) { // A future optimization would be to inspect fileList and @@ -157,32 +177,31 @@ public final class RuimRecords extends IccRecords { } } - /** Returns the 5 or 6 digit MCC/MNC of the operator that + /** + * Returns the 5 or 6 digit MCC/MNC of the operator that * provided the RUIM card. Returns null of RUIM is not yet ready */ - String getRUIMOperatorNumeric() { - if (imsi_m == null) { + public String getRUIMOperatorNumeric() { + if (mImsi == null) { return null; } + // TODO(Moto): mncLength is not set anywhere. if (mncLength != 0) { // Length = length of MCC + length of MNC // TODO: change spec name // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3) - return imsi_m.substring(0, 3 + mncLength); + return mImsi.substring(0, 3 + mncLength); } // Guess the MNC length based on the MCC if we don't // have a valid value in ef[ad] - int mcc; - - mcc = Integer.parseInt(imsi_m.substring(0,3)); - - return imsi_m.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); + int mcc = Integer.parseInt(mImsi.substring(0,3)); + return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); } - //***** Overridden from Handler + @Override public void handleMessage(Message msg) { AsyncResult ar; @@ -194,7 +213,9 @@ public final class RuimRecords extends IccRecords { case EVENT_RUIM_READY: onRuimReady(); break; - + case EVENT_NV_READY: + onNvReady(); + break; case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: onRadioOffOrNotAvailable(); break; @@ -211,15 +232,22 @@ public final class RuimRecords extends IccRecords { if (ar.exception != null) { break; } - - mdn = localTemp[0]; - h_sid = localTemp[1]; - h_nid = localTemp[2]; - if (localTemp.length >= 3) { // NEWRIL:TODO remove when new ril always returns min2_min1 - min2_min1 = localTemp[3]; + if (m_ota_commited) { + if (mMyMobileNumber != localTemp[0]) { + Intent intent = new Intent(TelephonyIntents.ACTION_CDMA_OTA_MDN_CHANGED); + intent.putExtra("mdn", localTemp[0]); + Log.d(LOG_TAG,"Broadcasting intent MDN Change in OTA "); + ActivityManagerNative.broadcastStickyIntent(intent, null); + } + m_ota_commited = false; } + mMyMobileNumber = localTemp[0]; + mSid = localTemp[1]; + mNid = localTemp[2]; + mMin2Min1 = localTemp[3]; + mPrlVersion = localTemp[4]; - Log.d(LOG_TAG, "MDN: " + mdn); + Log.d(LOG_TAG, "MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); break; @@ -266,6 +294,21 @@ public final class RuimRecords extends IccRecords { } break; + case EVENT_OTA_PROVISION_STATUS_CHANGE: + m_ota_commited=false; + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + int[] ints = (int[]) ar.result; + int otaStatus = ints[0]; + if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_COMMITTED) { + m_ota_commited=true; + phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); + } else if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) { + phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); + } + } + break; + }}catch (RuntimeException exc) { // I don't want these exceptions to be fatal Log.w(LOG_TAG, "Exception parsing RUIM record", exc); @@ -277,6 +320,7 @@ public final class RuimRecords extends IccRecords { } } + @Override protected void onRecordLoaded() { // One record loaded successfully or failed, In either case // we need to update the recordsToLoad count @@ -290,6 +334,7 @@ public final class RuimRecords extends IccRecords { } } + @Override protected void onAllRecordsLoaded() { Log.d(LOG_TAG, "RuimRecords: record load complete"); @@ -301,9 +346,6 @@ public final class RuimRecords extends IccRecords { RuimCard.INTENT_VALUE_ICC_LOADED, null); } - - //***** Private Methods - private void onRuimReady() { /* broadcast intent ICC_READY here so that we can make sure READY is sent before IMSI ready @@ -318,6 +360,11 @@ public final class RuimRecords extends IccRecords { } + private void onNvReady() { + phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); + + } + private void fetchRuimRecords() { recordsRequested = true; @@ -364,17 +411,17 @@ public final class RuimRecords extends IccRecords { switch ((result[0])) { case CommandsInterface.SIM_REFRESH_FILE_UPDATED: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); adnCache.reset(); fetchRuimRecords(); break; case CommandsInterface.SIM_REFRESH_INIT: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); // need to reload all files (that we care about) fetchRuimRecords(); break; case CommandsInterface.SIM_REFRESH_RESET: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); phone.mCM.setRadioPower(false, null); /* Note: no need to call setRadioPower(true). Assuming the desired * radio power state is still ON (as tracked by ServiceStateTracker), @@ -386,14 +433,14 @@ public final class RuimRecords extends IccRecords { break; default: // unknown refresh operation - if (DBG) log("handleRuimRefresh with unknown operation"); + if (DBG) log("handleRuimRefresh with unknown operation"); break; } } + @Override protected void log(String s) { Log.d(LOG_TAG, "[RuimRecords] " + s); } } - diff --git a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java new file mode 100644 index 000000000000..44958e950a0d --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java @@ -0,0 +1,280 @@ +/* + * 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.telephony.cdma; + +import java.util.HashMap; +import java.util.HashSet; +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; + static public final int IS95_CONST_IR_SIGNAL_IS54B = 2; + static public final int IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT = 4; + + // public final int int IS95_CONST_IR_ALERT_PITCH_TYPE; + static public final int IS95_CONST_IR_ALERT_MED = 0; + static public final int IS95_CONST_IR_ALERT_HIGH = 1; + static public final int IS95_CONST_IR_ALERT_LOW = 2; + static public final int TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN = 255; + + // public final int int IS95_CONST_IR_SIGNAL_TYPE; + static public final int IS95_CONST_IR_SIG_ISDN_NORMAL = 0; + static public final int IS95_CONST_IR_SIG_ISDN_INTGRP = 1; + static public final int IS95_CONST_IR_SIG_ISDN_SP_PRI = 2; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_3 = 3; + static public final int IS95_CONST_IR_SIG_ISDN_PING = 4; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_5 = 5; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_6 = 6; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_7 = 7; + static public final int IS95_CONST_IR_SIG_ISDN_OFF = 15; + static public final int IS95_CONST_IR_SIG_TONE_DIAL = 0; + static public final int IS95_CONST_IR_SIG_TONE_RING = 1; + static public final int IS95_CONST_IR_SIG_TONE_INT = 2; + static public final int IS95_CONST_IR_SIG_TONE_ABB_INT = 3; + static public final int IS95_CONST_IR_SIG_TONE_REORDER = 4; + static public final int IS95_CONST_IR_SIG_TONE_ABB_RE = 5; + static public final int IS95_CONST_IR_SIG_TONE_BUSY = 6; + static public final int IS95_CONST_IR_SIG_TONE_CONFIRM = 7; + static public final int IS95_CONST_IR_SIG_TONE_ANSWER = 8; + static public final int IS95_CONST_IR_SIG_TONE_CALL_W = 9; + static public final int IS95_CONST_IR_SIG_TONE_PIP = 10; + static public final int IS95_CONST_IR_SIG_TONE_NO_TONE = 63; + static public final int IS95_CONST_IR_SIG_IS54B_NO_TONE = 0; + static public final int IS95_CONST_IR_SIG_IS54B_L = 1; + static public final int IS95_CONST_IR_SIG_IS54B_SS = 2; + static public final int IS95_CONST_IR_SIG_IS54B_SSL = 3; + static public final int IS95_CONST_IR_SIG_IS54B_SS_2 = 4; + static public final int IS95_CONST_IR_SIG_IS54B_SLS = 5; + static public final int IS95_CONST_IR_SIG_IS54B_S_X4 = 6; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_L = 7; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SS = 8; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SSL = 9; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SLS = 10; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_S_X4 = 11; + static public final int IS95_CONST_IR_SIG_TONE_ABBR_ALRT = 0; + + // Hashmap to map signalInfo To AudioTone + static private HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>(); + + 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(CDMA_INVALID_TONE); + } + return new Integer(signalType * 256 * 256 + alertPitch * 256 + signal); + } + + public static int getAudioToneFromSignalInfo(int signalType, int alertPitch, int signal) { + Integer result = hm.get(signalParamHash(signalType, alertPitch, signal)); + if (result == null) { + return CDMA_INVALID_TONE; + } + return result; + } + + static { + + /* SIGNAL_TYPE_ISDN */ + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_NORMAL), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_INTGRP), + 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_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_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); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_6), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT6); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_7), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT7); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_OFF), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + /* SIGNAL_TYPE_TONE */ + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_DIAL), ToneGenerator.TONE_CDMA_DIAL_TONE_LITE); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_RING), ToneGenerator.TONE_CDMA_NETWORK_USA_RINGBACK); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_INT), ToneGenerator.TONE_SUP_INTERCEPT); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_INT), ToneGenerator.TONE_SUP_INTERCEPT_ABBREV); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_REORDER), ToneGenerator.TONE_CDMA_REORDER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_RE), ToneGenerator.TONE_CDMA_ABBR_REORDER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_BUSY), ToneGenerator.TONE_CDMA_NETWORK_BUSY); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_CONFIRM), ToneGenerator.TONE_SUP_CONFIRM); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ANSWER), ToneGenerator.TONE_CDMA_ANSWER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_RE), ToneGenerator.TONE_CDMA_NETWORK_CALLWAITING); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_PIP), ToneGenerator.TONE_CDMA_PIP); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_NO_TONE), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + /* SIGNAL_TYPE_IS54B */ + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + 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_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); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_HIGH_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_MED_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_LOW_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_HIGH_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_MED_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_LOW_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_HIGH_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_MED_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_LOW_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_HIGH_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_MED_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_LOW_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_HIGH_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_MED_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_LOW_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_HIGH_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_MED_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_LOW_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_HIGH_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_MED_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_LOW_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_HIGH_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_MED_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_LOW_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_HIGH_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_MED_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_LOW_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_HIGH_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_MED_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_LOW_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_IS54B_NO_TONE), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT, + TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_TONE_ABBR_ALRT), + ToneGenerator.TONE_CDMA_ABBR_ALERT); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT, + TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_TONE_NO_TONE), + ToneGenerator.TONE_CDMA_ABBR_ALERT); + + } + + // suppress default constructor for noninstantiability + private SignalToneUtil() { + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 343a22e54010..3a9206454e3d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -277,6 +277,15 @@ public class SmsMessage extends SmsMessageBase { } /** + * TODO(cleanup): why do getSubmitPdu methods take an scAddr input + * and do nothing with it? GSM allows us to specify a SC (eg, + * when responding to an SMS that explicitly requests the response + * is sent to a specific SC), or pass null to use the default + * value. Is there no similar notion in CDMA? Or do we just not + * have it hooked up? + */ + + /** * Get an SMS-SUBMIT PDU for a destination address and a message * * @param scAddr Service Centre address. Null means use default. @@ -290,88 +299,53 @@ public class SmsMessage extends SmsMessageBase { * Returns null on encode error. * @hide */ - public static SubmitPdu getSubmitPdu(String scAddr, - String destAddr, String message, - boolean statusReportRequested, byte[] headerData) { + public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message, + boolean statusReportRequested, SmsHeader smsHeader) { + /** - * TODO(cleanup): why does this method take an scAddr input - * and do nothing with it? GSM allows us to specify a SC (eg, - * when responding to an SMS that explicitly requests the - * response is sent to a specific SC), or pass null to use the - * default value. Is there no similar notion in CDMA? Or do - * we just not have it hooked up? + * TODO(cleanup): Do we really want silent failure like this? + * Would it not be much more reasonable to make sure we don't + * call this function if we really want nothing done? */ - if (message == null || destAddr == null) { return null; } UserData uData = new UserData(); uData.payloadStr = message; - if(headerData != null) { - /** - * TODO(cleanup): we force the outside to deal with _all_ - * of the raw details of properly constructing serialized - * headers, unserialze here, and then promptly reserialze - * during encoding -- rather undesirable. - */ - uData.userDataHeader = SmsHeader.parse(headerData); - } - - return privateGetSubmitPdu(destAddr, statusReportRequested, uData, (headerData == null)); - } - - - /** - * Get an SMS-SUBMIT PDU for a destination address and a message - * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destinationAddress, String message, - boolean statusReportRequested) { - return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); + uData.userDataHeader = smsHeader; + return privateGetSubmitPdu(destAddr, statusReportRequested, uData); } /** * Get an SMS-SUBMIT PDU for a data message to a destination address & port * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the + * @param scAddr Service Centre address. null == use default + * @param destAddr the address of the destination for the message + * @param destPort the port to deliver the message to at the * destination * @param data the data for the message * @return a <code>SubmitPdu</code> containing the encoded SC * address, if applicable, and the encoded message. * Returns null on encode error. */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destAddr, short destinationPort, byte[] data, - boolean statusReportRequested) { + public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, short destPort, + byte[] data, boolean statusReportRequested) { /** - * TODO(cleanup): if we had properly exposed SmsHeader - * information, this mess of many getSubmitPdu public - * interface methods that currently pollute the api could have - * been much more cleanly collapsed into one. + * TODO(cleanup): this is not a general-purpose SMS creation + * method, but rather something specialized to messages + * containing OCTET encoded (meaning non-human-readable) user + * data. The name should reflect that, and not just overload. */ - /** - * TODO(cleanup): header serialization should be put somewhere - * canonical to allow proper debugging and reuse. - */ - byte[] destPort = new byte[4]; - destPort[0] = (byte) ((destinationPort >> 8) & 0xFF); // MSB of destination port - destPort[1] = (byte) (destinationPort & 0xFF); // LSB of destination port - destPort[2] = 0x00; // MSB of originating port - destPort[3] = 0x00; // LSB of originating port + SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs(); + portAddrs.destPort = destPort; + portAddrs.origPort = 0; + portAddrs.areEightBits = false; + SmsHeader smsHeader = new SmsHeader(); - smsHeader.add( - new SmsHeader.Element(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT, destPort)); - smsHeader.nbrOfHeaders = smsHeader.getElements().size(); + smsHeader.portAddrs = portAddrs; UserData uData = new UserData(); uData.userDataHeader = smsHeader; @@ -379,40 +353,22 @@ public class SmsMessage extends SmsMessageBase { uData.msgEncodingSet = true; uData.payload = data; - return privateGetSubmitPdu(destAddr, statusReportRequested, uData, true); + return privateGetSubmitPdu(destAddr, statusReportRequested, uData); } - static class PduParser { - - PduParser() { - } - - /** - * Parses an SC timestamp and returns a currentTimeMillis()-style - * timestamp - */ - static long getSCTimestampMillis(byte[] timestamp) { - // TP-Service-Centre-Time-Stamp - int year = IccUtils.beBcdByteToInt(timestamp[0]); - int month = IccUtils.beBcdByteToInt(timestamp[1]); - int day = IccUtils.beBcdByteToInt(timestamp[2]); - int hour = IccUtils.beBcdByteToInt(timestamp[3]); - int minute = IccUtils.beBcdByteToInt(timestamp[4]); - int second = IccUtils.beBcdByteToInt(timestamp[5]); - - Time time = new Time(Time.TIMEZONE_UTC); - - // C.S0015-B v2.0, 4.5.4: range is 1996-2095 - time.year = year >= 96 ? year + 1900 : year + 2000; - time.month = month - 1; - time.monthDay = day; - time.hour = hour; - time.minute = minute; - time.second = second; - - return time.toMillis(true); - } - + /** + * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * + * @param destAddr the address of the destination for the message + * @param userDara the data for the message + * @param statusReportRequested Indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC + * address, if applicable, and the encoded message. + * Returns null on encode error. + */ + public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, + boolean statusReportRequested) { + return privateGetSubmitPdu(destAddr, statusReportRequested, userData); } /** @@ -445,31 +401,23 @@ public class SmsMessage extends SmsMessageBase { * {@inheritDoc} */ public boolean isMWIClearMessage() { - if ((mBearerData != null) && (0 == mBearerData.numberOfMessages)) { - return true; - } - return false; + return ((mBearerData != null) && (mBearerData.numberOfMessages == 0)); } /** * {@inheritDoc} */ public boolean isMWISetMessage() { - if ((mBearerData != null) && (mBearerData.numberOfMessages >0)) { - return true; - } - return false; + return ((mBearerData != null) && (mBearerData.numberOfMessages > 0)); } /** * {@inheritDoc} */ public boolean isMwiDontStore() { - if ((mBearerData != null) && (mBearerData.numberOfMessages >0) - && (null == mBearerData.userData)) { - return true; - } - return false; + return ((mBearerData != null) && + (mBearerData.numberOfMessages > 0) && + (mBearerData.userData == null)); } /** @@ -478,7 +426,7 @@ public class SmsMessage extends SmsMessageBase { * shifted to the bits 31-16. */ public int getStatus() { - return(status<<16); + return (status << 16); } /** @@ -498,6 +446,18 @@ public class SmsMessage extends SmsMessageBase { } /** + * Calculate the number of septets needed to encode the message. + * + * @param messageBody the message to encode + * @param use7bitOnly ignore (but still count) illegal characters if true + * @return TextEncodingDetails + */ + public static TextEncodingDetails calculateLength(CharSequence messageBody, + boolean use7bitOnly) { + return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly); + } + + /** * Returns the teleservice type of the message. * @return the teleservice: * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET}, @@ -518,7 +478,7 @@ public class SmsMessage extends SmsMessageBase { */ private void parsePdu(byte[] pdu) { ByteArrayInputStream bais = new ByteArrayInputStream(pdu); - DataInputStream dis = new DataInputStream(new BufferedInputStream(bais)); + DataInputStream dis = new DataInputStream(bais); byte length; int bearerDataLength; SmsEnvelope env = new SmsEnvelope(); @@ -568,90 +528,42 @@ public class SmsMessage extends SmsMessageBase { protected void parseSms() { mBearerData = BearerData.decode(mEnvelope.bearerData); messageRef = mBearerData.messageId; + if (mBearerData.userData != null) { + userData = mBearerData.userData.payload; + userDataHeader = mBearerData.userData.userDataHeader; + messageBody = mBearerData.userData.payloadStr; + } - // TP-Message-Type-Indicator - // (See 3GPP2 C.S0015-B, v2, 4.5.1) - int messageType = mBearerData.messageType; - - switch (messageType) { + // TP-Message-Type-Indicator (See 3GPP2 C.S0015-B, v2, 4.5.1) + switch (mBearerData.messageType) { case BearerData.MESSAGE_TYPE_USER_ACK: case BearerData.MESSAGE_TYPE_READ_ACK: case BearerData.MESSAGE_TYPE_DELIVER: - // Deliver (mobile-terminated only) - parseSmsDeliver(); - break; case BearerData.MESSAGE_TYPE_DELIVERY_ACK: - parseSmsDeliveryAck(); break; - default: - // the rest of these - throw new RuntimeException("Unsupported message type: " + messageType); + throw new RuntimeException("Unsupported message type: " + mBearerData.messageType); } - } - /** - * TODO(cleanup): why are there two nearly identical functions - * below? More rubbish... - */ - - /** - * Parses a SMS-DELIVER message. (mobile-terminated only) - * See 3GPP2 C.S0015-B, v2, 4.4.1 - */ - private void parseSmsDeliver() { if (originatingAddress != null) { originatingAddress.address = new String(originatingAddress.origBytes); if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: " + originatingAddress.address); } - if (mBearerData.timeStamp != null) { - scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp); + if (mBearerData.msgCenterTimeStamp != null) { + scTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true); } if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis); - parseUserData(mBearerData.userData); - } - - /** - * Parses a SMS-DELIVER message. (mobile-terminated only) - * See 3GPP2 C.S0015-B, v2, 4.4.1 - */ - private void parseSmsDeliveryAck() { - if (originatingAddress != null) { - originatingAddress.address = new String(originatingAddress.origBytes); - if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: " - + originatingAddress.address); - } - - if (mBearerData.timeStamp != null) { - scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp); - } - - if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis); - - if (mBearerData.errorClass != BearerData.ERROR_UNDEFINED) { + // TODO(Teleca): do we really want this test to occur only for DELIVERY_ACKs? + if ((mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) && + (mBearerData.errorClass != BearerData.ERROR_UNDEFINED)) { status = mBearerData.errorClass << 8; status |= mBearerData.messageStatus; } - parseUserData(mBearerData.userData); - } - - /** - * Copy parsed user data out from internal datastructures. - */ - private void parseUserData(UserData uData) { - if (uData == null) { - return; - } - - userData = uData.payload; - userDataHeader = uData.userDataHeader; - messageBody = uData.payloadStr; - if (messageBody != null) { if (Config.LOGV) Log.v(LOG_TAG, "SMS message body: '" + messageBody + "'"); parseMessageBody(); @@ -708,7 +620,7 @@ public class SmsMessage extends SmsMessageBase { * @return byte stream for SubmitPdu. */ private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested, - UserData userData, boolean useNewId) { + UserData userData) { /** * TODO(cleanup): give this function a more meaningful name. @@ -720,7 +632,7 @@ public class SmsMessage extends SmsMessageBase { BearerData bearerData = new BearerData(); bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT; - if (useNewId) setNextMessageId(); + if (userData != null) setNextMessageId(); bearerData.messageId = nextMessageId; bearerData.deliveryAckReq = statusReportRequested; @@ -731,12 +643,15 @@ public class SmsMessage extends SmsMessageBase { bearerData.userData = userData; bearerData.hasUserDataHeader = (userData.userDataHeader != null); + int teleservice = bearerData.hasUserDataHeader ? + SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; + byte[] encodedBearerData = BearerData.encode(bearerData); if (encodedBearerData == null) return null; SmsEnvelope envelope = new SmsEnvelope(); envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; - envelope.teleService = SmsEnvelope.TELESERVICE_WMT; + envelope.teleService = teleservice; envelope.destAddress = destAddr; envelope.bearerReply = RETURN_ACK; envelope.bearerData = encodedBearerData; @@ -812,6 +727,15 @@ public class SmsMessage extends SmsMessageBase { dos.write(env.bearerData, 0, env.bearerData.length); dos.close(); + /** + * TODO(cleanup) -- This is the only place where mPdu is + * defined, and this is not obviously the only place where + * it needs to be defined. It would be much nicer if + * accessing the serialized representation used a less + * fragile mechanism. Maybe the getPdu method could + * generate a representation if there was not yet one? + */ + mPdu = baos.toByteArray(); } catch (IOException ex) { Log.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex); @@ -849,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/cdma/package.html b/telephony/java/com/android/internal/telephony/cdma/package.html index cf1ad4a046bf..4eb1f9c0fd9e 100644 --- a/telephony/java/com/android/internal/telephony/cdma/package.html +++ b/telephony/java/com/android/internal/telephony/cdma/package.html @@ -1,6 +1,6 @@ <HTML> <BODY> -Provides classes to control or read data from CDMA phones. +Provides classes to control or read data from CDMA phones. @hide </BODY> </HTML> diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index e64d0224253b..ef3afff7082e 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -17,12 +17,17 @@ package com.android.internal.telephony.cdma.sms; import android.util.Log; +import android.util.SparseIntArray; import android.telephony.SmsMessage; +import android.text.format.Time; + +import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.cdma.sms.UserData; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import com.android.internal.util.HexDump; import com.android.internal.util.BitwiseInputStream; @@ -32,21 +37,22 @@ import com.android.internal.util.BitwiseOutputStream; /** * An object to encode and decode CDMA SMS bearer data. */ -public final class BearerData{ +public final class BearerData { private final static String LOG_TAG = "SMS"; /** * Bearer Data Subparameter Indentifiers * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1) + * NOTE: Commented subparameter types are not implemented. */ private final static byte SUBPARAM_MESSAGE_IDENTIFIER = 0x00; private final static byte SUBPARAM_USER_DATA = 0x01; private final static byte SUBPARAM_USER_REPONSE_CODE = 0x02; private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP = 0x03; - //private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE = 0x04; - //private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE = 0x05; - //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06; - //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07; + private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE = 0x04; + private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE = 0x05; + private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06; + private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07; private final static byte SUBPARAM_PRIORITY_INDICATOR = 0x08; private final static byte SUBPARAM_PRIVACY_INDICATOR = 0x09; private final static byte SUBPARAM_REPLY_OPTION = 0x0A; @@ -56,7 +62,7 @@ public final class BearerData{ private final static byte SUBPARAM_CALLBACK_NUMBER = 0x0E; private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE = 0x0F; //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA = 0x10; - //private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11; + private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11; //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12; //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13; private final static byte SUBPARAM_MESSAGE_STATUS = 0x14; @@ -77,7 +83,7 @@ public final class BearerData{ public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07; public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08; - public byte messageType; + public int messageType; /** * 16-bit value indicating the message ID, which increments modulo 65536. @@ -96,7 +102,7 @@ public final class BearerData{ public static final int PRIORITY_EMERGENCY = 0x3; public boolean priorityIndicatorSet = false; - public byte priority = PRIORITY_NORMAL; + public int priority = PRIORITY_NORMAL; /** * Supported privacy modes for CDMA SMS messages @@ -108,7 +114,7 @@ public final class BearerData{ public static final int PRIVACY_SECRET = 0x3; public boolean privacyIndicatorSet = false; - public byte privacy = PRIVACY_NOT_RESTRICTED; + public int privacy = PRIVACY_NOT_RESTRICTED; /** * Supported alert priority modes for CDMA SMS messages @@ -133,7 +139,7 @@ public final class BearerData{ public static final int DISPLAY_MODE_USER = 0x2; public boolean displayModeSet = false; - public byte displayMode = DISPLAY_MODE_DEFAULT; + public int displayMode = DISPLAY_MODE_DEFAULT; /** * Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B, @@ -189,8 +195,12 @@ public final class BearerData{ public int messageStatus = STATUS_UNDEFINED; /** - * 1-bit value that indicates whether a User Data Header is present. + * 1-bit value that indicates whether a User Data Header (UDH) is present. * (See 3GPP2 C.S0015-B, v2, 4.5.1) + * + * NOTE: during encoding, this value will be set based on the + * presence of a UDH in the structured data, any existing setting + * will be overwritten. */ public boolean hasUserDataHeader; @@ -201,23 +211,94 @@ public final class BearerData{ */ public UserData userData; - //public UserResponseCode userResponseCode; + /** + * The User Response Code subparameter is used in the SMS User + * Acknowledgment Message to respond to previously received short + * messages. This message center-specific element carries the + * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2, + * 4.5.3) + */ + public boolean userResponseCodeSet = false; + public int userResponseCode; /** * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4 - * year, month, day, hours, minutes, seconds; */ - public byte[] timeStamp; + public static class TimeStamp extends Time { - //public SmsTime validityPeriodAbsolute; - //public SmsRelTime validityPeriodRelative; - //public SmsTime deferredDeliveryTimeAbsolute; - //public SmsRelTime deferredDeliveryTimeRelative; + public TimeStamp() { + super(Time.TIMEZONE_UTC); + } + + public static TimeStamp fromByteArray(byte[] data) { + TimeStamp ts = new TimeStamp(); + // C.S0015-B v2.0, 4.5.4: range is 1996-2095 + int year = IccUtils.beBcdByteToInt(data[0]); + if (year > 99 || year < 0) return null; + ts.year = year >= 96 ? year + 1900 : year + 2000; + int month = IccUtils.beBcdByteToInt(data[1]); + if (month < 1 || month > 12) return null; + ts.month = month - 1; + int day = IccUtils.beBcdByteToInt(data[2]); + if (day < 1 || day > 31) return null; + ts.monthDay = day; + int hour = IccUtils.beBcdByteToInt(data[3]); + if (hour < 0 || hour > 23) return null; + ts.hour = hour; + int minute = IccUtils.beBcdByteToInt(data[4]); + if (minute < 0 || minute > 59) return null; + ts.minute = minute; + int second = IccUtils.beBcdByteToInt(data[5]); + if (second < 0 || second > 59) return null; + ts.second = second; + return ts; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("TimeStamp "); + builder.append("{ year=" + year); + builder.append(", month=" + month); + builder.append(", day=" + monthDay); + builder.append(", hour=" + hour); + builder.append(", minute=" + minute); + builder.append(", second=" + second); + builder.append(" }"); + return builder.toString(); + } + } + + public TimeStamp msgCenterTimeStamp; + public TimeStamp validityPeriodAbsolute; + public TimeStamp deferredDeliveryTimeAbsolute; + + /** + * Relative time is specified as one byte, the value of which + * falls into a series of ranges, as specified below. The idea is + * that shorter time intervals allow greater precision -- the + * value means minutes from zero until the MINS_LIMIT (inclusive), + * upon which it means hours until the HOURS_LIMIT, and so + * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1) + */ + public static final int RELATIVE_TIME_MINS_LIMIT = 143; + public static final int RELATIVE_TIME_HOURS_LIMIT = 167; + public static final int RELATIVE_TIME_DAYS_LIMIT = 196; + public static final int RELATIVE_TIME_WEEKS_LIMIT = 244; + public static final int RELATIVE_TIME_INDEFINITE = 245; + public static final int RELATIVE_TIME_NOW = 246; + public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247; + public static final int RELATIVE_TIME_RESERVED = 248; + + public boolean validityPeriodRelativeSet; + public int validityPeriodRelative; + public boolean deferredDeliveryTimeRelativeSet; + public int deferredDeliveryTimeRelative; /** - * Reply Option - * 1-bit values which indicate whether SMS acknowledgment is requested or not. - * (See 3GPP2 C.S0015-B, v2, 4.5.11) + * The Reply Option subparameter contains 1-bit values which + * indicate whether SMS acknowledgment is requested or not. (See + * 3GPP2 C.S0015-B, v2, 4.5.11) */ public boolean userAckReq; public boolean deliveryAckReq; @@ -225,14 +306,28 @@ public final class BearerData{ public boolean reportReq; /** - * The number of Messages element (8-bit value) is a decimal number in the 0 to 99 range - * representing the number of messages stored at the Voice Mail System. This element is - * used by the Voice Mail Notification service. - * (See 3GPP2 C.S0015-B, v2, 4.5.12) + * The Number of Messages subparameter (8-bit value) is a decimal + * number in the 0 to 99 range representing the number of messages + * stored at the Voice Mail System. This element is used by the + * Voice Mail Notification service. (See 3GPP2 C.S0015-B, v2, + * 4.5.12) */ public int numberOfMessages; /** + * The Message Deposit Index subparameter is assigned by the + * message center as a unique index to the contents of the User + * Data subparameter in each message sent to a particular mobile + * station. The mobile station, when replying to a previously + * received short message which included a Message Deposit Index + * subparameter, may include the Message Deposit Index of the + * received message to indicate to the message center that the + * original contents of the message are to be included in the + * reply. (See 3GPP2 C.S0015-B, v2, 4.5.18) + */ + public int depositIndex; + + /** * 4-bit or 8-bit value that indicates the number to be dialed in reply to a * received SMS message. * (See 3GPP2 C.S0015-B, v2, 4.5.15) @@ -248,25 +343,36 @@ public final class BearerData{ @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("BearerData:\n"); - builder.append(" messageType: " + messageType + "\n"); - builder.append(" messageId: " + (int)messageId + "\n"); - builder.append(" priority: " + (priorityIndicatorSet ? priority : "not set") + "\n"); - builder.append(" privacy: " + (privacyIndicatorSet ? privacy : "not set") + "\n"); - builder.append(" alert: " + (alertIndicatorSet ? alert : "not set") + "\n"); - builder.append(" displayMode: " + (displayModeSet ? displayMode : "not set") + "\n"); - builder.append(" language: " + (languageIndicatorSet ? language : "not set") + "\n"); - builder.append(" errorClass: " + (messageStatusSet ? errorClass : "not set") + "\n"); - builder.append(" msgStatus: " + (messageStatusSet ? messageStatus : "not set") + "\n"); - builder.append(" hasUserDataHeader: " + hasUserDataHeader + "\n"); - builder.append(" timeStamp: " + timeStamp + "\n"); - builder.append(" userAckReq: " + userAckReq + "\n"); - builder.append(" deliveryAckReq: " + deliveryAckReq + "\n"); - builder.append(" readAckReq: " + readAckReq + "\n"); - builder.append(" reportReq: " + reportReq + "\n"); - builder.append(" numberOfMessages: " + numberOfMessages + "\n"); - builder.append(" callbackNumber: " + callbackNumber + "\n"); - builder.append(" userData: " + userData + "\n"); + builder.append("BearerData "); + builder.append("{ messageType=" + messageType); + builder.append(", messageId=" + (int)messageId); + builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset")); + builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset")); + builder.append(", alert=" + (alertIndicatorSet ? alert : "unset")); + builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset")); + builder.append(", language=" + (languageIndicatorSet ? language : "unset")); + builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset")); + builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset")); + builder.append(", msgCenterTimeStamp=" + + ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset")); + builder.append(", validityPeriodAbsolute=" + + ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset")); + builder.append(", validityPeriodRelative=" + + ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset")); + builder.append(", deferredDeliveryTimeAbsolute=" + + ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset")); + builder.append(", deferredDeliveryTimeRelative=" + + ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset")); + builder.append(", userAckReq=" + userAckReq); + builder.append(", deliveryAckReq=" + deliveryAckReq); + builder.append(", readAckReq=" + readAckReq); + builder.append(", reportReq=" + reportReq); + builder.append(", numberOfMessages=" + numberOfMessages); + builder.append(", callbackNumber=" + callbackNumber); + builder.append(", depositIndex=" + depositIndex); + builder.append(", hasUserDataHeader=" + hasUserDataHeader); + builder.append(", userData=" + userData); + builder.append(" }"); return builder.toString(); } @@ -281,25 +387,60 @@ public final class BearerData{ outStream.skip(3); } - private static byte[] encode7bitAscii(String msg) + private static int countAsciiSeptets(CharSequence msg, boolean force) { + int msgLen = msg.length(); + if (force) return msgLen; + for (int i = 0; i < msgLen; i++) { + if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) { + return -1; + } + } + return msgLen; + } + + /** + * Calculate the message text encoding length, fragmentation, and other details. + * + * @param force ignore (but still count) illegal characters if true + * @return septet count, or -1 on failure + */ + public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg, + boolean force7BitEncoding) { + TextEncodingDetails ted; + int septets = countAsciiSeptets(msg, force7BitEncoding); + if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) { + ted = new TextEncodingDetails(); + ted.msgCount = 1; + ted.codeUnitCount = septets; + ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets; + ted.codeUnitSize = SmsMessage.ENCODING_7BIT; + } else { + ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength( + msg, force7BitEncoding); + } + return ted; + } + + private static byte[] encode7bitAscii(String msg, boolean force) throws CodingException { try { BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length()); - byte[] expandedData = msg.getBytes("US-ASCII"); - for (int i = 0; i < expandedData.length; i++) { - int charCode = expandedData[i]; - // Test ourselves for ASCII membership, since Java seems not to care. - if ((charCode < UserData.PRINTABLE_ASCII_MIN_INDEX) || - (charCode > UserData.PRINTABLE_ASCII_MAX_INDEX)) { - throw new CodingException("illegal ASCII code (" + charCode + ")"); + int msgLen = msg.length(); + for (int i = 0; i < msgLen; i++) { + int charCode = UserData.charToAscii.get(msg.charAt(i), -1); + if (charCode == -1) { + if (force) { + outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); + } else { + throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); + } + } else { + outStream.write(7, charCode); } - outStream.write(7, expandedData[i]); } return outStream.toByteArray(); - } catch (java.io.UnsupportedEncodingException ex) { - throw new CodingException("7bit ASCII encode failed: " + ex); - } catch (BitwiseOutputStream.AccessException ex) { + } catch (BitwiseOutputStream.AccessException ex) { throw new CodingException("7bit ASCII encode failed: " + ex); } } @@ -308,24 +449,31 @@ public final class BearerData{ throws CodingException { try { - return msg.getBytes("utf-16be"); // XXX(do not submit) -- make sure decode matches + return msg.getBytes("utf-16be"); } catch (java.io.UnsupportedEncodingException ex) { throw new CodingException("UTF-16 encode failed: " + ex); } } - private static byte[] encode7bitGsm(String msg) + private static int calcUdhSeptetPadding(int userDataHeaderLen) { + int udhBits = userDataHeaderLen * 8; + int udhSeptets = (udhBits + 6) / 7; + int paddingBits = (udhSeptets * 7) - udhBits; + return paddingBits; + } + + private static byte[] encode7bitGsm(String msg, int paddingBits) throws CodingException { try { - /** - * TODO(cleanup): find some way to do this without the copy. + /* + * TODO(cleanup): It would be nice if GsmAlphabet provided + * an option to produce just the data without prepending + * the length. */ - byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg); + byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg, 0, -1, paddingBits, true); byte []data = new byte[fullData.length - 1]; - for (int i = 0; i < data.length; i++) { - data[i] = fullData[i + 1]; - } + System.arraycopy(fullData, 1, data, 0, fullData.length - 1); return data; } catch (com.android.internal.telephony.EncodeException ex) { throw new CodingException("7bit GSM encode failed: " + ex); @@ -335,51 +483,85 @@ public final class BearerData{ private static void encodeUserDataPayload(UserData uData) throws CodingException { + // TODO(cleanup): UDH can only occur in EMS mode, meaning + // encapsulation of GSM encoding, and so the logic here should + // be refactored to more cleanly reflect this constraint. + + byte[] headerData = null; + if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader); + int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet + + byte[] payloadData; + int codeUnitCount; if (uData.msgEncodingSet) { if (uData.msgEncoding == UserData.ENCODING_OCTET) { if (uData.payload == null) { Log.e(LOG_TAG, "user data with octet encoding but null payload"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? - uData.payload = new byte[0]; + payloadData = new byte[0]; + codeUnitCount = 0; + } else { + payloadData = uData.payload; + codeUnitCount = uData.payload.length; } } else { if (uData.payloadStr == null) { Log.e(LOG_TAG, "non-octet user data with null payloadStr"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? uData.payloadStr = ""; } if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { - uData.payload = encode7bitGsm(uData.payloadStr); + int paddingBits = calcUdhSeptetPadding(headerDataLen); + payloadData = encode7bitGsm(uData.payloadStr, paddingBits); + codeUnitCount = ((payloadData.length + headerDataLen) * 8) / 7; } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { - uData.payload = encode7bitAscii(uData.payloadStr); + payloadData = encode7bitAscii(uData.payloadStr, true); + codeUnitCount = uData.payloadStr.length(); } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { - uData.payload = encodeUtf16(uData.payloadStr); + payloadData = encodeUtf16(uData.payloadStr); + codeUnitCount = uData.payloadStr.length(); } else { throw new CodingException("unsupported user data encoding (" + uData.msgEncoding + ")"); } - uData.numFields = uData.payloadStr.length(); } } else { if (uData.payloadStr == null) { Log.e(LOG_TAG, "user data with null payloadStr"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? uData.payloadStr = ""; } try { - uData.payload = encode7bitAscii(uData.payloadStr); - uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + if (headerData == null) { + payloadData = encode7bitAscii(uData.payloadStr, false); + codeUnitCount = uData.payloadStr.length(); + uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + } else { + // If there is a header, we are in EMS mode, in + // which case we use GSM encodings. + int paddingBits = calcUdhSeptetPadding(headerDataLen); + payloadData = encode7bitGsm(uData.payloadStr, paddingBits); + codeUnitCount = ((payloadData.length + headerDataLen) * 8) / 7; + uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; + } } catch (CodingException ex) { - uData.payload = encodeUtf16(uData.payloadStr); + payloadData = encodeUtf16(uData.payloadStr); + codeUnitCount = uData.payloadStr.length(); uData.msgEncoding = UserData.ENCODING_UNICODE_16; } uData.msgEncodingSet = true; - uData.numFields = uData.payloadStr.length(); } - if (uData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) { - throw new CodingException("encoded user data too large (" + uData.payload.length + + + int totalLength = payloadData.length + headerDataLen; + if (totalLength > SmsMessage.MAX_USER_DATA_BYTES) { + throw new CodingException("encoded user data too large (" + totalLength + " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)"); } + + uData.numFields = codeUnitCount; + uData.payload = new byte[totalLength]; + if (headerData != null) { + uData.payload[0] = (byte)headerData.length; + System.arraycopy(headerData, 0, uData.payload, 1, headerData.length); + } + System.arraycopy(payloadData, 0, uData.payload, headerDataLen, payloadData.length); } private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream) @@ -394,11 +576,6 @@ public final class BearerData{ * */ int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits; - byte[] headerData = null; - if (bData.hasUserDataHeader) { - headerData = bData.userData.userDataHeader.toByteArray(); - dataBits += headerData.length * 8; - } int paramBits = dataBits + 13; if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { @@ -413,7 +590,6 @@ public final class BearerData{ outStream.write(8, bData.userData.msgType); } outStream.write(8, bData.userData.numFields); - if (headerData != null) outStream.writeByteArray(headerData.length * 8, headerData); outStream.writeByteArray(dataBits, bData.userData.payload); if (paddingBits > 0) outStream.write(paddingBits, 0); } @@ -502,11 +678,11 @@ public final class BearerData{ outStream.write(8, bData.numberOfMessages); } - private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream) + private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream) throws BitwiseOutputStream.AccessException { - outStream.write(8, 6); - outStream.writeByteArray(6 * 8, bData.timeStamp); + outStream.write(8, 1); + outStream.write(8, bData.validityPeriodRelative); } private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream) @@ -557,6 +733,8 @@ public final class BearerData{ * @return data byta array of raw encoded SMS bearer data. */ public static byte[] encode(BearerData bData) { + bData.hasUserDataHeader = ((bData.userData != null) && + (bData.userData.userDataHeader != null)); try { BitwiseOutputStream outStream = new BitwiseOutputStream(200); outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER); @@ -577,9 +755,9 @@ public final class BearerData{ outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES); encodeMsgCount(bData, outStream); } - if (bData.timeStamp != null) { - outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP); - encodeMsgCenterTimeStamp(bData, outStream); + if (bData.validityPeriodRelativeSet) { + outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE); + encodeValidityPeriodRel(bData, outStream); } if (bData.privacyIndicatorSet) { outStream.write(8, SUBPARAM_PRIVACY_INDICATOR); @@ -630,7 +808,7 @@ public final class BearerData{ private static void decodeUserData(BearerData bData, BitwiseInputStream inStream) throws BitwiseInputStream.AccessException { - byte paramBytes = inStream.read(8); + int paramBytes = inStream.read(8); bData.userData = new UserData(); bData.userData.msgEncoding = inStream.read(5); bData.userData.msgEncodingSet = true; @@ -698,7 +876,7 @@ public final class BearerData{ inStream.skip(offset); byte[] expandedData = new byte[numFields]; for (int i = 0; i < numFields; i++) { - expandedData[i] = inStream.read(7); + expandedData[i] = (byte)inStream.read(7); } return new String(expandedData, 0, numFields, "US-ASCII"); } catch (java.io.UnsupportedEncodingException ex) { @@ -711,7 +889,12 @@ public final class BearerData{ private static String decode7bitGsm(byte[] data, int offset, int numFields) throws CodingException { - String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields); + int paddingBits = calcUdhSeptetPadding(offset); + numFields -= (((offset * 8) + paddingBits) / 7); + // TODO: It seems wrong that only Gsm7 bit encodings would + // take into account the header in numFields calculations. + // This should be verified. + String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits); if (result == null) { throw new CodingException("7bit GSM decoding failed"); } @@ -723,11 +906,11 @@ public final class BearerData{ { int offset = 0; if (hasUserDataHeader) { - int udhLen = userData.payload[0]; - offset += udhLen; + int udhLen = userData.payload[0] & 0x00FF; + offset += udhLen + 1; byte[] headerData = new byte[udhLen]; System.arraycopy(userData.payload, 1, headerData, 0, udhLen); - userData.userDataHeader = SmsHeader.parse(headerData); + userData.userDataHeader = SmsHeader.fromByteArray(headerData); } switch (userData.msgEncoding) { case UserData.ENCODING_OCTET: @@ -750,10 +933,126 @@ public final class BearerData{ } } + /** + * IS-91 Voice Mail message decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * (For character encodings, see TIA/EIA/IS-91, Annex B) + * + * Protocol Summary: The user data payload may contain 3-14 + * characters. The first two characters are parsed as a number + * and indicate the number of voicemails. The third character is + * either a SPACE or '!' to indicate normal or urgent priority, + * respectively. Any following characters are treated as normal + * text user data payload. + * + * Note that the characters encoding is 6-bit packed. + */ + private static void decodeIs91VoicemailStatus(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 6; // 6-bit packed character encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + try { + StringBuffer strbuf = new StringBuffer(dataLen); + while (inStream.available() >= 6) { + strbuf.append(UserData.IA5_MAP[inStream.read(6)]); + } + String data = strbuf.toString(); + bData.numberOfMessages = Integer.parseInt(data.substring(0, 2)); + char prioCode = data.charAt(2); + if (prioCode == ' ') { + bData.priority = PRIORITY_NORMAL; + } else if (prioCode == '!') { + bData.priority = PRIORITY_URGENT; + } else { + throw new CodingException("IS-91 voicemail status decoding failed: " + + "illegal priority setting (" + prioCode + ")"); + } + bData.priorityIndicatorSet = true; + bData.userData.payloadStr = data.substring(3, numFields - 3); + } catch (java.lang.NumberFormatException ex) { + throw new CodingException("IS-91 voicemail status decoding failed: " + ex); + } catch (java.lang.IndexOutOfBoundsException ex) { + throw new CodingException("IS-91 voicemail status decoding failed: " + ex); + } + } + + /** + * IS-91 Short Message decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * (For character encodings, see TIA/EIA/IS-91, Annex B) + * + * Protocol Summary: The user data payload may contain 1-14 + * characters, which are treated as normal text user data payload. + * Note that the characters encoding is 6-bit packed. + */ + private static void decodeIs91ShortMessage(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 6; // 6-bit packed character encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + StringBuffer strbuf = new StringBuffer(dataLen); + for (int i = 0; i < numFields; i++) { + strbuf.append(UserData.IA5_MAP[inStream.read(6)]); + } + bData.userData.payloadStr = strbuf.toString(); + } + + /** + * IS-91 CLI message (callback number) decoding + * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) + * + * Protocol Summary: The data payload may contain 1-32 digits, + * encoded using standard 4-bit DTMF, which are treated as a + * callback number. + */ + private static void decodeIs91Cli(BearerData bData) throws CodingException { + BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); + int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding. + int numFields = bData.userData.numFields; + if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { + throw new CodingException("IS-91 voicemail status decoding failed"); + } + CdmaSmsAddress addr = new CdmaSmsAddress(); + addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; + addr.origBytes = bData.userData.payload; + addr.numberOfDigits = (byte)numFields; + decodeSmsAddress(addr); + bData.callbackNumber = addr; + } + + private static void decodeIs91(BearerData bData) + throws BitwiseInputStream.AccessException, CodingException + { + switch (bData.userData.msgType) { + case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS: + decodeIs91VoicemailStatus(bData); + break; + case UserData.IS91_MSG_TYPE_CLI: + decodeIs91Cli(bData); + break; + case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL: + case UserData.IS91_MSG_TYPE_SHORT_MESSAGE: + decodeIs91ShortMessage(bData); + break; + default: + throw new CodingException("unsupported IS-91 message type (" + + bData.userData.msgType + ")"); + } + } + private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException { - byte paramBytes = inStream.read(8); + int paramBytes = inStream.read(8); if (paramBytes != 1) { throw new CodingException("REPLY_OPTION subparam size incorrect"); } @@ -773,6 +1072,15 @@ public final class BearerData{ bData.numberOfMessages = inStream.read(8); } + private static void decodeDepositIndex(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 2) { + throw new CodingException("MESSAGE_DEPOSIT_INDEX subparam size incorrect"); + } + bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8); + } + private static String decodeDtmfSmsAddress(byte[] rawData, int numFields) throws CodingException { @@ -807,7 +1115,7 @@ public final class BearerData{ private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException { - byte paramBytes = inStream.read(8); + int paramBytes = inStream.read(8); CdmaSmsAddress addr = new CdmaSmsAddress(); addr.digitMode = inStream.read(1); byte fieldBits = 4; @@ -845,14 +1153,51 @@ public final class BearerData{ bData.messageStatusSet = true; } - private static void decodeMsgCenterTimeStamp(BearerData bData, - BitwiseInputStream inStream) + private static void decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException { if (inStream.read(8) != 6) { throw new CodingException("MESSAGE_CENTER_TIME_STAMP subparam size incorrect"); } - bData.timeStamp = inStream.readByteArray(6 * 8); + bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); + } + + private static void decodeValidityAbs(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 6) { + throw new CodingException("VALIDITY_PERIOD_ABSOLUTE subparam size incorrect"); + } + bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); + } + + private static void decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 6) { + throw new CodingException("DEFERRED_DELIVERY_TIME_ABSOLUTE subparam size incorrect"); + } + bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); + } + + private static void decodeValidityRel(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 1) { + throw new CodingException("VALIDITY_PERIOD_RELATIVE subparam size incorrect"); + } + bData.deferredDeliveryTimeRelative = inStream.read(8); + bData.deferredDeliveryTimeRelativeSet = true; + } + + private static void decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 1) { + throw new CodingException("DEFERRED_DELIVERY_TIME_RELATIVE subparam size incorrect"); + } + bData.validityPeriodRelative = inStream.read(8); + bData.validityPeriodRelativeSet = true; } private static void decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream) @@ -909,6 +1254,16 @@ public final class BearerData{ bData.alertIndicatorSet = true; } + private static void decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream) + throws BitwiseInputStream.AccessException, CodingException + { + if (inStream.read(8) != 1) { + throw new CodingException("USER_REPONSE_CODE subparam size incorrect"); + } + bData.userResponseCode = inStream.read(8); + bData.userResponseCodeSet = true; + } + /** * Create BearerData object from serialized representation. * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) @@ -937,6 +1292,9 @@ public final class BearerData{ case SUBPARAM_USER_DATA: decodeUserData(bData, inStream); break; + case SUBPARAM_USER_REPONSE_CODE: + decodeUserResponseCode(bData, inStream); + break; case SUBPARAM_REPLY_OPTION: decodeReplyOption(bData, inStream); break; @@ -952,6 +1310,18 @@ public final class BearerData{ case SUBPARAM_MESSAGE_CENTER_TIME_STAMP: decodeMsgCenterTimeStamp(bData, inStream); break; + case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE: + decodeValidityAbs(bData, inStream); + break; + case SUBPARAM_VALIDITY_PERIOD_RELATIVE: + decodeValidityRel(bData, inStream); + break; + case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE: + decodeDeferredDeliveryAbs(bData, inStream); + break; + case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE: + decodeDeferredDeliveryRel(bData, inStream); + break; case SUBPARAM_PRIVACY_INDICATOR: decodePrivacyIndicator(bData, inStream); break; @@ -967,6 +1337,9 @@ public final class BearerData{ case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY: decodeMsgDeliveryAlert(bData, inStream); break; + case SUBPARAM_MESSAGE_DEPOSIT_INDEX: + decodeDepositIndex(bData, inStream); + break; default: throw new CodingException("unsupported bearer data subparameter (" + subparamId + ")"); @@ -976,7 +1349,18 @@ public final class BearerData{ throw new CodingException("missing MESSAGE_IDENTIFIER subparam"); } if (bData.userData != null) { - decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); + if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) { + if ((foundSubparamMask ^ + (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^ + (1 << SUBPARAM_USER_DATA)) + != 0) { + Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" + + foundSubparamMask + ")"); + } + decodeIs91(bData); + } else { + decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); + } } return bData; } catch (BitwiseInputStream.AccessException ex) { diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java index 440debbd354e..4d7996657d6d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -20,23 +20,31 @@ import com.android.internal.telephony.SmsAddress; import com.android.internal.util.HexDump; public class CdmaSmsAddress extends SmsAddress { + /** - * digit mode indicators - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + * Digit Mode Indicator is a 1-bit value that indicates whether + * the address digits are 4-bit DTMF codes or 8-bit codes. (See + * 3GPP2 C.S0015-B, v2, 3.4.3.3) */ static public final int DIGIT_MODE_4BIT_DTMF = 0x00; static public final int DIGIT_MODE_8BIT_CHAR = 0x01; + public int digitMode; + /** - * number mode indicators - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + * Number Mode Indicator is 1-bit value that indicates whether the + * address type is a data network address or not. (See 3GPP2 + * C.S0015-B, v2, 3.4.3.3) */ static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00; static public final int NUMBER_MODE_DATA_NETWORK = 0x01; + public int numberMode; + /** - * number types for data networks - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + * Number Types for data networks. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + * NOTE: value is stored in the parent class ton field. */ static public final int TON_UNKNOWN = 0x00; static public final int TON_INTERNATIONAL_OR_IP = 0x01; @@ -48,14 +56,21 @@ public class CdmaSmsAddress extends SmsAddress { static public final int TON_RESERVED = 0x07; /** - * maximum lengths for fields as defined in ril_cdma_sms.h + * Maximum lengths for fields as defined in ril_cdma_sms.h. */ static public final int SMS_ADDRESS_MAX = 36; static public final int SMS_SUBADDRESS_MAX = 36; /** - * Supported numbering plan identification - * (See C.S005-D, v1.0, table 2.7.1.3.2.4-3) + * This field shall be set to the number of address digits + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + public int numberOfDigits; + + /** + * Numbering Plan identification is a 0 or 4-bit value that + * indicates which numbering plan identification is set. (See + * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3) */ static public final int NUMBERING_PLAN_UNKNOWN = 0x0; static public final int NUMBERING_PLAN_ISDN_TELEPHONY = 0x1; @@ -63,50 +78,29 @@ public class CdmaSmsAddress extends SmsAddress { //static protected final int NUMBERING_PLAN_TELEX = 0x4; //static protected final int NUMBERING_PLAN_PRIVATE = 0x9; - /** - * 1-bit value that indicates whether the address digits are 4-bit DTMF codes - * or 8-bit codes. - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) - */ - public byte digitMode; - - /** - * 1-bit value that indicates whether the address type is a data network address or not. - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) - */ - public byte numberMode; - - // use parent class member ton instead public byte numberType; + public int numberPlan; /** - * 0 or 4-bit value that indicates which numbering plan identification is set. - * (See 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3) + * NOTE: the parsed string address and the raw byte array values + * are stored in the parent class address and origBytes fields, + * respectively. */ - public byte numberPlan; - - /** - * This field shall be set to the number of address digits - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) - */ - public byte numberOfDigits; - - // use parent class member orig_bytes instead of public byte[] digits; - // Constructor public CdmaSmsAddress(){ } @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("CdmaSmsAddress:\n"); - builder.append(" digitMode: " + digitMode + "\n"); - builder.append(" numberMode: " + numberMode + "\n"); - builder.append(" numberPlan: " + numberPlan + "\n"); - builder.append(" numberOfDigits: " + numberOfDigits + "\n"); - builder.append(" ton: " + ton + "\n"); - builder.append(" address: " + address + "\n"); - builder.append(" origBytes: " + HexDump.toHexString(origBytes) + "\n"); + builder.append("CdmaSmsAddress "); + builder.append("{ digitMode=" + digitMode); + builder.append(", numberMode=" + numberMode); + builder.append(", numberPlan=" + numberPlan); + builder.append(", numberOfDigits=" + numberOfDigits); + builder.append(", ton=" + ton); + builder.append(", address=" + address); + builder.append(", origBytes=" + HexDump.toHexString(origBytes)); + builder.append(" }"); return builder.toString(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java index 02e94ad768f0..34cbbfaa3921 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.cdma.sms; +import android.util.SparseIntArray; + import com.android.internal.telephony.SmsHeader; import com.android.internal.util.HexDump; @@ -38,8 +40,26 @@ public class UserData { public static final int ENCODING_GSM_DCS = 0x0A; /** + * IS-91 message types. + * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3) + */ + public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82; + public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83; + public static final int IS91_MSG_TYPE_CLI = 0x84; + public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85; + + /** * IA5 data encoding character mappings. * (See CCITT Rec. T.50 Tables 1 and 3) + * + * Note this mapping is the the same as for printable ASCII + * characters, with a 0x20 offset, meaning that the ASCII SPACE + * character occurs with code 0x20. + * + * Note this mapping is also equivalent to that used by the IS-91 + * protocol, except for the latter using only 6 bits, and hence + * mapping only entries up to the '_' character. + * */ public static final char[] IA5_MAP = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', @@ -50,10 +70,27 @@ public class UserData { 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~'}; /** + * Character to use when forced to encode otherwise unencodable + * characters, meaning those not in the respective ASCII or GSM + * 7-bit encoding tables. Current choice is SPACE, which is 0x20 + * in both the GSM-7bit and ASCII-7bit encodings. + */ + static final byte UNENCODABLE_7_BIT_CHAR = 0x20; + + /** * Only elements between these indices in the ASCII table are printable. */ public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20; - public static final int PRINTABLE_ASCII_MAX_INDEX = 0x7F; + public static final int ASCII_LF_INDEX = 0x0A; + public static final int ASCII_CR_INDEX = 0x0D; + public static final SparseIntArray charToAscii = new SparseIntArray(); + static { + for (int i = 0; i < IA5_MAP.length; i++) { + charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i); + } + charToAscii.put('\r', ASCII_LF_INDEX); + charToAscii.put('\n', ASCII_CR_INDEX); + } /** * Mapping for IA5 values less than 32 are flow control signals @@ -73,7 +110,6 @@ public class UserData { public int msgEncoding; public boolean msgEncodingSet = false; - // XXX needed when encoding is IS91 or DCS (not supported yet): public int msgType; /** @@ -93,14 +129,15 @@ public class UserData { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("UserData:\n"); - builder.append(" msgEncoding: " + (msgEncodingSet ? msgEncoding : "not set") + "\n"); - builder.append(" msgType: " + msgType + "\n"); - builder.append(" paddingBits: " + paddingBits + "\n"); - builder.append(" numFields: " + (int)numFields + "\n"); - builder.append(" userDataHeader: " + userDataHeader + "\n"); - builder.append(" payload: '" + HexDump.toHexString(payload) + "'"); - builder.append(", payloadStr: '" + payloadStr + "'"); + builder.append("UserData "); + builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset")); + builder.append(", msgType=" + msgType); + builder.append(", paddingBits=" + paddingBits); + builder.append(", numFields=" + (int)numFields); + builder.append(", userDataHeader=" + userDataHeader); + builder.append(", payload='" + HexDump.toHexString(payload) + "'"); + builder.append(", payloadStr='" + payloadStr + "'"); + builder.append(" }"); return builder.toString(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/package.html b/telephony/java/com/android/internal/telephony/cdma/sms/package.html index 48e10340cb86..b2bc7364748f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/package.html +++ b/telephony/java/com/android/internal/telephony/cdma/sms/package.html @@ -1,6 +1,6 @@ <HTML> <BODY> -Provides CDMA-specific features for text/data/PDU SMS messages +Provides CDMA-specific features for text/data/PDU SMS messages @hide </BODY> </HTML> diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index a2d3c5e38047..d1e4b4f21cab 100755 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -34,6 +34,7 @@ import android.provider.Telephony; import android.telephony.CellLocation; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.text.TextUtils; import android.util.Log; @@ -67,6 +68,7 @@ import com.android.internal.telephony.PhoneNotifier; import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.gsm.stk.StkService; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.IccVmNotSupportedException; @@ -203,9 +205,9 @@ public class GSMPhone extends PhoneBase { } } - //Change the system setting - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.GSM_PHONE); + //Change the system property + SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, + new Integer(RILConstants.GSM_PHONE).toString()); } public void dispose() { @@ -285,9 +287,8 @@ public class GSMPhone extends PhoneBase { return mDataConnection.getActiveApnString(); } - public int - getSignalStrengthASU() { - return mSST.rssi == 99 ? -1 : mSST.rssi; + public SignalStrength getSignalStrength() { + return mSST.mSignalStrength; } public boolean @@ -447,11 +448,6 @@ public class GSMPhone extends PhoneBase { } public void - notifyMessageWaitingIndicator() { - mNotifier.notifyMessageWaitingChanged(this); - } - - public void notifyCallForwardingIndicator() { mNotifier.notifyCallForwardingChanged(this); } @@ -825,6 +821,11 @@ public class GSMPhone extends PhoneBase { } public void + sendBurstDtmf(String dtmfString) { + Log.e(LOG_TAG, "[GSMPhone] sendBurstDtmf() is a CDMA method"); + } + + public void setRadioPower(boolean power) { mSST.setRadioPower(power); } @@ -832,21 +833,21 @@ public class GSMPhone extends PhoneBase { private void storeVoiceMailNumber(String number) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); - editor.putString(VM_NUMBER, number); + editor.putString(VM_NUMBER, number); editor.commit(); setVmSimImsi(getSubscriberId()); } public String getVoiceMailNumber() { // Read from the SIM. If its null, try reading from the shared preference area. - String number = mSIMRecords.getVoiceMailNumber(); + String number = mSIMRecords.getVoiceMailNumber(); if (TextUtils.isEmpty(number)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); number = sp.getString(VM_NUMBER, null); - } + } return number; } - + private String getVmSimImsi() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); return sp.getString(VM_SIM_IMSI, null); @@ -858,7 +859,7 @@ public class GSMPhone extends PhoneBase { editor.putString(VM_SIM_IMSI, imsi); editor.commit(); } - + public String getVoiceMailAlphaTag() { String ret; @@ -922,13 +923,13 @@ public class GSMPhone extends PhoneBase { public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { - - Message resp; + + Message resp; mVmNumber = voiceMailNumber; resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp); } - + private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { switch (commandInterfaceCFReason) { case CF_REASON_UNCONDITIONAL: @@ -1307,11 +1308,11 @@ public class GSMPhone extends PhoneBase { case EVENT_SIM_RECORDS_LOADED: updateCurrentCarrierInProvider(); - + // Check if this is a different SIM than the previous one. If so unset the // voice mail number. String imsi = getVmSimImsi(); - if (imsi != null && !getSubscriberId().equals(imsi)) { + if (imsi != null && !getSubscriberId().equals(imsi)) { storeVoiceMailNumber(null); setVmSimImsi(null); } @@ -1393,7 +1394,7 @@ public class GSMPhone extends PhoneBase { onComplete.sendToTarget(); } break; - + case EVENT_SET_VM_NUMBER_DONE: ar = (AsyncResult)msg.obj; if (IccVmNotSupportedException.class.isInstance(ar.exception)) { @@ -1407,7 +1408,7 @@ public class GSMPhone extends PhoneBase { } break; - + case EVENT_GET_CALL_FORWARD_DONE: ar = (AsyncResult)msg.obj; if (ar.exception == null) { @@ -1450,7 +1451,7 @@ public class GSMPhone extends PhoneBase { /** * Sets the "current" field in the telephony provider according to the SIM's operator - * + * * @return true for success; false otherwise. */ boolean updateCurrentCarrierInProvider() { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java index 2b2f077fd894..d93ca1d6b8ab 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java @@ -83,7 +83,7 @@ public class GsmConnection extends Connection { static final int EVENT_PAUSE_DONE = 2; static final int EVENT_NEXT_POST_DIAL = 3; static final int EVENT_WAKE_LOCK_TIMEOUT = 4; - + //***** Constants static final int PAUSE_DELAY_FIRST_MILLIS = 100; static final int PAUSE_DELAY_MILLIS = 3 * 1000; @@ -117,7 +117,7 @@ public class GsmConnection extends Connection { GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index) { createWakeLock(context); acquireWakeLock(); - + owner = ct; h = new MyHandler(owner.getLooper()); @@ -138,7 +138,7 @@ public class GsmConnection extends Connection { GsmConnection (Context context, String dialString, GsmCallTracker ct, GsmCall parent) { createWakeLock(context); acquireWakeLock(); - + owner = ct; h = new MyHandler(owner.getLooper()); @@ -375,7 +375,7 @@ public class GsmConnection extends Connection { return DisconnectCause.ICC_ERROR; } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) { if (phone.mSST.rs.isCsRestricted()) { - return DisconnectCause.CS_RESTRICTED; + return DisconnectCause.CS_RESTRICTED; } else if (phone.mSST.rs.isCsEmergencyRestricted()) { return DisconnectCause.CS_RESTRICTED_EMERGENCY; } else if (phone.mSST.rs.isCsNormalRestricted()) { @@ -575,7 +575,7 @@ public class GsmConnection extends Connection { return postDialString.substring(nextPostDialChar); } - + @Override protected void finalize() { @@ -609,7 +609,7 @@ public class GsmConnection extends Connection { c = 0; } else { boolean isValid; - + setPostDialState(PostDialState.STARTED); c = postDialString.charAt(nextPostDialChar++); @@ -680,31 +680,31 @@ public class GsmConnection extends Connection { } /** - * Set post dial state and acquire wake lock while switching to "started" - * state, the wake lock will be released if state switches out of "started" - * state or after WAKE_LOCK_TIMEOUT_MILLIS. + * Set post dial state and acquire wake lock while switching to "started" + * state, the wake lock will be released if state switches out of "started" + * state or after WAKE_LOCK_TIMEOUT_MILLIS. * @param s new PostDialState */ private void setPostDialState(PostDialState s) { - if (postDialState != PostDialState.STARTED + if (postDialState != PostDialState.STARTED && s == PostDialState.STARTED) { acquireWakeLock(); Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); - } else if (postDialState == PostDialState.STARTED + } else if (postDialState == PostDialState.STARTED && s != PostDialState.STARTED) { h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); releaseWakeLock(); } postDialState = s; } - + private void createWakeLock(Context context) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); } - + private void acquireWakeLock() { log("acquireWakeLock"); @@ -720,7 +720,7 @@ public class GsmConnection extends Connection { } } } - + private void log(String msg) { Log.d(LOG_TAG, "[GSMConn] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 9e6ebc4255ee..c33d4b6f6ed9 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -28,10 +28,9 @@ import android.content.SharedPreferences; import android.database.ContentObserver; import android.database.Cursor; import android.net.NetworkInfo; -import android.net.wifi.WifiManager; import android.net.Uri; +import android.net.wifi.WifiManager; import android.os.AsyncResult; -import android.os.Handler; import android.os.INetStatService; import android.os.Message; import android.os.RemoteException; @@ -42,7 +41,6 @@ import android.preference.PreferenceManager; import android.provider.Checkin; import android.provider.Settings; import android.provider.Telephony; -import android.provider.Settings.SettingNotFoundException; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; @@ -50,12 +48,12 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.DataConnection; -import com.android.internal.telephony.DataConnection.FailCause; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.TelephonyEventLog; +import com.android.internal.telephony.DataConnection.FailCause; import java.io.IOException; import java.util.ArrayList; @@ -67,6 +65,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private static final String LOG_TAG = "GSM"; private static final boolean DBG = true; + private GSMPhone mGsmPhone; /** * Handles changes to the APN db. */ @@ -87,6 +86,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Indicates baseband will not auto-attach private boolean noAutoAttach = false; long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + private boolean mReregisterOnReconnectFailure = false; private ContentResolver mResolver; private boolean mPingTestActive = false; @@ -150,7 +150,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private static final int POLL_PDP_MILLIS = 5 * 1000; - //WINK:TODO: Teleca, is this really gsm specific, what about CDMA? private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; @@ -177,9 +176,12 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); if (state == State.FAILED) { - cleanUpConnection(false, reason); + Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 0; // tearDown is false + msg.obj = (String) reason; + sendMessage(msg); } - trySetupData(reason); + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { final android.net.NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); @@ -204,6 +206,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { GsmDataConnectionTracker(GSMPhone p) { super(p); + mGsmPhone = p; p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); @@ -250,17 +253,17 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { //Unregister for all events phone.mCM.unregisterForAvailable(this); phone.mCM.unregisterForOffOrNotAvailable(this); - ((GSMPhone) phone).mSIMRecords.unregisterForRecordsLoaded(this); + mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this); phone.mCM.unregisterForDataStateChanged(this); - ((GSMPhone) phone).mCT.unregisterForVoiceCallEnded(this); - ((GSMPhone) phone).mCT.unregisterForVoiceCallStarted(this); - ((GSMPhone) phone).mSST.unregisterForGprsAttached(this); - ((GSMPhone) phone).mSST.unregisterForGprsDetached(this); - ((GSMPhone) phone).mSST.unregisterForRoamingOn(this); - ((GSMPhone) phone).mSST.unregisterForRoamingOff(this); - ((GSMPhone) phone).mSST.unregisterForPsRestrictedEnabled(this); - ((GSMPhone) phone).mSST.unregisterForPsRestrictedDisabled(this); - + mGsmPhone.mCT.unregisterForVoiceCallEnded(this); + mGsmPhone.mCT.unregisterForVoiceCallStarted(this); + mGsmPhone.mSST.unregisterForGprsAttached(this); + mGsmPhone.mSST.unregisterForGprsDetached(this); + mGsmPhone.mSST.unregisterForRoamingOn(this); + mGsmPhone.mSST.unregisterForRoamingOff(this); + mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this); + mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this); + phone.getContext().unregisterReceiver(this.mIntentReceiver); phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); @@ -362,7 +365,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * The APN of the specified type is no longer needed. Ensure that if * use of the default APN has not been explicitly disabled, we are connected * to the default APN. - * @param type the APN type. The only valid values are currently + * @param type the APN type. The only valid values are currently * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. * @return */ @@ -374,10 +377,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { removeMessages(EVENT_RESTORE_DEFAULT_APN); setEnabled(type, false); if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { + mRequestedApnType = Phone.APN_TYPE_DEFAULT; if (dataEnabled[APN_DEFAULT_ID]) { return Phone.APN_ALREADY_ACTIVE; } else { - cleanUpConnection(true, Phone.REASON_DATA_DISABLED); + Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true; + msg.obj = Phone.REASON_DATA_DISABLED; + sendMessage(msg); return Phone.APN_REQUEST_STARTED; } } else { @@ -407,10 +414,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { public boolean isDataConnectionAsDesired() { boolean roaming = phone.getServiceState().getRoaming(); - if (((GSMPhone) phone).mSIMRecords.getRecordsLoaded() && - ((GSMPhone) phone).mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && + if (mGsmPhone.mSIMRecords.getRecordsLoaded() && + mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && (!roaming || getDataOnRoamingEnabled()) && - !mIsWifiConnected && + !mIsWifiConnected && !mIsPsRestricted ) { return (state == State.CONNECTED); } @@ -477,7 +484,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { setEnabled(Phone.APN_TYPE_DEFAULT, true); // trySetupData() will be a no-op if we are currently // connected to the MMS APN - return trySetupData(Phone.REASON_DATA_ENABLED); + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + return true; } else if (!enable) { setEnabled(Phone.APN_TYPE_DEFAULT, false); // Don't tear down if there is an active APN and it handles MMS or SUPL. @@ -486,21 +494,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) { return false; } - cleanUpConnection(true, Phone.REASON_DATA_DISABLED); + Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true + msg.obj = Phone.REASON_DATA_DISABLED; + sendMessage(msg); return true; } else { // isEnabled && enable return true; } } - - /** - * Simply tear down data connections due to radio off - * and don't setup again. - */ - public void cleanConnectionBeforeRadioOff() { - cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); - } /** * Report the current state of data connectivity (enabled or disabled) for @@ -565,7 +568,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); - + if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator // FIXME this can be improved @@ -576,22 +579,23 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return true; } - int gprsState = ((GSMPhone) phone).mSST.getCurrentGprsState(); + int gprsState = mGsmPhone.mSST.getCurrentGprsState(); boolean roaming = phone.getServiceState().getRoaming(); + boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); if ((state == State.IDLE || state == State.SCANNING) && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) - && ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() - && ( ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() || - phone.getState() == Phone.State.IDLE ) + && mGsmPhone.mSIMRecords.getRecordsLoaded() + && phone.getState() == Phone.State.IDLE && isDataAllowed() - && !mIsPsRestricted ) { + && !mIsPsRestricted + && desiredPowerState ) { if (state == State.IDLE) { waitingApns = buildWaitingApns(); if (waitingApns.isEmpty()) { if (DBG) log("No APN found"); - notifyNoData(PdpConnection.FailCause.BAD_APN); + notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); return false; } else { log ("Create from allApns : " + apnListToString(allApns)); @@ -607,13 +611,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { log("trySetupData: Not ready for data: " + " dataState=" + state + " gprsState=" + gprsState + - " sim=" + ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() + - " UMTS=" + ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() + + " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + + " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + " phoneState=" + phone.getState() + " dataEnabled=" + getAnyDataEnabled() + " roaming=" + roaming + " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + - " ps restricted=" + mIsPsRestricted); + " ps restricted=" + mIsPsRestricted + + " desiredPowerState=" + desiredPowerState); return false; } } @@ -638,6 +643,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { mReconnectIntent = null; } + setState(State.DISCONNECTING); + for (DataConnection conn : pdpList) { PdpConnection pdp = (PdpConnection) conn; if (tearDown) { @@ -649,25 +656,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } stopNetStatPoll(); - /* - * If we've been asked to tear down the connection, - * set the state to DISCONNECTING. However, there's - * a race that can occur if for some reason we were - * already in the IDLE state. In that case, the call - * to pdp.disconnect() above will immediately post - * a message to the handler thread that the disconnect - * is done, and if the handler runs before the code - * below does, the handler will have set the state to - * IDLE before the code below runs. If we didn't check - * for that, future calls to trySetupData would fail, - * and we would never get out of the DISCONNECTING state. - */ if (!tearDown) { setState(State.IDLE); phone.notifyDataConnection(reason); mActiveApn = null; - } else if (state != State.IDLE) { - setState(State.DISCONNECTING); } } @@ -779,7 +771,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } private boolean - pdpStatesHasCID (ArrayList<PDPContextState> states, int cid) { + pdpStatesHasCID (ArrayList<DataCallState> states, int cid) { for (int i = 0, s = states.size() ; i < s ; i++) { if (states.get(i).cid == cid) return true; } @@ -788,9 +780,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } private boolean - pdpStatesHasActiveCID (ArrayList<PDPContextState> states, int cid) { + pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) { for (int i = 0, s = states.size() ; i < s ; i++) { - if (states.get(i).cid == cid) return (states.get(i).active != 0); + if ((states.get(i).cid == cid) && (states.get(i).active != 0)) { + return true; + } } return false; @@ -805,7 +799,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { isConnected = (state != State.IDLE && state != State.FAILED); // The "current" may no longer be valid. MMS depends on this to send properly. - ((GSMPhone) phone).updateCurrentCarrierInProvider(); + mGsmPhone.updateCurrentCarrierInProvider(); // TODO: It'd be nice to only do this if the changed entrie(s) // match the current operator. @@ -813,6 +807,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (state != State.DISCONNECTING) { cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); if (!isConnected) { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; trySetupData(Phone.REASON_APN_CHANGED); } } @@ -825,9 +822,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * previous state */ protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { - ArrayList<PDPContextState> pdpStates; + ArrayList<DataCallState> pdpStates; - pdpStates = (ArrayList<PDPContextState>)(ar.result); + pdpStates = (ArrayList<DataCallState>)(ar.result); if (ar.exception != null) { // This is probably "radio not available" or something @@ -893,6 +890,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { startNetStatPoll(); // reset reconnect timer nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } private void setupDnsProperties() { @@ -963,7 +961,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } else { mPdpResetCount = 0; EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); - ((GSMPhone) phone).mSST.reRegisterNetwork(null); + mGsmPhone.mSST.reRegisterNetwork(null); } // TODO: Add increasingly drastic recovery steps, eg, // reset the radio, reset the device. @@ -1056,7 +1054,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } int watchdogTrigger = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, + Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, NUMBER_SENT_PACKETS_OF_HANG); if (sentSinceLastRecv >= watchdogTrigger) { @@ -1079,7 +1077,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Slow down the poll interval to let things happen netStatPollPeriod = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, + Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, POLL_NETSTAT_SLOW_MILLIS); } else { if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + @@ -1112,7 +1110,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } } }; - + private void runPingTest () { int status = -1; try { @@ -1161,22 +1159,36 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { /** * Return true if data connection need to be setup after disconnected due to * reason. - * + * * @param reason the reason why data is disconnected - * @return true if try setup data connection is need for this reason + * @return true if try setup data connection is need for this reason */ private boolean retryAfterDisconnected(String reason) { boolean retry = true; - + if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || - Phone.REASON_DATA_DISABLED.equals(reason) ) { + Phone.REASON_DATA_DISABLED.equals(reason) ) { retry = false; } return retry; - } + } private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + if (mReregisterOnReconnectFailure) { + // We have already tried to re-register to the network. + // This might be a problem with the data network. + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } else { + // Try to Re-register to the network. + Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); + mReregisterOnReconnectFailure = true; + mGsmPhone.mSST.reRegisterNetwork(null); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + return; + } + } Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); @@ -1192,9 +1204,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // double it for next time nextReconnectDelay *= 2; - if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { - nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; - } if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " @@ -1214,7 +1223,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (state == State.FAILED) { cleanUpConnection(false, null); } - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); } protected void onEnableNewApn() { @@ -1223,17 +1232,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { cleanUpConnection(true, Phone.REASON_APN_SWITCHED); } - protected void onTrySetupData() { - trySetupData(null); + protected void onTrySetupData(String reason) { + trySetupData(reason); } protected void onRestoreDefaultApn() { if (DBG) Log.d(LOG_TAG, "Restore default APN"); setEnabled(Phone.APN_TYPE_MMS, false); - + mRequestedApnType = Phone.APN_TYPE_DEFAULT; if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN); - mRequestedApnType = Phone.APN_TYPE_DEFAULT; } } @@ -1269,6 +1277,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Make sure our reconnect delay starts at the initial value // next time the radio comes on nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator @@ -1326,19 +1335,13 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { cause = (PdpConnection.FailCause) (ar.result); if(DBG) log("PDP setup failed " + cause); // Log this failure to the Event Logs. - if (cause == PdpConnection.FailCause.BAD_APN || - cause == PdpConnection.FailCause.BAD_PAP_SECRET || - cause == PdpConnection.FailCause.BARRED || - cause == PdpConnection.FailCause.RADIO_ERROR_RETRY || - cause == PdpConnection.FailCause.SUSPENED_TEMPORARY || - cause == PdpConnection.FailCause.UNKNOWN || - cause == PdpConnection.FailCause.USER_AUTHENTICATION) { + if (cause.isEventLoggable()) { int cid = -1; GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); if (loc != null) cid = loc.getCid(); EventLog.List val = new EventLog.List( - cause.ordinal(), cid, + cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType()); EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); } @@ -1346,20 +1349,20 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); + return; } - if (tryNextApn(cause)) { - waitingApns.remove(0); - if (waitingApns.isEmpty()) { - // No more to try, start delayed retry - startDelayedRetry(cause, reason); - } else { - // we still have more apns to try - setState(State.SCANNING); - trySetupData(reason); - } - } else { + waitingApns.remove(0); + if (waitingApns.isEmpty()) { + // No more to try, start delayed retry startDelayedRetry(cause, reason); + } else { + // we still have more apns to try + setState(State.SCANNING); + // Wait a bit before trying the next APN, so that + // we're not tying up the RIL command channel + sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), + RECONNECT_DELAY_INITIAL_MILLIS); } } } @@ -1387,7 +1390,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } protected void onVoiceCallStarted() { - if (state == State.CONNECTED && !((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { stopNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); } @@ -1395,7 +1398,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { protected void onVoiceCallEnded() { if (state == State.CONNECTED) { - if (!((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) { startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); } else { @@ -1403,17 +1406,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { resetPollStats(); } } else { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } } - private boolean tryNextApn(FailCause cause) { - return (cause != FailCause.RADIO_NOT_AVAILABLE) - && (cause != FailCause.RADIO_OFF) - && (cause != FailCause.RADIO_ERROR_RETRY) - && (cause != FailCause.NO_SIGNAL) - && (cause != FailCause.SIM_LOCKED); + protected void onCleanUpConnection(boolean tearDown, String reason) { + cleanUpConnection(tearDown, reason); } private int getRestoreDefaultApnDelay() { @@ -1436,7 +1438,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { */ private void createAllApnList() { allApns = new ArrayList<ApnSetting>(); - String operator = ((GSMPhone) phone).mSIMRecords.getSIMOperatorNumeric(); + String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); if (operator != null) { String selection = "numeric = '" + operator + "'"; @@ -1462,7 +1464,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (allApns.isEmpty()) { if (DBG) log("No APN found for carrier: " + operator); preferredApn = null; - notifyNoData(PdpConnection.FailCause.BAD_APN); + notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); } else { preferredApn = getPreferredApn(); Log.d(LOG_TAG, "Get PreferredAPN"); @@ -1478,7 +1480,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { DataConnection pdp; for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { - pdp = new PdpConnection((GSMPhone) phone); + pdp = new PdpConnection(mGsmPhone); pdpList.add(pdp); } } @@ -1497,7 +1499,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { */ private ArrayList<ApnSetting> buildWaitingApns() { ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); - String operator = ((GSMPhone )phone).mSIMRecords.getSIMOperatorNumeric(); + String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { if (canSetPreferApn && preferredApn != null) { @@ -1568,7 +1570,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { ContentResolver resolver = phone.getContext().getContentResolver(); resolver.delete(PREFERAPN_URI, null, null); - if (pos >= 0) { + if (pos >= 0) { ContentValues values = new ContentValues(); values.put(APN_ID, pos); resolver.insert(PREFERAPN_URI, values); @@ -1581,7 +1583,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } Cursor cursor = phone.getContext().getContentResolver().query( - PREFERAPN_URI, new String[] { "_id", "name", "apn" }, + PREFERAPN_URI, new String[] { "_id", "name", "apn" }, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); if (cursor != null) { @@ -1665,9 +1667,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * PDP context and notify us with PDP_CONTEXT_CHANGED. * But we should stop the network polling and prevent reset PDP. */ - Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); + Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); stopNetStatPoll(); - mIsPsRestricted = true; + mIsPsRestricted = true; break; case EVENT_PS_RESTRICT_DISABLED: @@ -1683,6 +1685,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (state == State.FAILED) { cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + mReregisterOnReconnectFailure = false; } trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index 4db8fc64f8c0..18e6375a1392 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -621,7 +621,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { if (isInterrogate()) { phone.mCM.queryFacilityLock(facility, password, serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); - } else if (isActivate() || isDeactivate()) { + } else if (isActivate() || isDeactivate()) { phone.mCM.setFacilityLock(facility, isActivate(), password, serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); } else { @@ -1186,9 +1186,9 @@ public final class GsmMmiCode extends Handler implements MmiCode { sb.append(createQueryCallWaitingResultMessage(ints[1])); } else if (isServiceCodeCallBarring(sc)) { // ints[0] for Call Barring is a bit vector of services - sb.append(createQueryCallBarringResultMessage(ints[0])); + sb.append(createQueryCallBarringResultMessage(ints[0])); } else if (ints[0] == 1) { - // for all other services, treat it as a boolean + // for all other services, treat it as a boolean sb.append(context.getText(com.android.internal.R.string.serviceEnabled)); } else { sb.append(context.getText(com.android.internal.R.string.mmiError)); @@ -1224,9 +1224,9 @@ public final class GsmMmiCode extends Handler implements MmiCode { { StringBuilder sb = new StringBuilder(context.getText(com.android.internal.R.string.serviceEnabledFor)); - for (int classMask = 1 + for (int classMask = 1 ; classMask <= SERVICE_CLASS_MAX - ; classMask <<= 1 + ; classMask <<= 1 ) { if ((classMask & serviceClass) != 0) { sb.append("\n"); @@ -1235,7 +1235,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { } return sb; } - + /*** * TODO: It would be nice to have a method here that can take in a dialstring and * figure out if there is an MMI code embedded within it. This code would replace diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 3e73cafda52a..2770ddc349ec 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -22,12 +22,14 @@ import android.app.PendingIntent.CanceledException; import android.content.Intent; import android.os.AsyncResult; import android.os.Message; +import android.provider.Telephony.Sms.Intents; import android.telephony.ServiceState; import android.util.Config; import android.util.Log; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.gsm.SmsMessage; +import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; @@ -39,8 +41,11 @@ import java.util.HashMap; final class GsmSMSDispatcher extends SMSDispatcher { private static final String TAG = "GSM"; + private GSMPhone mGsmPhone; + GsmSMSDispatcher(GSMPhone phone) { super(phone); + mGsmPhone = phone; } /** @@ -73,134 +78,61 @@ final class GsmSMSDispatcher extends SMSDispatcher { } } } - - if (mCm != null) { - mCm.acknowledgeLastIncomingSMS(true, 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; // Special case the message waiting indicator messages if (sms.isMWISetMessage()) { - ((GSMPhone) mPhone).updateMessageWaitingIndicator(true); - - if (sms.isMwiDontStore()) { - handled = true; - } - + mGsmPhone.updateMessageWaitingIndicator(true); + handled |= sms.isMwiDontStore(); if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator set SMS shouldStore=" - + !handled); + Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); } } else if (sms.isMWIClearMessage()) { - ((GSMPhone) mPhone).updateMessageWaitingIndicator(false); - - if (sms.isMwiDontStore()) { - handled = true; - } - + mGsmPhone.updateMessageWaitingIndicator(false); + handled |= sms.isMwiDontStore(); if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator clear SMS shouldStore=" - + !handled); + Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); } } if (handled) { - return; + return Intents.RESULT_SMS_HANDLED; } - // Parse the headers to see if this is partial, or port addressed - int referenceNumber = -1; - int count = 0; - int sequence = 0; - int destPort = -1; - - SmsHeader header = sms.getUserDataHeader(); - if (header != null) { - for (SmsHeader.Element element : header.getElements()) { - try { - switch (element.getID()) { - case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = data[0] & 0xff; - count = data[1] & 0xff; - sequence = data[2] & 0xff; - - // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); - count = data[2] & 0xff; - sequence = data[3] & 0xff; - - // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { - byte[] data = element.getData(); - - destPort = (data[0] & 0xff) << 8; - destPort |= (data[1] & 0xff); - - break; - } - } - } catch (ArrayIndexOutOfBoundsException e) { - Log.e(TAG, "Bad element in header", e); - return; // TODO: NACK the message or something, don't just discard. - } - } - } - - if (referenceNumber == -1) { - // notify everyone of the message if it isn't partial + SmsHeader smsHeader = sms.getUserDataHeader(); + // See if message is partial or port addressed. + if ((smsHeader == null) || (smsHeader.concatRef == null)) { + // Message is not partial (not part of concatenated sequence). byte[][] pdus = new byte[1][]; pdus[0] = sms.getPdu(); - if (destPort != -1) { - if (destPort == SmsHeader.PORT_WAP_PUSH) { - mWapPush.dispatchWapPdu(sms.getUserData()); + if (smsHeader != null && smsHeader.portAddrs != null) { + if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { + 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, destPort); } else { - // It's a normal message, dispatch it + // Normal short and non-port-addressed message, dispatch it. dispatchPdus(pdus); } + return Activity.RESULT_OK; } else { - // Process the message part - processMessagePart(sms, referenceNumber, sequence, count, destPort); + // Process the message part. + return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); } } @@ -208,28 +140,36 @@ final class GsmSMSDispatcher extends SMSDispatcher { protected void sendMultipartText(String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { - int ref = ++sConcatenatedRef & 0xff; - - for (int i = 0, count = parts.size(); i < count; i++) { - // build SmsHeader - byte[] data = new byte[3]; - data[0] = (byte) ref; // reference #, unique per message - data[1] = (byte) count; // total part count - data[2] = (byte) (i + 1); // 1-based sequence - SmsHeader header = new SmsHeader(); - header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; + int refNumber = getNextConcatenatedRef() & 0x00FF; + + for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = i + 1; // 1-based sequence + concatRef.msgCount = msgCount; + // TODO: We currently set this to true since our messaging app will never + // send more than 255 parts (it converts the message to MMS well before that). + // However, we should support 3rd party messaging apps that might need 16-bit + // references + // Note: It's not sufficient to just flip this bit to true; it will have + // ripple effects (several calculations assume 8-bit ref). + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + + PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } + + PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, header.toByteArray()); + parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); } @@ -239,7 +179,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { * Send a multi-part text based SMS which already passed SMS control check. * * It is the working function for sendMultipartText(). - * + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -259,18 +199,16 @@ final class GsmSMSDispatcher extends SMSDispatcher { * to the recipient. The raw pdu of the status report is in the * extended data ("pdu"). */ - private void sendMultipartTextWithPermit(String destinationAddress, + private void sendMultipartTextWithPermit(String destinationAddress, String scAddress, ArrayList<String> parts, - ArrayList<PendingIntent> sentIntents, + ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { - - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; - + // check if in service int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) { for (int i = 0, count = parts.size(); i < count; i++) { + PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } @@ -280,26 +218,29 @@ final class GsmSMSDispatcher extends SMSDispatcher { return; } - int ref = ++sConcatenatedRef & 0xff; - - for (int i = 0, count = parts.size(); i < count; i++) { - // build SmsHeader - byte[] data = new byte[3]; - data[0] = (byte) ref; // reference #, unique per message - data[1] = (byte) count; // total part count - data[2] = (byte) (i + 1); // 1-based sequence - SmsHeader header = new SmsHeader(); - header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); - + int refNumber = getNextConcatenatedRef() & 0x00FF; + + for (int i = 0, msgCount = parts.size(); i < msgCount; i++) { + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = i + 1; // 1-based sequence + concatRef.msgCount = msgCount; + concatRef.isEightBits = false; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + + PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } + + PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > i) { deliveryIntent = deliveryIntents.get(i); } SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, header.toByteArray()); + parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader)); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("smsc", pdus.encodedScAddress); @@ -307,7 +248,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); sendSms(tracker); - } + } } /** {@inheritDoc} */ @@ -324,33 +265,33 @@ final class GsmSMSDispatcher extends SMSDispatcher { /** * Send the multi-part SMS based on multipart Sms tracker - * + * * @param tracker holds the multipart Sms tracker ready to be sent */ protected void sendMultipartSms (SmsTracker tracker) { ArrayList<String> parts; ArrayList<PendingIntent> sentIntents; ArrayList<PendingIntent> deliveryIntents; - + HashMap map = tracker.mData; - + String destinationAddress = (String) map.get("destination"); String scAddress = (String) map.get("scaddress"); - + parts = (ArrayList<String>) map.get("parts"); sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); - - sendMultipartTextWithPermit(destinationAddress, + + sendMultipartTextWithPermit(destinationAddress, scAddress, parts, sentIntents, deliveryIntents); } /** {@inheritDoc} */ - protected void acknowledgeLastIncomingSms(boolean success, Message response){ + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ // FIXME unit test leaves cm == null. this should change if (mCm != null) { - mCm.acknowledgeLastIncomingSMS(success, response); + mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); } } @@ -375,5 +316,17 @@ final class GsmSMSDispatcher extends SMSDispatcher { response.recycle(); } + private int resultToCause(int rc) { + switch (rc) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + // Cause code is ignored on success. + return 0; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR; + } + } } - diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java index 9ab1002d646f..b3b434558945 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -16,7 +16,13 @@ package com.android.internal.telephony.gsm; -import com.android.internal.telephony.Phone; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -38,6 +44,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.provider.Telephony.Intents; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.Config; @@ -49,21 +56,11 @@ import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.IccCard; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.TelephonyEventLog; import com.android.internal.telephony.TelephonyIntents; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; - import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -73,6 +70,7 @@ import java.util.TimeZone; * {@hide} */ final class GsmServiceStateTracker extends ServiceStateTracker { + //***** Instance Variables GSMPhone phone; GsmCellLocation cellLoc; @@ -80,9 +78,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker { int mPreferredNetworkType; RestrictedState rs; - int rssi = 99; // signal strength 0-31, 99=unknown - // That's "received signal strength indication" fyi - private int gprsState = ServiceState.STATE_OUT_OF_SERVICE; private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE; @@ -121,7 +116,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { private boolean mStartedGprsRegCheck = false; // Already sent the event-log for no gprs register private boolean mReportedGprsNoReg = false; - + /** * The Notification object given to the NotificationManager. */ @@ -151,9 +146,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker { static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service - + // notification id - static final int PS_NOTIFICATION = 888; //id to update and cancel PS restricted + static final int PS_NOTIFICATION = 888; //id to update and cancel PS restricted static final int CS_NOTIFICATION = 999; //id to update and cancel CS restricted private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { @@ -177,6 +172,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { cellLoc = new GsmCellLocation(); newCellLoc = new GsmCellLocation(); rs = new RestrictedState(); + mSignalStrength = new SignalStrength(); PowerManager powerManager = (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); @@ -201,7 +197,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { cr.registerContentObserver( Settings.System.getUriFor(Settings.System.AUTO_TIME), true, mAutoTimeObserver); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mNeedToRegForSimLoaded = true; } @@ -280,7 +276,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { * @param obj placed in Message.obj */ /*protected*/ void registerForPsRestrictedEnabled(Handler h, int what, Object obj) { - Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled "); + Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled "); Registrant r = new Registrant(h, what, obj); psRestrictEnabledRegistrants.add(r); @@ -300,7 +296,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { * @param obj placed in Message.obj */ /*protected*/ void registerForPsRestrictedDisabled(Handler h, int what, Object obj) { - Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled "); + Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled "); Registrant r = new Registrant(h, what, obj); psRestrictDisabledRegistrants.add(r); @@ -308,7 +304,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { r.notifyRegistrant(); } } - + /*protected*/ void unregisterForPsRestrictedDisabled(Handler h) { psRestrictDisabledRegistrants.remove(h); } @@ -506,13 +502,13 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } mStartedGprsRegCheck = false; break; - + case EVENT_RESTRICTED_STATE_CHANGED: // This is a notification from // CommandsInterface.setOnRestrictedStateChanged Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_RESTRICTED_STATE_CHANGED"); - + ar = (AsyncResult) msg.obj; onRestrictedStateChanged(ar); @@ -541,12 +537,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker { (dcTracker.getAnyDataEnabled() ? 1 : 0) ); EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF, val); } - dcTracker.cleanConnectionBeforeRadioOff(); - + Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true + msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF; + dcTracker.sendMessage(msg); + // poll data state up to 15 times, with a 100ms delay // totaling 1.5 sec. Normal data disable action will finish in 100ms. for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) { - if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED + if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) { Log.d(LOG_TAG, "Data shutdown complete."); break; @@ -557,7 +556,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { cm.setRadioPower(false, null); } // Otherwise, we're in the desired state } - + protected void updateSpnDisplay() { int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric()); String spn = phone.mSIMRecords.getServiceProviderName(); @@ -699,9 +698,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } - private void - setRssiDefaultValues() { - rssi = 99; + private void setSignalStrengthDefaultValues() { + mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, true); } /** @@ -722,7 +720,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { case RADIO_UNAVAILABLE: newSS.setStateOutOfService(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; pollStateDone(); @@ -731,7 +729,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { case RADIO_OFF: newSS.setStateOff(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; pollStateDone(); @@ -745,10 +743,10 @@ final class GsmServiceStateTracker extends ServiceStateTracker { Log.d(LOG_TAG, "Radio Technology Change ongoing, setting SS to off"); newSS.setStateOff(); newCellLoc.setStateInvalid(); - setRssiDefaultValues(); + setSignalStrengthDefaultValues(); mGotCountryCode = false; - pollStateDone(); + //NOTE: pollStateDone() is not needed in this case break; default: @@ -1044,17 +1042,18 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } /** - * send signal-strength-changed notification if rssi changed + * send signal-strength-changed notification if changed * Called both for solicited and unsolicited signal stength updates */ private void onSignalStrengthResult(AsyncResult ar) { - int oldRSSI = rssi; + SignalStrength oldSignalStrength = mSignalStrength; + int rssi = 99; if (ar.exception != null) { - // 99 = unknown + // -1 = unknown // most likely radio is resetting/disconnected - rssi = 99; + setSignalStrengthDefaultValues(); } else { int[] ints = (int[])ar.result; @@ -1067,13 +1066,16 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } } - if (rssi != oldRSSI) { + mSignalStrength = new SignalStrength(rssi, -1, -1, -1, + -1, -1, -1, true); + + if (!mSignalStrength.equals(oldSignalStrength)) { try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after // POLL_PERIOD_MILLIS) during Radio Technology Change) phone.notifySignalStrength(); } catch (NullPointerException ex) { - Log.d(LOG_TAG, "onSignalStrengthResult() Phone already destroyed: " + ex - + "Signal Stranth not notified"); + log("onSignalStrengthResult() Phone already destroyed: " + ex + + "SignalStrength not notified"); } } } @@ -1089,27 +1091,27 @@ final class GsmServiceStateTracker extends ServiceStateTracker { { Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged"); RestrictedState newRs = new RestrictedState(); - + Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at enter "+ rs); - + if (ar.exception == null) { int[] ints = (int[])ar.result; int state = ints[0]; - + newRs.setCsEmergencyRestricted( ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); //ignore the normal call and data restricted state before SIM READY - if (phone.getIccCard().getState() == IccCard.State.READY) { + if (phone.getIccCard().getState() == IccCard.State.READY) { newRs.setCsNormalRestricted( ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); newRs.setPsRestricted( (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); } - - Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs); - + + Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs); + if (!rs.isPsRestricted() && newRs.isPsRestricted()) { psRestrictEnabledRegistrants.notifyRegistrants(); setNotification(PS_ENABLED); @@ -1117,9 +1119,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker { psRestrictDisabledRegistrants.notifyRegistrants(); setNotification(PS_DISABLED); } - + /** - * There are two kind of cs restriction, normal and emergency. So + * There are two kind of cs restriction, normal and emergency. So * there are 4 x 4 combinations in current and new restricted states * and we only need to notify when state is changed. */ @@ -1129,32 +1131,32 @@ final class GsmServiceStateTracker extends ServiceStateTracker { setNotification(CS_DISABLED); } else if (!newRs.isCsNormalRestricted()) { // remove normal restriction - setNotification(CS_EMERGENCY_ENABLED); + setNotification(CS_EMERGENCY_ENABLED); } else if (!newRs.isCsEmergencyRestricted()) { // remove emergency restriction - setNotification(CS_NORMAL_ENABLED); + setNotification(CS_NORMAL_ENABLED); } } else if (rs.isCsEmergencyRestricted() && !rs.isCsNormalRestricted()) { if (!newRs.isCsRestricted()) { // remove all restriction - setNotification(CS_DISABLED); + setNotification(CS_DISABLED); } else if (newRs.isCsRestricted()) { // enable all restriction setNotification(CS_ENABLED); } else if (newRs.isCsNormalRestricted()) { // remove emergency restriction and enable normal restriction - setNotification(CS_NORMAL_ENABLED); + setNotification(CS_NORMAL_ENABLED); } } else if (!rs.isCsEmergencyRestricted() && rs.isCsNormalRestricted()) { if (!newRs.isCsRestricted()) { // remove all restriction - setNotification(CS_DISABLED); + setNotification(CS_DISABLED); } else if (newRs.isCsRestricted()) { // enable all restriction setNotification(CS_ENABLED); } else if (newRs.isCsEmergencyRestricted()) { // remove normal restriction and enable emergency restriction - setNotification(CS_EMERGENCY_ENABLED); + setNotification(CS_EMERGENCY_ENABLED); } } else { if (newRs.isCsRestricted()) { @@ -1162,10 +1164,10 @@ final class GsmServiceStateTracker extends ServiceStateTracker { setNotification(CS_ENABLED); } else if (newRs.isCsEmergencyRestricted()) { // enable emergency restriction - setNotification(CS_EMERGENCY_ENABLED); + setNotification(CS_EMERGENCY_ENABLED); } else if (newRs.isCsNormalRestricted()) { // enable normal restriction - setNotification(CS_NORMAL_ENABLED); + setNotification(CS_NORMAL_ENABLED); } } @@ -1527,7 +1529,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { /** * Post a notification to NotificationManager for restricted state - * + * * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE */ private void setNotification(int notifyType) { @@ -1546,7 +1548,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { CharSequence details = ""; CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle); int notificationId = CS_NOTIFICATION; - + switch (notifyType) { case PS_ENABLED: notificationId = PS_NOTIFICATION; @@ -1557,24 +1559,24 @@ final class GsmServiceStateTracker extends ServiceStateTracker { break; case CS_ENABLED: details = context.getText(com.android.internal.R.string.RestrictedOnAll);; - break; + break; case CS_NORMAL_ENABLED: details = context.getText(com.android.internal.R.string.RestrictedOnNormal);; - break; + break; case CS_EMERGENCY_ENABLED: details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);; - break; + break; case CS_DISABLED: // do nothing and cancel the notification later - break; + break; } - + Log.d(LOG_TAG, "[DSAC DEB] " + "put notification " + title + " / " +details); mNotification.tickerText = title; - mNotification.setLatestEventInfo(context, title, details, + mNotification.setLatestEventInfo(context, title, details, mNotification.contentIntent); - - NotificationManager notificationManager = (NotificationManager) + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { @@ -1585,4 +1587,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker { notificationManager.notify(notificationId, mNotification); } } + + private void log(String s) { + Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java index 619897951e41..e18da56cb0ae 100644 --- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java +++ b/telephony/java/com/android/internal/telephony/gsm/MccTable.java @@ -182,7 +182,7 @@ public final class MccTable table.add(new MccEntry(222,"it",2,"Europe/Rome","it")); //Italy table.add(new MccEntry(225,"va",2,"Europe/Rome","it")); //Vatican City State table.add(new MccEntry(226,"ro",2)); //Romania - table.add(new MccEntry(228,"ch",2,"Europe/Zurich","en")); //Switzerland (Confederation of) + table.add(new MccEntry(228,"ch",2,"Europe/Zurich","de")); //Switzerland (Confederation of) table.add(new MccEntry(230,"cz",2,"Europe/Prague","cs")); //Czech Republic table.add(new MccEntry(231,"sk",2)); //Slovak Republic table.add(new MccEntry(232,"at",2,"Europe/Vienna","de")); //Austria diff --git a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java index 88acb1bfd1a2..55e5adc8ac66 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java @@ -35,32 +35,32 @@ public class PdpConnection extends DataConnection { private static final String LOG_TAG = "GSM"; private static final boolean DBG = true; - private static final boolean FAKE_FAIL = false; /** Fail cause of last PDP activate, from RIL_LastPDPActivateFailCause */ - private static final int PDP_FAIL_RIL_BARRED = 8; - private static final int PDP_FAIL_RIL_BAD_APN = 27; - private static final int PDP_FAIL_RIL_USER_AUTHENTICATION = 29; - private static final int PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUPPORTED = 32; - private static final int PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUBSCRIBED = 33; - private static final int PDP_FAIL_RIL_ERROR_UNSPECIFIED = 0xffff; + private static final int PDP_FAIL_OPERATOR_BARRED = 0x08; + private static final int PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A; + private static final int PDP_FAIL_MISSING_UKNOWN_APN = 0x1B; + private static final int PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; + private static final int PDP_FAIL_USER_AUTHENTICATION = 0x1D; + private static final int PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E; + private static final int PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F; + private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20; + private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; + private static final int PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22; + private static final int PDP_FAIL_NSAPI_IN_USE = 0x23; + private static final int PDP_FAIL_PROTOCOL_ERRORS = 0x6F; + private static final int PDP_FAIL_ERROR_UNSPECIFIED = 0xffff; + + private static final int PDP_FAIL_REGISTRATION_FAIL = -1; + private static final int PDP_FAIL_GPRS_REGISTRATION_FAIL = -2; //***** Instance Variables private String pdp_name; private ApnSetting apn; - // dataLink is only used to support pppd link - private DataLink dataLink; - //***** Constructor PdpConnection(GSMPhone phone) { super(phone); - this.dataLink = null; - - if (SystemProperties.get("ro.radio.use-ppp","no").equals("yes")) { - dataLink = new PppLink((GsmDataConnectionTracker) phone.mDataConnection, phone); - dataLink.setOnLinkChange(this, EVENT_LINK_STATE_CHANGED, null); - } } /** @@ -83,28 +83,20 @@ public class PdpConnection extends DataConnection { lastFailCause = FailCause.NONE; receivedDisconnectReq = false; - if (FAKE_FAIL) { - // for debug before baseband implement error in setup PDP - if (apn.apn.equalsIgnoreCase("badapn")){ - notifyFail(FailCause.BAD_APN, onConnectCompleted); - return; - } - } - phone.mCM.setupDataCall(Integer.toString(RILConstants.GSM_PHONE), null, apn.apn, apn.user, apn.password, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE)); } + private void tearDownData(Message msg) { + if (phone.mCM.getRadioState().isOn()) { + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); + } + } + protected void disconnect(Message msg) { onDisconnect = msg; if (state == State.ACTIVE) { - if (dataLink != null) { - dataLink.disconnect(); - } - - if (phone.mCM.getRadioState().isOn()) { - phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); - } + tearDownData(msg); } else if (state == State.ACTIVATING) { receivedDisconnectReq = true; } else { @@ -186,21 +178,51 @@ public class PdpConnection extends DataConnection { FailCause cause; switch (rilCause) { - case PDP_FAIL_RIL_BARRED: - cause = FailCause.BARRED; + case PDP_FAIL_OPERATOR_BARRED: + cause = FailCause.OPERATOR_BARRED; + break; + case PDP_FAIL_INSUFFICIENT_RESOURCES: + cause = FailCause.INSUFFICIENT_RESOURCES; break; - case PDP_FAIL_RIL_BAD_APN: - cause = FailCause.BAD_APN; + case PDP_FAIL_MISSING_UKNOWN_APN: + cause = FailCause.MISSING_UKNOWN_APN; break; - case PDP_FAIL_RIL_USER_AUTHENTICATION: + case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE: + cause = FailCause.UNKNOWN_PDP_ADDRESS; + break; + case PDP_FAIL_USER_AUTHENTICATION: cause = FailCause.USER_AUTHENTICATION; break; - case PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUPPORTED: + case PDP_FAIL_ACTIVATION_REJECT_GGSN: + cause = FailCause.ACTIVATION_REJECT_GGSN; + break; + case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED: + cause = FailCause.ACTIVATION_REJECT_UNSPECIFIED; + break; + case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER: + cause = FailCause.SERVICE_OPTION_OUT_OF_ORDER; + break; + case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED: cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; break; - case PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUBSCRIBED: + case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED: cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; break; + case PDP_FAIL_NSAPI_IN_USE: + cause = FailCause.NSAPI_IN_USE; + break; + case PDP_FAIL_PROTOCOL_ERRORS: + cause = FailCause.PROTOCOL_ERRORS; + break; + case PDP_FAIL_ERROR_UNSPECIFIED: + cause = FailCause.UNKNOWN; + break; + case PDP_FAIL_REGISTRATION_FAIL: + cause = FailCause.REGISTRATION_FAIL; + break; + case PDP_FAIL_GPRS_REGISTRATION_FAIL: + cause = FailCause.GPRS_REGISTRATION_FAIL; + break; default: cause = FailCause.UNKNOWN; } @@ -243,7 +265,7 @@ public class PdpConnection extends DataConnection { // Don't bother reporting success if there's already a // pending disconnect request, since DataConnectionTracker // has already updated its state. - disconnect(onDisconnect); + tearDownData(onDisconnect); } else { String[] response = ((String[]) ar.result); cid = Integer.parseInt(response[0]); @@ -278,11 +300,7 @@ public class PdpConnection extends DataConnection { } } - if (dataLink != null) { - dataLink.connect(); - } else { - onLinkStateChanged(DataLink.LinkState.LINK_UP); - } + onLinkStateChanged(DataLink.LinkState.LINK_UP); if (DBG) log("PDP setup on cid = " + cid); } diff --git a/telephony/java/com/android/internal/telephony/gsm/PppLink.java b/telephony/java/com/android/internal/telephony/gsm/PppLink.java deleted file mode 100644 index 96276968add1..000000000000 --- a/telephony/java/com/android/internal/telephony/gsm/PppLink.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm; - -import android.database.Cursor; -import android.os.Message; -import android.os.SystemProperties; -import android.os.SystemService; -import android.util.Log; - -import com.android.internal.telephony.DataLink; -import com.android.internal.telephony.DataConnectionTracker.State; -import com.android.internal.telephony.PhoneBase; -import com.android.internal.util.ArrayUtils; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.RandomAccessFile; - -/** - * Represents a PPP link. - * - * Ideally this would be managed by the RIL implementation, but - * we currently have implementations where this is not the case. - * - * {@hide} - */ -final class PppLink extends DataLink { - private static final String LOG_TAG = "GSM"; - - static final String PATH_PPP_OPERSTATE = "/sys/class/net/ppp0/operstate"; - static final String SERVICE_PPPD_GPRS = "pppd_gprs"; - static final String PROPERTY_PPPD_EXIT_CODE = "net.gprs.ppp-exit"; - static final int POLL_SYSFS_MILLIS = 5 * 1000; - static final int EVENT_POLL_DATA_CONNECTION = 2; - static final int EVENT_PPP_OPERSTATE_CHANGED = 8; - static final int EVENT_PPP_PIDFILE_CHANGED = 9; - - static final byte[] UP_ASCII_STRING = new byte[] { - 'u' & 0xff, - 'p' & 0xff, - }; - static final byte[] DOWN_ASCII_STRING = new byte[] { - 'd' & 0xff, - 'o' & 0xff, - 'w' & 0xff, - 'n' & 0xff, - }; - static final byte[] UNKNOWN_ASCII_STRING = new byte[] { - 'u' & 0xff, - 'n' & 0xff, - 'k' & 0xff, - 'n' & 0xff, - 'o' & 0xff, - 'w' & 0xff, - 'n' & 0xff, - }; - private final byte[] mCheckPPPBuffer = new byte[32]; - - private PhoneBase phone; - - int lastPppdExitCode = EXIT_OK; - - - PppLink(GsmDataConnectionTracker dc, GSMPhone p) { - super(dc); - this.phone = p; - } - - public void connect() { - // Clear any previous exit code - SystemProperties.set(PROPERTY_PPPD_EXIT_CODE, ""); - SystemService.start(SERVICE_PPPD_GPRS); - removeMessages(EVENT_POLL_DATA_CONNECTION); - Message poll = obtainMessage(); - poll.what = EVENT_POLL_DATA_CONNECTION; - sendMessageDelayed(poll, POLL_SYSFS_MILLIS); - } - - public void disconnect() { - SystemService.stop(SERVICE_PPPD_GPRS); - } - - public int getLastLinkExitCode() { - return lastPppdExitCode; - } - - public void setPasswordInfo(Cursor cursor) { - StringBuilder builder = new StringBuilder(); - FileOutputStream output = null; - - try { - output = new FileOutputStream("/etc/ppp/pap-secrets"); - if (cursor.moveToFirst()) { - do { - builder.append(cursor.getString(cursor.getColumnIndex("user"))); - builder.append(" "); - builder.append(cursor.getString(cursor.getColumnIndex("server"))); - builder.append(" "); - builder.append(cursor.getString(cursor.getColumnIndex("password"))); - builder.append("\n"); - } while (cursor.moveToNext()); - } - - output.write(builder.toString().getBytes()); - } catch (java.io.IOException e) { - Log.e(LOG_TAG, "Could not create '/etc/ppp/pap-secrets'", e); - } finally { - try { - if (output != null) output.close(); - } catch (java.io.IOException e) { - Log.e(LOG_TAG, "Error closing '/etc/ppp/pap-secrets'", e); - } - } - } - - public void handleMessage (Message msg) { - - switch (msg.what) { - - case EVENT_POLL_DATA_CONNECTION: - checkPPP(); - - // keep polling in case interface goes down - if (dataConnection.getState() != State.IDLE) { - Message poll = obtainMessage(); - poll.what = EVENT_POLL_DATA_CONNECTION; - sendMessageDelayed(poll, POLL_SYSFS_MILLIS); - } - break; - } - } - - private void checkPPP() { - boolean connecting = (dataConnection.getState() == State.CONNECTING); - - try { - RandomAccessFile file = new RandomAccessFile(PATH_PPP_OPERSTATE, "r"); - file.read(mCheckPPPBuffer); - file.close(); - - // Unfortunately, we're currently seeing operstate - // "unknown" where one might otherwise expect "up" - if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length) - || ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING, - UNKNOWN_ASCII_STRING.length) - && dataConnection.getState() == State.CONNECTING) { - - Log.i(LOG_TAG, - "found ppp interface. Notifying GPRS connected"); - - if (mLinkChangeRegistrant != null) { - mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP); - } - - connecting = false; - } else if (dataConnection.getState() == State.CONNECTED - && ArrayUtils.equals(mCheckPPPBuffer, DOWN_ASCII_STRING, - DOWN_ASCII_STRING.length)) { - - Log.i(LOG_TAG, - "ppp interface went down. Reconnecting..."); - - if (mLinkChangeRegistrant != null) { - mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN); - } - } - } catch (IOException ex) { - if (! (ex instanceof FileNotFoundException)) { - Log.i(LOG_TAG, "Poll ppp0 ex " + ex.toString()); - } - - if (dataConnection.getState() == State.CONNECTED && - mLinkChangeRegistrant != null) { - mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN); - } - } - - // CONNECTING means pppd has started but negotiation is not complete - // If we're still CONNECTING here, check to see if pppd has - // already exited - if (connecting) { - String exitCode; - - exitCode = SystemProperties.get(PROPERTY_PPPD_EXIT_CODE, ""); - - if (!exitCode.equals("")) { - // pppd has exited. Let's figure out why - lastPppdExitCode = Integer.parseInt(exitCode); - - Log.d(LOG_TAG,"pppd exited with " + exitCode); - - if (mLinkChangeRegistrant != null) { - mLinkChangeRegistrant.notifyResult(LinkState.LINK_EXITED); - } - } - } - - } - - protected void log(String s) { - Log.d(LOG_TAG, "[PppLink] " + s); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/RestrictedState.java b/telephony/java/com/android/internal/telephony/gsm/RestrictedState.java index d17f134ead7e..3f7d5d75e5b9 100644 --- a/telephony/java/com/android/internal/telephony/gsm/RestrictedState.java +++ b/telephony/java/com/android/internal/telephony/gsm/RestrictedState.java @@ -19,7 +19,7 @@ package com.android.internal.telephony.gsm; import android.telephony.ServiceState; public class RestrictedState { - + /** * Set true to block packet data access due to restriction */ @@ -32,7 +32,7 @@ public class RestrictedState { * Set true to block emergency call due to restriction */ private boolean mCsEmergencyRestricted; - + public RestrictedState() { setPsRestricted(false); setCsNormalRestricted(false); @@ -80,11 +80,11 @@ public class RestrictedState { public boolean isPsRestricted() { return mPsRestricted; } - + public boolean isCsRestricted() { return mCsNormalRestricted && mCsEmergencyRestricted; } - + @Override public boolean equals (Object o) { RestrictedState s; @@ -107,7 +107,7 @@ public class RestrictedState { @Override public String toString() { String csString = "none"; - + if (mCsEmergencyRestricted && mCsNormalRestricted) { csString = "all"; } else if (mCsEmergencyRestricted && !mCsNormalRestricted) { @@ -115,7 +115,7 @@ public class RestrictedState { } else if (!mCsEmergencyRestricted && mCsNormalRestricted) { csString = "normal call"; } - + return "Restricted State CS: " + csString + " PS:" + mPsRestricted; } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index f9015d92dce2..e25de8147ee4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -67,7 +67,7 @@ public final class SIMRecords extends IccRecords { SpnOverride mSpnOverride; - + //***** Cached SIM State; cleared on channel close String imsi; @@ -92,7 +92,6 @@ public final class SIMRecords extends IccRecords { byte[] mEfCfis = null; - String spn; int spnDisplayCondition; // Numeric network codes listed in TS 51.011 EF[SPDI] ArrayList<String> spdiNetworks = null; @@ -204,7 +203,7 @@ public final class SIMRecords extends IccRecords { // -1 means no EF_SPN found; treat accordingly. spnDisplayCondition = -1; efMWIS = null; - efCPHS_MWI = null; + efCPHS_MWI = null; spdiNetworks = null; pnnHomeName = null; @@ -483,7 +482,7 @@ public final class SIMRecords extends IccRecords { return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); } - + /** * If the timezone is not already set, set it based on the MCC of the SIM. * @param mcc Mobile Country Code of the SIM @@ -492,7 +491,7 @@ public final class SIMRecords extends IccRecords { String timezone = SystemProperties.get(TIMEZONE_PROPERTY); if (timezone == null || timezone.length() == 0) { String zoneId = MccTable.defaultTimeZoneForMcc(mcc); - + if (zoneId != null && zoneId.length() > 0) { // Set time zone based on MCC AlarmManager alarm = @@ -1011,7 +1010,7 @@ public final class SIMRecords extends IccRecords { IccUtils.bytesToHexString(data)); mEfCfis = data; - + // Refer TS 51.011 Section 10.3.46 for the content description callForwardingEnabled = ((data[1] & 0x01) != 0); diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java new file mode 100644 index 000000000000..45f50bc49fb1 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java @@ -0,0 +1,133 @@ +/* + * 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.telephony.gsm; + +/** + * SmsBroadcastConfigInfo defines one configuration of Cell Broadcast + * Message (CBM) to be received by the ME + * + * fromServiceId - toServiceId defines a range of CBM message identifiers + * whose value is 0x0000 - 0xFFFF as defined in TS 23.041 9.4.1.2.2 for GMS + * and 9.4.4.2.2 for UMTS. All other values can be treated as empty + * CBM message ID. + * + * fromCodeScheme - toCodeScheme defines a range of CBM data coding schemes + * whose value is 0x00 - 0xFF as defined in TS 23.041 9.4.1.2.3 for GMS + * and 9.4.4.2.3 for UMTS. + * All other values can be treated as empty CBM data coding scheme. + * + * selected false means message types specified in <fromServiceId, toServiceId> + * and <fromCodeScheme, toCodeScheme>are not accepted, while true means accepted. + * + */ +public class SmsBroadcastConfigInfo { + private int fromServiceId; + private int toServiceId; + private int fromCodeScheme; + private int toCodeScheme; + private boolean selected; + + /** + * Initialize the object from rssi and cid. + */ + public SmsBroadcastConfigInfo(int fromId, int toId, int fromScheme, + int toScheme, boolean selected) { + setFromServiceId(fromId); + setToServiceId(toId); + setFromCodeScheme(fromScheme); + setToCodeScheme(toScheme); + this.setSelected(selected); + } + + /** + * @param fromServiceId the fromServiceId to set + */ + public void setFromServiceId(int fromServiceId) { + this.fromServiceId = fromServiceId; + } + + /** + * @return the fromServiceId + */ + public int getFromServiceId() { + return fromServiceId; + } + + /** + * @param toServiceId the toServiceId to set + */ + public void setToServiceId(int toServiceId) { + this.toServiceId = toServiceId; + } + + /** + * @return the toServiceId + */ + public int getToServiceId() { + return toServiceId; + } + + /** + * @param fromCodeScheme the fromCodeScheme to set + */ + public void setFromCodeScheme(int fromCodeScheme) { + this.fromCodeScheme = fromCodeScheme; + } + + /** + * @return the fromCodeScheme + */ + public int getFromCodeScheme() { + return fromCodeScheme; + } + + /** + * @param toCodeScheme the toCodeScheme to set + */ + public void setToCodeScheme(int toCodeScheme) { + this.toCodeScheme = toCodeScheme; + } + + /** + * @return the toCodeScheme + */ + public int getToCodeScheme() { + return toCodeScheme; + } + + /** + * @param selected the selected to set + */ + public void setSelected(boolean selected) { + this.selected = selected; + } + + /** + * @return the selected + */ + public boolean isSelected() { + return selected; + } + + @Override + public String toString() { + return "SmsBroadcastConfigInfo: Id [" + + getFromServiceId() + "," + getToServiceId() + "] Code [" + + getFromCodeScheme() + "," + getToCodeScheme() + "] " + + (isSelected() ? "ENABLED" : "DISABLED"); + } +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 867b719d9db7..f1207e4ad600 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -26,6 +26,7 @@ import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; @@ -250,6 +251,12 @@ public class SmsMessage extends SmsMessageBase{ // TP-Data-Coding-Scheme // Default encoding, uncompressed + // To test writing messages to the SIM card, change this value 0x00 + // to 0x12, which means "bits 1 and 0 contain message class, and the + // class is 2". Note that this takes effect for the sender. In other + // words, messages sent by the phone with this change will end up on + // the receiver's SIM card. You can then send messages to yourself + // (on a phone with this change) and they'll end up on the SIM card. bo.write(0x00); // (no TP-Validity-Period) @@ -330,9 +337,20 @@ public class SmsMessage extends SmsMessageBase{ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, short destinationPort, byte[] data, boolean statusReportRequested) { - if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) { + + SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs(); + portAddrs.destPort = destinationPort; + portAddrs.origPort = 0; + portAddrs.areEightBits = false; + + SmsHeader smsHeader = new SmsHeader(); + smsHeader.portAddrs = portAddrs; + + byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader); + + if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) { Log.e(LOG_TAG, "SMS data message may only contain " - + (MAX_USER_DATA_BYTES - 7) + " bytes"); + + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes"); return null; } @@ -348,21 +366,12 @@ public class SmsMessage extends SmsMessageBase{ // (no TP-Validity-Period) - // User data size - bo.write(data.length + 7); - - // User data header size - bo.write(0x06); // header is 6 octets + // Total size + bo.write(data.length + smsHeaderData.length + 1); - // User data header, indicating the destination port - bo.write(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT); // port - // addressing - // header - bo.write(0x04); // each port is 2 octets - bo.write((destinationPort >> 8) & 0xFF); // MSB of destination port - bo.write(destinationPort & 0xFF); // LSB of destination port - bo.write(0x00); // MSB of originating port - bo.write(0x00); // LSB of originating port + // User data header + bo.write(smsHeaderData.length); + bo.write(smsHeaderData, 0, smsHeaderData.length); // User data bo.write(data, 0, data.length); @@ -556,13 +565,14 @@ public class SmsMessage extends SmsMessageBase{ int offset = cur; int userDataLength = pdu[offset++] & 0xff; int headerSeptets = 0; + int userDataHeaderLength = 0; if (hasUserDataHeader) { - int userDataHeaderLength = pdu[offset++] & 0xff; + userDataHeaderLength = pdu[offset++] & 0xff; byte[] udh = new byte[userDataHeaderLength]; System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength); - userDataHeader = SmsHeader.parse(udh); + userDataHeader = SmsHeader.fromByteArray(udh); offset += userDataHeaderLength; int headerBits = (userDataHeaderLength + 1) * 8; @@ -571,19 +581,34 @@ public class SmsMessage extends SmsMessageBase{ mUserDataSeptetPadding = (headerSeptets * 7) - headerBits; } - /* - * Here we just create the user data length to be the remainder of - * the pdu minus the user data hearder. This is because the count - * could mean the number of uncompressed sepets if the userdata is - * encoded in 7-bit. - */ - userData = new byte[pdu.length - offset]; + int bufferLen; + if (dataInSeptets) { + /* + * Here we just create the user data length to be the remainder of + * the pdu minus the user data header, since userDataLength means + * the number of uncompressed sepets. + */ + bufferLen = pdu.length - offset; + } else { + /* + * userDataLength is the count of octets, so just subtract the + * user data header. + */ + bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0); + if (bufferLen < 0) { + bufferLen = 0; + } + } + + userData = new byte[bufferLen]; System.arraycopy(pdu, offset, userData, 0, userData.length); cur = offset; if (dataInSeptets) { // Return the number of septets - return userDataLength - headerSeptets; + int count = userDataLength - headerSeptets; + // If count < 0, return 0 (means UDL was probably incorrect) + return count < 0 ? 0 : count; } else { // Return the number of octets return userData.length; @@ -613,8 +638,6 @@ public class SmsMessage extends SmsMessageBase{ /** * Returns an object representing the user data headers * - * @return an object representing the user data headers - * * {@hide} */ SmsHeader getUserDataHeader() { @@ -717,6 +740,44 @@ public class SmsMessage extends SmsMessageBase{ } } + /** + * Calculate the number of septets needed to encode the message. + * + * @param msgBody the message to encode + * @param use7bitOnly ignore (but still count) illegal characters if true + * @return TextEncodingDetails + */ + public static TextEncodingDetails calculateLength(CharSequence msgBody, + boolean use7bitOnly) { + TextEncodingDetails ted = new TextEncodingDetails(); + try { + int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly); + ted.codeUnitCount = septets; + if (septets > MAX_USER_DATA_SEPTETS) { + ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; + ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER + - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); + } else { + ted.msgCount = 1; + ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets; + } + ted.codeUnitSize = ENCODING_7BIT; + } catch (EncodeException ex) { + int octets = msgBody.length() * 2; + ted.codeUnitCount = msgBody.length(); + if (octets > MAX_USER_DATA_BYTES) { + ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; + ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER + - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; + } else { + ted.msgCount = 1; + ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2; + } + ted.codeUnitSize = ENCODING_16BIT; + } + return ted; + } + /** {@inheritDoc} */ public int getProtocolIdentifier() { return protocolIdentifier; diff --git a/telephony/java/com/android/internal/telephony/gsm/SuppServiceNotification.java b/telephony/java/com/android/internal/telephony/gsm/SuppServiceNotification.java index 11ad52d183e3..e68655e76d0b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SuppServiceNotification.java +++ b/telephony/java/com/android/internal/telephony/gsm/SuppServiceNotification.java @@ -34,7 +34,7 @@ public class SuppServiceNotification { public int type; /** TS 27.007 7.17 "number" (MT only) */ public String number; - + static public final int MO_CODE_UNCONDITIONAL_CF_ACTIVE = 0; static public final int MO_CODE_SOME_CF_ACTIVE = 1; static public final int MO_CODE_CALL_FORWARDED = 2; @@ -44,7 +44,7 @@ public class SuppServiceNotification { static public final int MO_CODE_INCOMING_CALLS_BARRED = 6; static public final int MO_CODE_CLIR_SUPPRESSION_REJECTED = 7; static public final int MO_CODE_CALL_DEFLECTED = 8; - + static public final int MT_CODE_FORWARDED_CALL = 0; static public final int MT_CODE_CUG_CALL = 1; static public final int MT_CODE_CALL_ON_HOLD = 2; diff --git a/telephony/java/com/android/internal/telephony/gsm/package.html b/telephony/java/com/android/internal/telephony/gsm/package.html index 4b1cf99d5df4..fed8399f48a2 100755 --- a/telephony/java/com/android/internal/telephony/gsm/package.html +++ b/telephony/java/com/android/internal/telephony/gsm/package.html @@ -1,6 +1,6 @@ <HTML> <BODY> -Provides classes to control or read data from GSM phones. +Provides classes to control or read data from GSM phones. @hide </BODY> </HTML> diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java b/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java index c00272993259..58f1f97bb94b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java @@ -24,15 +24,15 @@ package com.android.internal.telephony.gsm.stk; public interface AppInterface { /* - * Intent's actions which are broadcasted by the Telephony once a new STK + * Intent's actions which are broadcasted by the Telephony once a new STK * proactive command, session end arrive. */ - public static final String STK_CMD_ACTION = + public static final String STK_CMD_ACTION = "android.intent.action.stk.command"; - public static final String STK_SESSION_END_ACTION = + public static final String STK_SESSION_END_ACTION = "android.intent.action.stk.session_end"; - - /* + + /* * Callback function from app to telephony to pass a result code and user's * input back to the SIM. */ @@ -44,20 +44,20 @@ public interface AppInterface { * implementation should support those. */ public static enum CommandType { - DISPLAY_TEXT(0x21), - GET_INKEY(0x22), - GET_INPUT(0x23), - LAUNCH_BROWSER(0x15), - PLAY_TONE(0x20), - REFRESH(0x01), - SELECT_ITEM(0x24), - SEND_SS(0x11), - SEND_USSD(0x12), - SEND_SMS(0x13), - SEND_DTMF(0x14), - SET_UP_EVENT_LIST(0x05), - SET_UP_IDLE_MODE_TEXT(0x28), - SET_UP_MENU(0x25), + DISPLAY_TEXT(0x21), + GET_INKEY(0x22), + GET_INPUT(0x23), + LAUNCH_BROWSER(0x15), + PLAY_TONE(0x20), + REFRESH(0x01), + SELECT_ITEM(0x24), + SEND_SS(0x11), + SEND_USSD(0x12), + SEND_SMS(0x13), + SEND_DTMF(0x14), + SET_UP_EVENT_LIST(0x05), + SET_UP_IDLE_MODE_TEXT(0x28), + SET_UP_MENU(0x25), SET_UP_CALL(0x10); private int mValue; @@ -72,7 +72,7 @@ public interface AppInterface { /** * Create a CommandType object. - * + * * @param value Integer value to be converted to a CommandType object. * @return CommandType object whose "Type of Command" value is {@code * value}. If no CommandType object has that value, null is diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java index 168361aead1d..e81ff984cbf7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java @@ -36,17 +36,17 @@ class CommandDetails extends ValueObject implements Parcelable { public ComprehensionTlvTag getTag() { return ComprehensionTlvTag.COMMAND_DETAILS; } - + CommandDetails() { } public boolean compareTo(CommandDetails other) { return (this.compRequired == other.compRequired && this.commandNumber == other.commandNumber && - this.commandQualifier == other.commandQualifier && + this.commandQualifier == other.commandQualifier && this.typeOfCommand == other.typeOfCommand); } - + public CommandDetails(Parcel in) { compRequired = true; commandNumber = in.readInt(); @@ -60,7 +60,7 @@ class CommandDetails extends ValueObject implements Parcelable { dest.writeInt(commandQualifier); } - public static final Parcelable.Creator<CommandDetails> CREATOR = + public static final Parcelable.Creator<CommandDetails> CREATOR = new Parcelable.Creator<CommandDetails>() { public CommandDetails createFromParcel(Parcel in) { return new CommandDetails(in); @@ -85,7 +85,7 @@ class DeviceIdentities extends ValueObject { } } -// Container class to hold icon identifier value. +// Container class to hold icon identifier value. class IconId extends ValueObject { int recordNumber; boolean selfExplanatory; @@ -95,7 +95,7 @@ class IconId extends ValueObject { } } -// Container class to hold item icon identifier list value. +// Container class to hold item icon identifier list value. class ItemsIconId extends ValueObject { int [] recordNumbers; boolean selfExplanatory; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java index a27c5823c2d8..3da652fe0340 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java @@ -158,7 +158,7 @@ class GetInputParams extends CommandParams { this.input = input; } - boolean setIcon(Bitmap icon) { + boolean setIcon(Bitmap icon) { if (icon != null && input != null) { input.icon = icon; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java index 06b36a455bef..bfde616639f5 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java @@ -635,7 +635,7 @@ class CommandParamsFactory extends Handler { /** * Processes SET_UP_EVENT_LIST proactive command from the SIM card. - * + * * @param cmdDet Command Details object retrieved. * @param ctlvs List of ComprehensionTlv objects following Command Details * object and Device Identities object within the proactive command diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java index 4f746ac3e3aa..ffde6a3fa6ec 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java @@ -33,7 +33,7 @@ class ComprehensionTlv { private int mLength; private int mValueIndex; private byte[] mRawValue; - + /** * Constructor. Private on purpose. Use * {@link #decodeMany(byte[], int) decodeMany} or @@ -165,9 +165,9 @@ class ComprehensionTlv { } else { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } - + return new ComprehensionTlv(tag, cr, length, data, curIndex); - + } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java b/telephony/java/com/android/internal/telephony/gsm/stk/Input.java index 1f0d9711b705..19f724bd3704 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Input.java @@ -21,13 +21,13 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Container class for STK GET INPUT, GET IN KEY commands parameters. + * Container class for STK GET INPUT, GET IN KEY commands parameters. * */ public class Input implements Parcelable { public String text; public String defaultText; - public Bitmap icon; + public Bitmap icon; public int minLen; public int maxLen; public boolean ucs2; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java b/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java index 40a6b378df8c..331f69d4d393 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.List; /** - * Container class for STK menu (SET UP MENU, SELECT ITEM) parameters. + * Container class for STK menu (SET UP MENU, SELECT ITEM) parameters. * */ public class Menu implements Parcelable { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java index 810afd2d201e..afd1bba98dab 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2006-2007 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -56,7 +56,7 @@ class GetInkeyInputResponseData extends ResponseData { private boolean mIsYesNo; private boolean mYesNoResponse; public String mInData; - + // GetInKey Yes/No response characters constants. protected static final byte GET_INKEY_YES = 0x01; protected static final byte GET_INKEY_NO = 0x00; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java index 62a778ed2b16..5425a4330c8c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java @@ -20,9 +20,9 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Class used to pass STK messages from telephony to application. Application + * Class used to pass STK messages from telephony to application. Application * should call getXXX() to get commands's specific values. - * + * */ public class StkCmdMessage implements Parcelable { // members diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java index f6e568549ef0..bd6bc8ff0208 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java @@ -30,7 +30,7 @@ public abstract class StkLog { Log.d("STK", className.substring(className.lastIndexOf('.') + 1) + ": " + msg); } - + public static void d(String caller, String msg) { if (!DEBUG) { return; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java index 3de14f001491..92680371478e 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java @@ -25,8 +25,6 @@ import android.os.Message; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.CommandsInterface; -import com.android.internal.telephony.EncodeException; -import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.gsm.SimCard; import com.android.internal.telephony.gsm.SIMFileHandler; import com.android.internal.telephony.gsm.SIMRecords; @@ -34,8 +32,6 @@ import com.android.internal.telephony.gsm.SIMRecords; import android.util.Config; import java.io.ByteArrayOutputStream; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; /** * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If @@ -119,7 +115,7 @@ public class StkService extends Handler implements AppInterface { // Class members private static SIMRecords mSimRecords; - + // Service members. private static StkService sInstance; private CommandsInterface mCmdIf; @@ -174,6 +170,9 @@ public class StkService extends Handler implements AppInterface { // Register for SIM ready event. mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null); + + mCmdIf.reportStkServiceIsRunning(null); + StkLog.d(this, "StkService: is running"); } public void dispose() { @@ -184,9 +183,6 @@ public class StkService extends Handler implements AppInterface { mCmdIf.unSetOnStkCallSetUp(this); this.removeCallbacksAndMessages(null); - - //removing instance - sInstance = null; } protected void finalize() { @@ -450,7 +446,7 @@ public class StkService extends Handler implements AppInterface { } /** - * Used for instantiating the Service from the GsmPhone constructor. + * Used for instantiating/updating the Service from the GsmPhone constructor. * * @param ci CommandsInterface object * @param sr SIMRecords object @@ -560,15 +556,15 @@ public class StkService extends Handler implements AppInterface { } private void handleCmdResponse(StkResponseMessage resMsg) { - // Make sure the response details match the last valid command. An invalid + // Make sure the response details match the last valid command. An invalid // response is a one that doesn't have a corresponding proactive command - // and sending it can "confuse" the baseband/ril. - // One reason for out of order responses can be UI glitches. For example, - // if the application launch an activity, and that activity is stored + // and sending it can "confuse" the baseband/ril. + // One reason for out of order responses can be UI glitches. For example, + // if the application launch an activity, and that activity is stored // by the framework inside the history stack. That activity will be - // available for relaunch using the latest application dialog - // (long press on the home button). Relaunching that activity can send - // the same command's result again to the StkService and can cause it to + // available for relaunch using the latest application dialog + // (long press on the home button). Relaunching that activity can send + // the same command's result again to the StkService and can cause it to // get out of sync with the SIM. if (!validateResponse(resMsg)) { return; @@ -621,7 +617,7 @@ public class StkService extends Handler implements AppInterface { mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null); // No need to send terminal response for SET UP CALL. The user's // confirmation result is send back using a dedicated ril message - // invoked by the CommandInterface call above. + // invoked by the CommandInterface call above. mCurrntCmd = null; return; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java b/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java index bbc925eca190..90cc6c19c52f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java @@ -20,7 +20,7 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Container class for PlayTone commands parameters. + * Container class for PlayTone commands parameters. * */ public class ToneSettings implements Parcelable { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java index 8c8f977c33c4..09a860eb6e1f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2006-2007 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -28,7 +28,7 @@ abstract class ValueParser { /** * Search for a Command Details object from a list. - * + * * @param ctlvs List of ComprehensionTlv objects used for search * @return An CtlvCommandDetails object found from the objects. If no * Command Details object is found, ResultException is thrown. @@ -53,7 +53,7 @@ abstract class ValueParser { /** * Search for a Device Identities object from a list. - * + * * @param ctlvs List of ComprehensionTlv objects used for search * @return An CtlvDeviceIdentities object found from the objects. If no * Command Details object is found, ResultException is thrown. @@ -77,7 +77,7 @@ abstract class ValueParser { /** * Retrieves Duration information from the Duration COMPREHENSION-TLV * object. - * + * * @param ctlv A Text Attribute COMPREHENSION-TLV object * @return A Duration object * @throws ResultException @@ -100,7 +100,7 @@ abstract class ValueParser { /** * Retrieves Item information from the COMPREHENSION-TLV object. - * + * * @param ctlv A Text Attribute COMPREHENSION-TLV object * @return An Item * @throws ResultException @@ -130,7 +130,7 @@ abstract class ValueParser { /** * Retrieves Item id information from the COMPREHENSION-TLV object. - * + * * @param ctlv A Text Attribute COMPREHENSION-TLV object * @return An Item id * @throws ResultException @@ -152,7 +152,7 @@ abstract class ValueParser { /** * Retrieves icon id from an Icon Identifier COMPREHENSION-TLV object - * + * * @param ctlv An Icon Identifier COMPREHENSION-TLV object * @return IconId instance * @throws ResultException @@ -175,7 +175,7 @@ abstract class ValueParser { /** * Retrieves item icons id from an Icon Identifier List COMPREHENSION-TLV * object - * + * * @param ctlv An Item Icon List Identifier COMPREHENSION-TLV object * @return ItemsIconId instance * @throws ResultException @@ -206,7 +206,7 @@ abstract class ValueParser { /** * Retrieves text attribute information from the Text Attribute * COMPREHENSION-TLV object. - * + * * @param ctlv A Text Attribute COMPREHENSION-TLV object * @return A list of TextAttribute objects * @throws ResultException @@ -266,7 +266,7 @@ abstract class ValueParser { /** * Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV * object. - * + * * @param ctlv An Alpha Identifier COMPREHENSION-TLV object * @return String corresponding to the alpha identifier * @throws ResultException @@ -290,7 +290,7 @@ abstract class ValueParser { /** * Retrieves text from the Text COMPREHENSION-TLV object, and decodes it * into a Java String. - * + * * @param ctlv A Text COMPREHENSION-TLV object * @return A Java String object decoded from the Text object * @throws ResultException diff --git a/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java b/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java index 25d2026dd3d0..b116c354c118 100644 --- a/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java +++ b/telephony/java/com/android/internal/telephony/test/ModelInterpreter.java @@ -75,7 +75,7 @@ class LineReader * Returns NULL on EOF */ - String + String getNextLine(boolean ctrlZ) { int i = 0; @@ -131,7 +131,7 @@ class InterpreterEx extends Exception String result; } -public class ModelInterpreter +public class ModelInterpreter implements Runnable, SimulatedRadioControl { static final int MAX_CALLS = 6; @@ -153,14 +153,14 @@ public class ModelInterpreter SimulatedGsmCallState simulatedCallState; HandlerThread mHandlerThread; - + int pausedResponseCount; Object pausedResponseMonitor = new Object(); //***** Events static final int PROGRESS_CALL_STATE = 1; - + //***** Constructor public @@ -181,7 +181,7 @@ public class ModelInterpreter ss.bind(sa); init(); - } + } private void init() @@ -190,7 +190,7 @@ public class ModelInterpreter mHandlerThread = new HandlerThread("ModelInterpreter"); mHandlerThread.start(); Looper looper = mHandlerThread.getLooper(); - simulatedCallState = new SimulatedGsmCallState(looper); + simulatedCallState = new SimulatedGsmCallState(looper); } //***** Runnable Implementation @@ -204,7 +204,7 @@ public class ModelInterpreter try { s = ss.accept(); } catch (java.io.IOException ex) { - Log.w(LOG_TAG, + Log.w(LOG_TAG, "IOException on socket.accept(); stopping", ex); return; } @@ -213,15 +213,15 @@ public class ModelInterpreter in = s.getInputStream(); out = s.getOutputStream(); } catch (java.io.IOException ex) { - Log.w(LOG_TAG, + Log.w(LOG_TAG, "IOException on accepted socket(); re-listening", ex); continue; } Log.i(LOG_TAG, "New connection accepted"); } - - + + lineReader = new LineReader (in); println ("Welcome"); @@ -271,14 +271,14 @@ public class ModelInterpreter //***** Instance Methods - + /** Start the simulated phone ringing */ public void triggerRing(String number) { synchronized (this) { boolean success; - + success = simulatedCallState.triggerRing(number); if (success) { @@ -307,10 +307,10 @@ public class ModelInterpreter */ public void setAutoProgressConnectingCall(boolean b) - { + { simulatedCallState.setAutoProgressConnectingCall(b); } - + public void setNextDialFailImmediately(boolean b) { @@ -321,7 +321,7 @@ public class ModelInterpreter { //FIXME implement } - + /** hangup ringing, dialing, or actuve calls */ public void @@ -373,7 +373,7 @@ public class ModelInterpreter public void triggerSsn(int a, int b) {} public void triggerIncomingUssd(String statusCode, String message) {} - + public void triggerIncomingSMS(String message) { @@ -386,7 +386,7 @@ public class ModelInterpreter // source address: +18005551212 pdu.append("918100551521F0"); - + // protocol ID and data coding scheme pdu.append("0000"); @@ -421,7 +421,7 @@ public class ModelInterpreter pausedResponseMonitor.notifyAll(); } } - } + } //***** Private Instance Methods @@ -429,11 +429,11 @@ public class ModelInterpreter onAnswer() throws InterpreterEx { boolean success; - + success = simulatedCallState.onAnswer(); if (!success) { - throw new InterpreterEx("ERROR"); + throw new InterpreterEx("ERROR"); } } @@ -445,7 +445,7 @@ public class ModelInterpreter success = simulatedCallState.onAnswer(); if (!success) { - throw new InterpreterEx("ERROR"); + throw new InterpreterEx("ERROR"); } finalResponse = "NO CARRIER"; @@ -471,12 +471,12 @@ public class ModelInterpreter throw new InterpreterEx("ERROR"); } } - + private void releaseHeldOrUDUB() throws InterpreterEx { boolean success; - + success = simulatedCallState.releaseHeldOrUDUB(); if (!success) { @@ -488,19 +488,19 @@ public class ModelInterpreter releaseActiveAcceptHeldOrWaiting() throws InterpreterEx { boolean success; - + success = simulatedCallState.releaseActiveAcceptHeldOrWaiting(); if (!success) { throw new InterpreterEx("ERROR"); } - } + } private void switchActiveAndHeldOrWaiting() throws InterpreterEx { boolean success; - + success = simulatedCallState.switchActiveAndHeldOrWaiting(); if (!success) { @@ -512,7 +512,7 @@ public class ModelInterpreter separateCall(int index) throws InterpreterEx { boolean success; - + success = simulatedCallState.separateCall(index); if (!success) { @@ -524,7 +524,7 @@ public class ModelInterpreter conference() throws InterpreterEx { boolean success; - + success = simulatedCallState.conference(); if (!success) { @@ -536,7 +536,7 @@ public class ModelInterpreter onDial(String command) throws InterpreterEx { boolean success; - + success = simulatedCallState.onDial(command.substring(1)); if (!success) { @@ -566,7 +566,7 @@ public class ModelInterpreter println("+CMGS: 1"); } - + void processLine (String line) throws InterpreterEx { @@ -645,8 +645,8 @@ public class ModelInterpreter } ***/ } - - void + + void println (String s) { synchronized(this) { @@ -663,7 +663,7 @@ public class ModelInterpreter } } - void + void print (String s) { synchronized(this) { @@ -714,8 +714,8 @@ public class ModelInterpreter {"+CIMI", "320720000000000\r"}, {"+CSCS=?", "+CSCS: (\"HEX\",\"UCS2\")\r"}, {"+CFUN?", "+CFUN: 1\r"}, - {"+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", - "+COPS: 0,0,\"Android\"\r" + {"+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", + "+COPS: 0,0,\"Android\"\r" + "+COPS: 0,1,\"Android\"\r" + "+COPS: 0,2,\"310995\"\r"}, {"+CREG?", "+CREG: 2,5, \"0113\", \"6614\"\r"}, diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 5c69017e0379..22adc19ef061 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -26,8 +26,9 @@ import android.util.Log; import com.android.internal.telephony.BaseCommands; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.gsm.CallFailCause; -import com.android.internal.telephony.gsm.PDPContextState; +import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; import com.android.internal.telephony.Phone; @@ -59,7 +60,7 @@ public final class SimulatedCommands extends BaseCommands private final static String SIM_PUK2_CODE = "87654321"; //***** Instance Variables - + SimulatedGsmCallState simulatedCallState; HandlerThread mHandlerThread; SimLockState mSimLockedState; @@ -79,7 +80,7 @@ public final class SimulatedCommands extends BaseCommands ArrayList<Message> pausedResponses = new ArrayList<Message>(); int nextCallFailCause = CallFailCause.NORMAL_CLEARING; - + //***** Constructor public @@ -88,9 +89,9 @@ public final class SimulatedCommands extends BaseCommands mHandlerThread = new HandlerThread("SimulatedCommands"); mHandlerThread.start(); Looper looper = mHandlerThread.getLooper(); - + simulatedCallState = new SimulatedGsmCallState(looper); - + setRadioState(RadioState.RADIO_OFF); mSimLockedState = INITIAL_LOCK_STATE; mSimLockEnabled = (mSimLockedState != SimLockState.NONE); @@ -353,11 +354,11 @@ public final class SimulatedCommands extends BaseCommands public void setSuppServiceNotifications(boolean enable, Message result) { resultSuccess(result, null); - + if (enable && mSsnNotifyOn) { Log.w(LOG_TAG, "Supp Service Notifications already enabled!"); } - + mSsnNotifyOn = enable; } @@ -465,7 +466,7 @@ public final class SimulatedCommands extends BaseCommands unimplemented(result); } - /** + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure @@ -479,13 +480,13 @@ public final class SimulatedCommands extends BaseCommands resultSuccess(result, simulatedCallState.getDriverCalls()); } else { //Log.i("GSM", "[SimCmds] getCurrentCalls: SIM not ready!"); - resultFail(result, + resultFail(result, new CommandException( CommandException.Error.RADIO_NOT_AVAILABLE)); } } - /** + /** * @deprecated */ public void getPDPContextList(Message result) { @@ -497,13 +498,13 @@ public final class SimulatedCommands extends BaseCommands * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj - * ar.result contains a List of PDPContextState + * ar.result contains a List of DataCallState */ public void getDataCallList(Message result) { - resultSuccess(result, new ArrayList<PDPContextState>(0)); + resultSuccess(result, new ArrayList<DataCallState>(0)); } - /** + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure @@ -520,7 +521,7 @@ public final class SimulatedCommands extends BaseCommands resultSuccess(result, null); } - /** + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure @@ -531,7 +532,7 @@ public final class SimulatedCommands extends BaseCommands resultSuccess(result, "012345678901234"); } - /** + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure @@ -542,7 +543,7 @@ public final class SimulatedCommands extends BaseCommands resultSuccess(result, "012345678901234"); } - /** + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure @@ -553,7 +554,7 @@ public final class SimulatedCommands extends BaseCommands resultSuccess(result, "99"); } - /** + /** * Hang up one individual connection. * returned message * retMsg.obj = AsyncResult ar @@ -566,7 +567,7 @@ public final class SimulatedCommands extends BaseCommands */ public void hangupConnection (int gsmIndex, Message result) { boolean success; - + success = simulatedCallState.onChld('1', (char)('0'+gsmIndex)); if (!success){ @@ -588,7 +589,7 @@ public final class SimulatedCommands extends BaseCommands */ public void hangupWaitingOrBackground (Message result) { boolean success; - + success = simulatedCallState.onChld('0', '\0'); if (!success){ @@ -600,7 +601,7 @@ public final class SimulatedCommands extends BaseCommands /** * 3GPP 22.030 6.5.5 - * "Releases all active calls (if any exist) and accepts + * "Releases all active calls (if any exist) and accepts * the other (held or waiting) call." * * ar.exception carries exception on failure @@ -609,7 +610,7 @@ public final class SimulatedCommands extends BaseCommands */ public void hangupForegroundResumeBackground (Message result) { boolean success; - + success = simulatedCallState.onChld('1', '\0'); if (!success){ @@ -621,7 +622,7 @@ public final class SimulatedCommands extends BaseCommands /** * 3GPP 22.030 6.5.5 - * "Places all active calls (if any exist) on hold and accepts + * "Places all active calls (if any exist) on hold and accepts * the other (held or waiting) call." * * ar.exception carries exception on failure @@ -630,7 +631,7 @@ public final class SimulatedCommands extends BaseCommands */ public void switchWaitingOrHoldingAndActive (Message result) { boolean success; - + success = simulatedCallState.onChld('2', '\0'); if (!success){ @@ -647,10 +648,10 @@ public final class SimulatedCommands extends BaseCommands * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure - */ + */ public void conference (Message result) { boolean success; - + success = simulatedCallState.onChld('3', '\0'); if (!success){ @@ -682,7 +683,7 @@ public final class SimulatedCommands extends BaseCommands /** * 3GPP 22.030 6.5.5 - * "Places all active calls on hold except call X with which + * "Places all active calls on hold except call X with which * communication shall be supported." */ public void separateConnection (int gsmIndex, Message result) { @@ -703,10 +704,10 @@ public final class SimulatedCommands extends BaseCommands * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure - */ + */ public void acceptCall (Message result) { boolean success; - + success = simulatedCallState.onAnswer(); if (!success){ @@ -716,15 +717,15 @@ public final class SimulatedCommands extends BaseCommands } } - /** + /** * also known as UDUB * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure - */ + */ public void rejectCall (Message result) { boolean success; - + success = simulatedCallState.onChld('0', '\0'); if (!success){ @@ -734,7 +735,7 @@ public final class SimulatedCommands extends BaseCommands } } - /** + /** * cause code returned as Integer in Message.obj.response * Returns integer cause code defined in TS 24.008 * Annex H or closest approximation. @@ -765,11 +766,11 @@ public final class SimulatedCommands extends BaseCommands public void getMute (Message result) {unimplemented(result);} - /** + /** * response.obj is an AsyncResult * response.obj.result is an int[2] - * response.obj.result[0] is received signal strength (0-31, 99) - * response.obj.result[1] is bit error rate (0-7, 99) + * response.obj.result[0] is received signal strength (0-31, 99) + * response.obj.result[1] is bit error rate (0-7, 99) * as defined in TS 27.007 8.5 */ public void getSignalStrength (Message result) { @@ -893,7 +894,7 @@ public final class SimulatedCommands extends BaseCommands * response.obj.result[0] is long alpha or null if unregistered * response.obj.result[1] is short alpha or null if unregistered * response.obj.result[2] is numeric or null if unregistered - */ + */ public void getOperator(Message result) { String[] ret = new String[3]; @@ -908,7 +909,7 @@ public final class SimulatedCommands extends BaseCommands * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure - */ + */ public void sendDtmf(char c, Message result) { resultSuccess(result, null); } @@ -932,10 +933,19 @@ public final class SimulatedCommands extends BaseCommands } /** + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + public void sendBurstDtmf(String dtmfString, Message result) { + resultSuccess(result, null); + } + + /** * smscPDU is smsc address in PDU form GSM BCD format prefixed * by a length byte (as expected by TS 27.005) or NULL for default SMSC * pdu is SMS in PDU format as an ASCII hex string - * less the SMSC address + * less the SMSC address */ public void sendSMS (String smscPDU, String pdu, Message result) {unimplemented(result);} @@ -1009,6 +1019,14 @@ public final class SimulatedCommands extends BaseCommands unimplemented(result); } + public void reportSmsMemoryStatus(boolean available, Message result) { + unimplemented(result); + } + + public void reportStkServiceIsRunning(Message result) { + resultSuccess(result, null); + } + private boolean isSimLocked() { if (mSimLockedState != SimLockState.NONE) { return true; @@ -1032,16 +1050,16 @@ public final class SimulatedCommands extends BaseCommands } - public void acknowledgeLastIncomingSMS(boolean success, Message result) { + public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) { unimplemented(result); } - public void acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) { unimplemented(result); } - /** - * parameters equivilient to 27.007 AT+CRSM command + /** + * parameters equivilient to 27.007 AT+CRSM command * response.obj will be an AsyncResult * response.obj.userObj will be a SimIoResult on success */ @@ -1052,7 +1070,7 @@ public final class SimulatedCommands extends BaseCommands /** * (AsyncResult)response.obj).result is an int[] with element [0] set to - * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned". + * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned". * * @param response is callback message */ @@ -1063,46 +1081,46 @@ public final class SimulatedCommands extends BaseCommands * response.obj will be a an int[2] * * response.obj[0] will be TS 27.007 +CLIR parameter 'n' - * 0 presentation indicator is used according to the subscription of the CLIR service - * 1 CLIR invocation - * 2 CLIR suppression + * 0 presentation indicator is used according to the subscription of the CLIR service + * 1 CLIR invocation + * 2 CLIR suppression * * response.obj[1] will be TS 27.007 +CLIR parameter 'm' - * 0 CLIR not provisioned - * 1 CLIR provisioned in permanent mode - * 2 unknown (e.g. no network, etc.) - * 3 CLIR temporary mode presentation restricted - * 4 CLIR temporary mode presentation allowed + * 0 CLIR not provisioned + * 1 CLIR provisioned in permanent mode + * 2 unknown (e.g. no network, etc.) + * 3 CLIR temporary mode presentation restricted + * 4 CLIR temporary mode presentation allowed */ public void getCLIR(Message result) {unimplemented(result);} - + /** * clirMode is one of the CLIR_* constants above * * response.obj is null */ - + public void setCLIR(int clirMode, Message result) {unimplemented(result);} /** * (AsyncResult)response.obj).result is an int[] with element [0] set to - * 0 for disabled, 1 for enabled. + * 0 for disabled, 1 for enabled. * * @param serviceClass is a sum of SERVICE_CLASS_* * @param response is callback message */ - + public void queryCallWaiting(int serviceClass, Message response) { unimplemented(response); } - + /** * @param enable is true to enable, false to disable * @param serviceClass is a sum of SERVICE_CLASS_* * @param response is callback message */ - + public void setCallWaiting(boolean enable, int serviceClass, Message response) { unimplemented(response); @@ -1111,9 +1129,9 @@ public final class SimulatedCommands extends BaseCommands /** * @param action is one of CF_ACTION_* * @param cfReason is one of CF_REASON_* - * @param serviceClass is a sum of SERVICE_CLASSS_* + * @param serviceClass is a sum of SERVICE_CLASSS_* */ - public void setCallForward(int action, int cfReason, int serviceClass, + public void setCallForward(int action, int cfReason, int serviceClass, String number, int timeSeconds, Message result) {unimplemented(result);} /** @@ -1121,14 +1139,14 @@ public final class SimulatedCommands extends BaseCommands * * ((AsyncResult)response.obj).result will be an array of * CallForwardInfo's - * + * * An array of length 0 means "disabled for all codes" */ public void queryCallForwardStatus(int cfReason, int serviceClass, String number, Message result) {unimplemented(result);} public void setNetworkSelectionModeAutomatic(Message result) {unimplemented(result);} - + public void exitEmergencyCallbackMode(Message result) {unimplemented(result);} public void setNetworkSelectionModeManual( String operatorNumeric, Message result) {unimplemented(result);} @@ -1155,7 +1173,7 @@ public final class SimulatedCommands extends BaseCommands public void getAvailableNetworks(Message result) {unimplemented(result);} public void getBasebandVersion (Message result) { - resultSuccess(result, "SimulatedCommands"); + resultSuccess(result, "SimulatedCommands"); } /** @@ -1167,7 +1185,7 @@ public final class SimulatedCommands extends BaseCommands public void triggerIncomingUssd(String statusCode, String message) { if (mUSSDRegistrant != null) { String[] result = {statusCode, message}; - mUSSDRegistrant.notifyResult(result); + mUSSDRegistrant.notifyResult(result); } } @@ -1215,7 +1233,7 @@ public final class SimulatedCommands extends BaseCommands //***** SimulatedRadioControl - + /** Start the simulated phone ringing */ public void triggerRing(String number) { @@ -1249,9 +1267,9 @@ public final class SimulatedCommands extends BaseCommands simulatedCallState.setNextDialFailImmediately(b); } - public void + public void setNextCallFailCause(int gsmCause) { - nextCallFailCause = gsmCause; + nextCallFailCause = gsmCause; } public void @@ -1319,7 +1337,7 @@ public final class SimulatedCommands extends BaseCommands private void unimplemented(Message result) { if (result != null) { - AsyncResult.forMessage(result).exception + AsyncResult.forMessage(result).exception = new RuntimeException("Unimplemented"); if (pausedResponseCount > 0) { @@ -1359,13 +1377,13 @@ public final class SimulatedCommands extends BaseCommands unimplemented(response); } - public void + public void getCDMASubscription(Message response) { Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); unimplemented(response); } - public void + public void setCdmaSubscription(int cdmaSubscriptionType, Message response) { Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); unimplemented(response); @@ -1403,7 +1421,7 @@ public final class SimulatedCommands extends BaseCommands * @param serviceClass is a sum of SERVICE_CLASS_* * @param response is callback message */ - public void setTTYModeEnabled(boolean enable, Message response) { + public void setTTYMode(int ttyMode, Message response) { Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); unimplemented(response); } @@ -1411,12 +1429,12 @@ public final class SimulatedCommands extends BaseCommands /** * Query the TTY mode for the CDMA phone * (AsyncResult)response.obj).result is an int[] with element [0] set to - * 0 for disabled, 1 for enabled. - * + * 0 for disabled, 1 for enabled. + * * @param serviceClass is a sum of SERVICE_CLASS_* * @param response is callback message */ - public void queryTTYModeEnabled(Message response) { + public void queryTTYMode(Message response) { Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); unimplemented(response); } @@ -1436,19 +1454,36 @@ public final class SimulatedCommands extends BaseCommands Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); } - public void activateCdmaBroadcastSms(int activate, Message result) { - // TODO Auto-generated method stub + public void setCdmaBroadcastActivation(boolean activate, Message response) { + unimplemented(response); } - public void getCdmaBroadcastConfig(Message result) { - // TODO Auto-generated method stub + public void getCdmaBroadcastConfig(Message response) { + unimplemented(response); } - public void setCdmaBroadcastConfig(int[] configValuesArray, Message result) { - // TODO Auto-generated method stub + public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) { + unimplemented(response); } + public void forceDataDormancy(Message response) { + unimplemented(response); + } + + + public void setGsmBroadcastActivation(boolean activate, Message response) { + unimplemented(response); + } + + + public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) { + unimplemented(response); + } + + public void getGsmBroadcastConfig(Message response) { + unimplemented(response); + } } diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java index 803735c5464c..c6c301d24b63 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java @@ -37,7 +37,7 @@ class CallInfo { WAITING(5); // MT call only State (int value) {this.value = value;} - + private final int value; public int value() {return value;}; }; @@ -73,7 +73,7 @@ class CallInfo { String toCLCCLine(int index) { - return + return "+CLCC: " + index + "," + (isMT ? "1" : "0") +"," + state.value() + ",0," + (isMpty ? "1" : "0") @@ -82,7 +82,7 @@ class CallInfo { DriverCall toDriverCall(int index) { - DriverCall ret; + DriverCall ret; ret = new DriverCall(); @@ -136,7 +136,7 @@ class SimulatedGsmCallState extends Handler { private boolean autoProgressConnecting = true; private boolean nextDialFailImmediately; - + //***** Event Constants @@ -169,8 +169,8 @@ class SimulatedGsmCallState extends Handler { //***** Public Methods - /** - * Start the simulated phone ringing + /** + * Start the simulated phone ringing * true if succeeded, false if failed */ public boolean @@ -185,7 +185,7 @@ class SimulatedGsmCallState extends Handler { if (call == null && empty < 0) { empty = i; - } else if (call != null + } else if (call != null && (call.state == CallInfo.State.INCOMING || call.state == CallInfo.State.WAITING) ) { @@ -208,7 +208,7 @@ class SimulatedGsmCallState extends Handler { if (isCallWaiting) { calls[empty].state = CallInfo.State.WAITING; } - + } return true; } @@ -225,11 +225,11 @@ class SimulatedGsmCallState extends Handler { if (autoProgressConnecting) { sendMessageDelayed( - obtainMessage(EVENT_PROGRESS_CALL_STATE, call), + obtainMessage(EVENT_PROGRESS_CALL_STATE, call), CONNECTING_PAUSE_MSEC); } break; - } else if (call != null + } else if (call != null && call.state == CallInfo.State.ALERTING ) { call.state = CallInfo.State.ACTIVE; @@ -246,7 +246,7 @@ class SimulatedGsmCallState extends Handler { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; - if (call != null && (call.state == CallInfo.State.DIALING + if (call != null && (call.state == CallInfo.State.DIALING || call.state == CallInfo.State.ALERTING) ) { call.state = CallInfo.State.ACTIVE; @@ -263,13 +263,13 @@ class SimulatedGsmCallState extends Handler { setAutoProgressConnectingCall(boolean b) { autoProgressConnecting = b; } - + public void setNextDialFailImmediately(boolean b) { nextDialFailImmediately = b; } - /** + /** * hangup ringing, dialing, or active calls * returns true if call was hung up, false if not */ @@ -283,7 +283,7 @@ class SimulatedGsmCallState extends Handler { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; - if (call != null + if (call != null && (call.state == CallInfo.State.INCOMING || call.state == CallInfo.State.WAITING) ) { @@ -295,7 +295,7 @@ class SimulatedGsmCallState extends Handler { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; - if (call != null + if (call != null && (call.state == CallInfo.State.DIALING || call.state == CallInfo.State.ACTIVE || call.state == CallInfo.State.ALERTING) @@ -308,7 +308,7 @@ class SimulatedGsmCallState extends Handler { } } - /** + /** * hangup holding calls * returns true if call was hung up, false if not */ @@ -330,7 +330,7 @@ class SimulatedGsmCallState extends Handler { } } - /** + /** * hangup all * returns true if call was hung up, false if not */ @@ -359,7 +359,7 @@ class SimulatedGsmCallState extends Handler { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; - if (call != null + if (call != null && (call.state == CallInfo.State.INCOMING || call.state == CallInfo.State.WAITING) ) { @@ -399,7 +399,7 @@ class SimulatedGsmCallState extends Handler { return false; } } - + switch (c0) { case '0': ret = releaseHeldOrUDUB(); @@ -442,7 +442,7 @@ class SimulatedGsmCallState extends Handler { return ret; } - + public boolean releaseHeldOrUDUB() { boolean found = false; @@ -493,8 +493,8 @@ class SimulatedGsmCallState extends Handler { for (int i = 0 ; i < calls.length ; i++) { CallInfo c = calls[i]; - if (c != null - && (c.state == CallInfo.State.DIALING + if (c != null + && (c.state == CallInfo.State.DIALING || c.state == CallInfo.State.ALERTING) ) { calls[i] = null; @@ -531,7 +531,7 @@ class SimulatedGsmCallState extends Handler { public boolean switchActiveAndHeldOrWaiting() { boolean hasHeld = false; - + // first, are there held calls? for (int i = 0 ; i < calls.length ; i++) { CallInfo c = calls[i]; @@ -595,7 +595,7 @@ class SimulatedGsmCallState extends Handler { } return true; - } catch (InvalidStateEx ex) { + } catch (InvalidStateEx ex) { return false; } } @@ -612,7 +612,7 @@ class SimulatedGsmCallState extends Handler { if (c != null) { countCalls++; - + if (c.isConnecting()) { return false; } @@ -624,7 +624,7 @@ class SimulatedGsmCallState extends Handler { if (c != null) { c.state = CallInfo.State.ACTIVE; if (countCalls > 0) { - c.isMpty = true; + c.isMpty = true; } } } @@ -659,12 +659,12 @@ class SimulatedGsmCallState extends Handler { int freeSlot = -1; Log.d("GSM", "SC> dial '" + address + "'"); - + if (nextDialFailImmediately) { nextDialFailImmediately = false; Log.d("GSM", "SC< dial fail (per request)"); - return false; + return false; } String phNum = PhoneNumberUtils.extractNetworkPortion(address); @@ -696,15 +696,15 @@ class SimulatedGsmCallState extends Handler { if (freeSlot < 0 && calls[i] == null) { freeSlot = i; } - + if (calls[i] != null && !calls[i].isActiveOrHeld()) { - // Can't make outgoing calls when there is a ringing or + // Can't make outgoing calls when there is a ringing or // connecting outgoing call Log.d("GSM", "SC< dial fail (invalid call state)"); return false; } else if (calls[i] != null && calls[i].state == CallInfo.State.ACTIVE) { // All active calls behome held - calls[i].state = CallInfo.State.HOLDING; + calls[i].state = CallInfo.State.HOLDING; } } @@ -717,7 +717,7 @@ class SimulatedGsmCallState extends Handler { if (autoProgressConnecting) { sendMessageDelayed( - obtainMessage(EVENT_PROGRESS_CALL_STATE, calls[freeSlot]), + obtainMessage(EVENT_PROGRESS_CALL_STATE, calls[freeSlot]), CONNECTING_PAUSE_MSEC); } @@ -776,7 +776,7 @@ class SimulatedGsmCallState extends Handler { if (call != null) { if (!hasMpty && call.isMpty) { mptyIsHeld = call.state == CallInfo.State.HOLDING; - } else if (call.isMpty && mptyIsHeld + } else if (call.isMpty && mptyIsHeld && call.state == CallInfo.State.ACTIVE ) { Log.e("ModelInterpreter", "Invalid state"); diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedRadioControl.java b/telephony/java/com/android/internal/telephony/test/SimulatedRadioControl.java index 9e1a7c528d24..054d37024ad6 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedRadioControl.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedRadioControl.java @@ -45,7 +45,7 @@ public interface SimulatedRadioControl /** see pauseResponses */ public void resumeResponses(); - + public void triggerSsn(int type, int code); /** Generates an incoming USSD message. */ diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java index d5e64596c731..6658fb073213 100644 --- a/test-runner/android/test/InstrumentationTestRunner.java +++ b/test-runner/android/test/InstrumentationTestRunner.java @@ -118,7 +118,8 @@ import java.util.List; * <b>To generate EMMA code coverage:</b> * -e coverage true * Note: this requires an emma instrumented build. By default, the code coverage results file - * will be saved as /sdcard/coverage.ec, unless overridden by coverageFile flag (see below) + * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see + * below) * <p/> * <b> To specify EMMA code coverage results file path:</b> * -e coverageFile /sdcard/myFile.ec @@ -218,6 +219,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu */ private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment"; /** + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * identifies the path to the generated code coverage file. + */ + private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath"; + /** * The test is starting. */ public static final int REPORT_VALUE_RESULT_START = 1; @@ -240,7 +246,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu */ public static final String REPORT_KEY_STACK = "stack"; - private static final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage.ec"; + // Default file name for code coverage + private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec"; private static final String LOG_TAG = "InstrumentationTestRunner"; @@ -456,14 +463,20 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private void generateCoverageReport() { // use reflection to call emma dump coverage method, to avoid // always statically compiling against emma jar - java.io.File coverageFile = new java.io.File(getCoverageFilePath()); + String coverageFilePath = getCoverageFilePath(); + java.io.File coverageFile = new java.io.File(coverageFilePath); try { Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", coverageFile.getClass(), boolean.class, boolean.class); dumpCoverageMethod.invoke(null, coverageFile, false, false); - + // output path to generated coverage file so it can be parsed by a test harness if + // needed + mResults.putString(REPORT_KEY_COVERAGE_PATH, coverageFilePath); + // also output a more user friendly msg + mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("Generated code coverage data to %s", coverageFilePath)); } catch (ClassNotFoundException e) { reportEmmaError("Is emma jar on classpath?", e); } catch (SecurityException e) { @@ -481,8 +494,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private String getCoverageFilePath() { if (mCoverageFilePath == null) { - return DEFAULT_COVERAGE_FILE_PATH; - } + return getTargetContext().getFilesDir().getAbsolutePath() + File.separator + + DEFAULT_COVERAGE_FILE_NAME; + } else { return mCoverageFilePath; } diff --git a/test-runner/android/test/MoreAsserts.java b/test-runner/android/test/MoreAsserts.java index 2e746440403c..9e0d018b0afb 100644 --- a/test-runner/android/test/MoreAsserts.java +++ b/test-runner/android/test/MoreAsserts.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.ArrayList; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -316,8 +317,11 @@ public final class MoreAsserts { */ public static void assertContentsInOrder( String message, Iterable<?> actual, Object... expected) { - Assert.assertEquals(message, - Arrays.asList(expected), Lists.newArrayList(actual)); + ArrayList actualList = new ArrayList(); + for (Object o : actual) { + actualList.add(o); + } + Assert.assertEquals(message, Arrays.asList(expected), actualList); } /** diff --git a/test-runner/android/test/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java index dded745d2d1f..2ea020ebc7ff 100644 --- a/test-runner/android/test/TestLocationProvider.java +++ b/test-runner/android/test/TestLocationProvider.java @@ -159,6 +159,9 @@ public class TestLocationProvider extends ILocationProvider.Stub { public void updateNetworkState(int state) { } + public void updateLocation(Location location) { + } + public boolean sendExtraCommand(String command, Bundle extras) { return false; } diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index bd39a14857e0..b83a44d6ac00 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -100,11 +101,21 @@ public class MockContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + throw new UnsupportedOperationException(); + } + + @Override public String getPackageResourcePath() { throw new UnsupportedOperationException(); } @Override + public File getSharedPrefsFile(String name) { + throw new UnsupportedOperationException(); + } + + @Override public String getPackageCodePath() { throw new UnsupportedOperationException(); } @@ -387,11 +398,8 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } - /** - * @hide - */ @Override - public float getApplicationScale() { - throw new UnsupportedOperationException(); + public boolean isRestricted() { + throw new UnsupportedOperationException(); } } diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java index bf1629fd48ff..d5cd6ef5c5ba 100644 --- a/test-runner/android/test/mock/MockPackageManager.java +++ b/test-runner/android/test/mock/MockPackageManager.java @@ -16,10 +16,10 @@ package android.test.mock; -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDeleteObserver; @@ -57,11 +57,10 @@ public class MockPackageManager extends PackageManager { } @Override - public Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException { + public Intent getLaunchIntentForPackage(String packageName) { throw new UnsupportedOperationException(); } - + @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { throw new UnsupportedOperationException(); @@ -327,13 +326,13 @@ public class MockPackageManager extends PackageManager { long idealStorageSize, IPackageDataObserver observer) { throw new UnsupportedOperationException(); } - + /** * @hide - to match hiding in superclass */ @Override public void freeStorage( - long idealStorageSize, PendingIntent onFinishedIntent) { + long idealStorageSize, IntentSender pi) { throw new UnsupportedOperationException(); } @@ -388,6 +387,16 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** + * @hide - to match hiding in superclass + */ + @Override + public void replacePreferredActivity(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity) { + throw new UnsupportedOperationException(); + } + + @Override public void clearPackagePreferredActivities(String packageName) { throw new UnsupportedOperationException(); diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index 843d844379ae..55d4d64bf547 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -206,6 +206,12 @@ <meta-data android:name="com.android.unit_tests.reference" android:resource="@xml/metadata" /> </provider> + <!-- Application components used for content tests --> + <provider android:name=".content.MemoryFileProvider" + android:authorities="com.android.unit_tests.content.MemoryFileProvider" + android:process=":MemoryFileProvider"> + </provider> + <!-- Application components used for os tests --> <service android:name=".os.MessengerService" @@ -213,7 +219,20 @@ </service> <!-- Application components used for search manager tests --> - <!-- TODO: Removed temporarily - need to be replaced using mocks --> + + <activity android:name=".SearchableActivity" + android:label="Searchable Activity"> + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable" /> + </activity> + + <provider android:name=".SuggestionProvider" + android:authorities="com.android.unit_tests.SuggestionProvider"> + </provider> <!-- Used to test IPC. --> <service android:name=".binder.BinderTestService" diff --git a/tests/AndroidTests/res/values/strings.xml b/tests/AndroidTests/res/values/strings.xml index 21c72cf8b93f..49d8ae72d964 100644 --- a/tests/AndroidTests/res/values/strings.xml +++ b/tests/AndroidTests/res/values/strings.xml @@ -50,5 +50,8 @@ <item quantity="other">Some dogs</item> </plurals> + <string name="searchable_label">SearchManager Test</string> + <string name="searchable_hint">A search hint</string> + <!-- <string name="layout_six_text_text">F</string> --> </resources> diff --git a/tests/AndroidTests/res/xml/searchable.xml b/tests/AndroidTests/res/xml/searchable.xml index a40d53de0511..9d293b5ac425 100644 --- a/tests/AndroidTests/res/xml/searchable.xml +++ b/tests/AndroidTests/res/xml/searchable.xml @@ -15,7 +15,12 @@ --> <searchable xmlns:android="http://schemas.android.com/apk/res/android" - android:label="SearchManagerTest" - android:hint="SearchManagerTest Hint" -/> + android:label="@string/searchable_label" + android:hint="@string/searchable_hint" + android:searchSuggestAuthority="com.android.unit_tests.SuggestionProvider" + > + <actionkey android:keycode="KEYCODE_CALL" + android:suggestActionMsgColumn="suggest_action_msg_call" /> + +</searchable>
\ No newline at end of file diff --git a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java index 3daa8abbde9b..fb1b9ad5bcff 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java @@ -507,7 +507,7 @@ public class AppCacheTest extends AndroidTestCase { try { // Spin lock waiting for call back synchronized(r) { - getPm().freeStorage(idealStorageSize, pi); + getPm().freeStorage(idealStorageSize, pi.getIntentSender()); long waitTime = 0; while(!r.isDone() && (waitTime < MAX_WAIT_TIME)) { r.wait(WAIT_TIME_INCR); diff --git a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java index a93524776d21..c5562b36b7e3 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/BitwiseStreamsTest.java @@ -25,6 +25,8 @@ import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import java.util.Random; + public class BitwiseStreamsTest extends AndroidTestCase { private final static String LOG_TAG = "BitwiseStreamsTest"; @@ -39,7 +41,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { BitwiseInputStream inStream = new BitwiseInputStream(outBuf); byte[] inBufDup = new byte[inBuf.length]; inStream.skip(offset); - for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8); + for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8); assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } @@ -53,7 +55,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray()); inStream.skip(offset); byte[] inBufDup = new byte[inBuf.length]; - for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8); + for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8); assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } @@ -67,7 +69,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray()); inStream.skip(offset); byte[] inBufDup = new byte[inBuf.length]; - for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8); + for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8); assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } @@ -84,12 +86,33 @@ public class BitwiseStreamsTest extends AndroidTestCase { BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray()); inStream.skip(offset); byte[] inBufDup = new byte[inBuf.length]; - for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8); + for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8); assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } @SmallTest public void testFive() throws Exception { + Random random = new Random(); + int iterations = 10000; + int[] sizeArr = new int[iterations]; + int[] valueArr = new int[iterations]; + BitwiseOutputStream outStream = new BitwiseOutputStream(iterations * 4); + for (int i = 0; i < iterations; i++) { + int x = random.nextInt(); + int size = (x & 0x07) + 1; + int value = x & (-1 >>> (32 - size)); + sizeArr[i] = size; + valueArr[i] = value; + outStream.write(size, value); + } + BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray()); + for (int i = 0; i < iterations; i++) { + assertEquals(valueArr[i], inStream.read(sizeArr[i])); + } + } + + @SmallTest + public void testSix() throws Exception { int num_runs = 10; long start = android.os.SystemClock.elapsedRealtime(); for (int run = 0; run < num_runs; run++) { @@ -104,7 +127,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray()); inStream.skip(offset); byte[] inBufDup = new byte[inBuf.length]; - for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = inStream.read(8); + for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8); assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } long end = android.os.SystemClock.elapsedRealtime(); diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index b3e88e13e3f6..f0ba5737d884 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -30,10 +30,12 @@ import android.test.suitebuilder.annotation.SmallTest; import java.util.Iterator; +import java.lang.Integer; + import android.util.Log; public class CdmaSmsTest extends AndroidTestCase { - private final static String LOG_TAG = "Cdma_Sms_Test"; + private final static String LOG_TAG = "CDMA"; @SmallTest public void testUserData7bitGsm() throws Exception { @@ -103,6 +105,24 @@ public class CdmaSmsTest extends AndroidTestCase { assertEquals(userData.msgEncoding, revBearerData.userData.msgEncoding); assertEquals(userData.payloadStr.length(), revBearerData.userData.numFields); assertEquals(userData.payloadStr, revBearerData.userData.payloadStr); + userData.payloadStr = "More @ testing\nis great^|^~woohoo"; + revBearerData = BearerData.decode(BearerData.encode(bearerData)); + assertEquals(userData.payloadStr, revBearerData.userData.payloadStr); + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = 0xEE; + concatRef.msgCount = 2; + concatRef.seqNumber = 2; + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + byte[] encodedHeader = SmsHeader.toByteArray(smsHeader); + userData.userDataHeader = smsHeader; + revBearerData = BearerData.decode(BearerData.encode(bearerData)); + assertEquals(userData.payloadStr, revBearerData.userData.payloadStr); + SmsHeader decodedHeader = revBearerData.userData.userDataHeader; + assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber); + assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount); + assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber); } @SmallTest @@ -136,6 +156,212 @@ public class CdmaSmsTest extends AndroidTestCase { } @SmallTest + public void testMonolithicOne() throws Exception { + String pdu = "0003200010010410168d2002010503060812011101590501c706069706180000000701c108" + + "01c00901800a01e00b01030c01c00d01070e05039acc13880f018011020566"; + BearerData bearerData = BearerData.decode(HexDump.hexStringToByteArray(pdu)); + assertEquals(bearerData.messageType, BearerData.MESSAGE_TYPE_SUBMIT); + assertEquals(bearerData.messageId, 1); + assertEquals(bearerData.priority, BearerData.PRIORITY_EMERGENCY); + assertEquals(bearerData.privacy, BearerData.PRIVACY_CONFIDENTIAL); + assertEquals(bearerData.userAckReq, true); + assertEquals(bearerData.readAckReq, true); + assertEquals(bearerData.deliveryAckReq, true); + assertEquals(bearerData.reportReq, false); + assertEquals(bearerData.numberOfMessages, 3); + assertEquals(bearerData.alert, BearerData.ALERT_HIGH_PRIO); + assertEquals(bearerData.language, BearerData.LANGUAGE_HEBREW); + assertEquals(bearerData.callbackNumber.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(bearerData.callbackNumber.numberMode, + CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(bearerData.callbackNumber.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(bearerData.callbackNumber.numberPlan, CdmaSmsAddress.NUMBERING_PLAN_UNKNOWN); + assertEquals(bearerData.callbackNumber.numberOfDigits, 7); + assertEquals(bearerData.callbackNumber.address, "3598271"); + assertEquals(bearerData.displayMode, BearerData.DISPLAY_MODE_USER); + assertEquals(bearerData.depositIndex, 1382); + assertEquals(bearerData.userResponseCode, 5); + assertEquals(bearerData.msgCenterTimeStamp.year, 2008); + assertEquals(bearerData.msgCenterTimeStamp.month, 11); + assertEquals(bearerData.msgCenterTimeStamp.monthDay, 1); + assertEquals(bearerData.msgCenterTimeStamp.hour, 11); + assertEquals(bearerData.msgCenterTimeStamp.minute, 1); + assertEquals(bearerData.msgCenterTimeStamp.second, 59); + assertEquals(bearerData.validityPeriodAbsolute, null); + assertEquals(bearerData.validityPeriodRelative, 193); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0); + assertEquals(bearerData.deferredDeliveryTimeRelative, 199); + assertEquals(bearerData.hasUserDataHeader, false); + assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII); + assertEquals(bearerData.userData.numFields, 2); + assertEquals(bearerData.userData.payloadStr, "hi"); + } + + @SmallTest + public void testMonolithicTwo() throws Exception { + String pdu = "0003200010010410168d200201050306081201110159050192060697061800000007013d0" + + "801c00901800a01e00b01030c01c00d01070e05039acc13880f018011020566"; + BearerData bearerData = BearerData.decode(HexDump.hexStringToByteArray(pdu)); + assertEquals(bearerData.messageType, BearerData.MESSAGE_TYPE_SUBMIT); + assertEquals(bearerData.messageId, 1); + assertEquals(bearerData.priority, BearerData.PRIORITY_EMERGENCY); + assertEquals(bearerData.privacy, BearerData.PRIVACY_CONFIDENTIAL); + assertEquals(bearerData.userAckReq, true); + assertEquals(bearerData.readAckReq, true); + assertEquals(bearerData.deliveryAckReq, true); + assertEquals(bearerData.reportReq, false); + assertEquals(bearerData.numberOfMessages, 3); + assertEquals(bearerData.alert, BearerData.ALERT_HIGH_PRIO); + assertEquals(bearerData.language, BearerData.LANGUAGE_HEBREW); + assertEquals(bearerData.callbackNumber.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(bearerData.callbackNumber.numberMode, + CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(bearerData.callbackNumber.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(bearerData.callbackNumber.numberPlan, CdmaSmsAddress.NUMBERING_PLAN_UNKNOWN); + assertEquals(bearerData.callbackNumber.numberOfDigits, 7); + assertEquals(bearerData.callbackNumber.address, "3598271"); + assertEquals(bearerData.displayMode, BearerData.DISPLAY_MODE_USER); + assertEquals(bearerData.depositIndex, 1382); + assertEquals(bearerData.userResponseCode, 5); + assertEquals(bearerData.msgCenterTimeStamp.year, 2008); + assertEquals(bearerData.msgCenterTimeStamp.month, 11); + assertEquals(bearerData.msgCenterTimeStamp.monthDay, 1); + assertEquals(bearerData.msgCenterTimeStamp.hour, 11); + assertEquals(bearerData.msgCenterTimeStamp.minute, 1); + assertEquals(bearerData.msgCenterTimeStamp.second, 59); + assertEquals(bearerData.validityPeriodAbsolute, null); + assertEquals(bearerData.validityPeriodRelative, 61); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0); + assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0); + assertEquals(bearerData.deferredDeliveryTimeRelative, 146); + assertEquals(bearerData.hasUserDataHeader, false); + assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII); + assertEquals(bearerData.userData.numFields, 2); + assertEquals(bearerData.userData.payloadStr, "hi"); + } + + @SmallTest + public void testUserDataHeaderConcatRefFeedback() throws Exception { + BearerData bearerData = new BearerData(); + bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; + bearerData.messageId = 55; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = 0xEE; + concatRef.msgCount = 2; + concatRef.seqNumber = 2; + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + byte[] encodedHeader = SmsHeader.toByteArray(smsHeader); + SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber); + assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount); + assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber); + assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits); + assertEquals(decodedHeader.portAddrs, null); + UserData userData = new UserData(); + userData.payloadStr = "User Data Header (UDH) feedback test"; + userData.userDataHeader = smsHeader; + bearerData.userData = userData; + byte[] encodedSms = BearerData.encode(bearerData); + BearerData revBearerData = BearerData.decode(encodedSms); + decodedHeader = revBearerData.userData.userDataHeader; + assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber); + assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount); + assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber); + assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits); + assertEquals(decodedHeader.portAddrs, null); + } + + @SmallTest + public void testUserDataHeaderIllegalConcatRef() throws Exception { + BearerData bearerData = new BearerData(); + bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; + bearerData.messageId = 55; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = 0x10; + concatRef.msgCount = 0; + concatRef.seqNumber = 2; + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + byte[] encodedHeader = SmsHeader.toByteArray(smsHeader); + SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef, null); + concatRef.isEightBits = false; + encodedHeader = SmsHeader.toByteArray(smsHeader); + decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef, null); + concatRef.msgCount = 1; + concatRef.seqNumber = 2; + encodedHeader = SmsHeader.toByteArray(smsHeader); + decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef, null); + concatRef.msgCount = 1; + concatRef.seqNumber = 0; + encodedHeader = SmsHeader.toByteArray(smsHeader); + decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef, null); + concatRef.msgCount = 2; + concatRef.seqNumber = 1; + encodedHeader = SmsHeader.toByteArray(smsHeader); + decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef.msgCount, 2); + assertEquals(decodedHeader.concatRef.seqNumber, 1); + } + + @SmallTest + public void testUserDataHeaderMixedFeedback() throws Exception { + BearerData bearerData = new BearerData(); + bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; + bearerData.messageId = 42; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = 0x34; + concatRef.msgCount = 5; + concatRef.seqNumber = 2; + concatRef.isEightBits = false; + SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs(); + portAddrs.destPort = 88; + portAddrs.origPort = 66; + portAddrs.areEightBits = false; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + smsHeader.portAddrs = portAddrs; + byte[] encodedHeader = SmsHeader.toByteArray(smsHeader); + SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader); + assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber); + assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount); + assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber); + assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits); + assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort); + assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort); + assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits); + UserData userData = new UserData(); + userData.payloadStr = "User Data Header (UDH) feedback test"; + userData.userDataHeader = smsHeader; + bearerData.userData = userData; + byte[] encodedSms = BearerData.encode(bearerData); + BearerData revBearerData = BearerData.decode(encodedSms); + decodedHeader = revBearerData.userData.userDataHeader; + assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber); + assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount); + assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber); + assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits); + assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort); + assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort); + assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits); + } + + @SmallTest public void testReplyOption() throws Exception { String pdu1 = "0003104090011648b6a794e0705476bf77bceae934fe5f6d94d87450080a0180"; BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1)); @@ -298,22 +524,6 @@ public class CdmaSmsTest extends AndroidTestCase { } @SmallTest - public void testMsgCenterTimeStampFeedback() throws Exception { - BearerData bearerData = new BearerData(); - bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; - bearerData.messageId = 0; - bearerData.hasUserDataHeader = false; - UserData userData = new UserData(); - userData.payloadStr = "test message center timestamp"; - bearerData.userData = userData; - bearerData.timeStamp = HexDump.hexStringToByteArray("112233445566"); - byte []encodedSms = BearerData.encode(bearerData); - BearerData revBearerData = BearerData.decode(encodedSms); - assertEquals(HexDump.toHexString(bearerData.timeStamp), - HexDump.toHexString(revBearerData.timeStamp)); - } - - @SmallTest public void testPrivacyIndicator() throws Exception { String pdu1 = "0003104090010c485f4194dfea34becf61b840090140"; BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1)); @@ -486,4 +696,15 @@ public class CdmaSmsTest extends AndroidTestCase { assertEquals(revBearerData.displayModeSet, true); assertEquals(revBearerData.displayMode, bearerData.displayMode); } + + @SmallTest + public void testIs91() throws Exception { + String pdu1 = "000320001001070c2039acc13880"; + BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1)); + assertEquals(bd1.callbackNumber.address, "3598271"); + String pdu4 = "000320001001080c283c314724b34e"; + BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4)); + assertEquals(bd4.userData.payloadStr, "ABCDEFG"); + } + } diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java index d775dc201526..0991e8ce2240 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java @@ -175,6 +175,7 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase assertEquals("+" + PHONE_NUMBER, number); c.close(); + /* c = mDatabase.query("phones", null, "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null); assertNotNull(c); @@ -183,6 +184,7 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase number = c.getString(c.getColumnIndexOrThrow("num")); assertEquals("+" + PHONE_NUMBER, number); c.close(); + */ c = mDatabase.query("phones", null, "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null); @@ -203,85 +205,97 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase c.close(); } + + private void phoneNumberCompare(String phone1, String phone2, boolean equal) + throws Exception { + String[] temporalPhoneNumbers = new String[2]; + temporalPhoneNumbers[0] = phone1; + temporalPhoneNumbers[1] = phone2; + + Cursor cursor = mDatabase.rawQuery( + "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", + temporalPhoneNumbers); + try { + assertNotNull(cursor); + assertTrue(cursor.moveToFirst()); + if (equal) { + assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2), + "equal", cursor.getString(0)); + } else { + assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2), + "not equal", cursor.getString(0)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + + private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception { + phoneNumberCompare(phone1, phone2, true); + } + + private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception { + phoneNumberCompare(phone1, phone2, false); + } + /** * Tests international matching issues for the PHONE_NUMBERS_EQUAL function. * * @throws Exception */ + @SmallTest public void testPhoneNumbersEqualInternationl() throws Exception { - Cursor c; - String[] phoneNumbers = new String[2]; + assertPhoneNumberEqual("1", "1"); + assertPhoneNumberEqual("123123", "123123"); + assertPhoneNumberNotEqual("123123", "923123"); + assertPhoneNumberNotEqual("123123", "123129"); + assertPhoneNumberNotEqual("123123", "1231234"); + assertPhoneNumberNotEqual("123123", "0123123"); + assertPhoneNumberEqual("650-253-0000", "6502530000"); + assertPhoneNumberEqual("650-253-0000", "650 253 0000"); + assertPhoneNumberEqual("650 253 0000", "6502530000"); + assertPhoneNumberEqual("+1 650-253-0000", "6502530000"); + assertPhoneNumberEqual("001 650-253-0000", "6502530000"); + assertPhoneNumberEqual("0111 650-253-0000", "6502530000"); // Russian trunk digit - phoneNumbers[0] = "+79161234567"; // globablly dialable number - phoneNumbers[1] = "89161234567"; // in-country dialable number - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("equal", c.getString(0)); - c.close(); + assertPhoneNumberEqual("+79161234567", "89161234567"); // French trunk digit - phoneNumbers[0] = "+33123456789"; // globablly dialable number - phoneNumbers[1] = "0123456789"; // in-country dialable number - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("equal", c.getString(0)); - c.close(); - + assertPhoneNumberEqual("+33123456789", "0123456789"); // Trunk digit for city codes in the Netherlands - phoneNumbers[0] = "+31771234567"; // globablly dialable number - phoneNumbers[1] = "0771234567"; // in-country dialable number - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("equal", c.getString(0)); - c.close(); + assertPhoneNumberEqual("+31771234567", "0771234567"); // Test broken caller ID seen on call from Thailand to the US - phoneNumbers[0] = "+66811234567"; // in address book - phoneNumbers[1] = "166811234567"; // came in from the network - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("equal", c.getString(0)); - c.close(); + assertPhoneNumberEqual("+66811234567", "166811234567"); // Test the same in-country number with different country codes - phoneNumbers[0] = "+33123456789"; - phoneNumbers[1] = "+1123456789"; - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("not equal", c.getString(0)); - c.close(); + assertPhoneNumberNotEqual("+33123456789", "+1123456789"); // Test one number with country code and the other without - phoneNumbers[0] = "5125551212"; - phoneNumbers[1] = "+15125551212"; - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("equal", c.getString(0)); - c.close(); + assertPhoneNumberEqual("5125551212", "+15125551212"); // Test two NANP numbers that only differ in the area code - phoneNumbers[0] = "5125551212"; - phoneNumbers[1] = "6505551212"; - c = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", - phoneNumbers); - assertTrue(c.moveToFirst()); - assertEquals("not equal", c.getString(0)); - c.close(); + assertPhoneNumberNotEqual("5125551212", "6505551212"); + + // Japanese phone numbers + assertPhoneNumberEqual("090-1234-5678", "+819012345678"); + assertPhoneNumberEqual("090(1234)5678", "+819012345678"); + assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678"); + + // Equador + assertPhoneNumberEqual("+593(800)123-1234", "8001231234"); + assertPhoneNumberEqual("+593-2-1234-123", "21234123"); + + // Two continuous 0 at the beginning of the phone string should not be + // treated as trunk prefix. + assertPhoneNumberNotEqual("008001231234", "8001231234"); + + // Confirm that the bug found before does not re-appear. + assertPhoneNumberNotEqual("080-1234-5678", "+819012345678"); } @MediumTest @@ -509,9 +523,14 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase Cursor c; mDatabase.execSQL("CREATE TABLE tokens (" + "token TEXT COLLATE unicode," + - "source INTEGER " + + "source INTEGER," + + "token_index INTEGER," + + "tag TEXT" + + ");"); + mDatabase.execSQL("CREATE TABLE tokens_no_index (" + + "token TEXT COLLATE unicode," + + "source INTEGER" + ");"); - String[] cols = new String[]{"token", "source"}; Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null)); @@ -523,60 +542,152 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null)); Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, - "SELECT _TOKENIZE('tokens', 1, 'some string ok', ' ')", null)); - + "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null)); + Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, + "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null)); + + Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, + "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null)); + Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, + "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null)); + // test Chinese String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca"); Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, - "SELECT _TOKENIZE('tokens', 1,'" + chinese + "', ' ')", null)); + "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null)); String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g"); Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, - "SELECT _TOKENIZE('tokens', 1, '" + icustr + "', ' ')", null)); + "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null)); - Assert.assertEquals(7, DatabaseUtils.longForQuery(mDatabase, + Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens;", null)); String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); key = DatabaseUtils.getHexCollationKey("Hjonneva"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); key = DatabaseUtils.getHexCollationKey("some string ok"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase, + "SELECT tag from tokens where token GLOB '" + key + "*'", null)); key = DatabaseUtils.getHexCollationKey("string"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase, + "SELECT tag from tokens where token GLOB '" + key + "*'", null)); key = DatabaseUtils.getHexCollationKey("ok"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); - + Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase, + "SELECT tag from tokens where token GLOB '" + key + "*'", null)); + + key = DatabaseUtils.getHexCollationKey("second field"); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase, + "SELECT tag from tokens where token GLOB '" + key + "*'", null)); + key = DatabaseUtils.getHexCollationKey("field"); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase, + "SELECT tag from tokens where token GLOB '" + key + "*'", null)); + key = DatabaseUtils.getHexCollationKey(chinese); String[] a = new String[1]; a[0] = key; Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token= ?", a)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token= ?", a)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token= ?", a)); a[0] += "*"; Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB ?", a)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB ?", a)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB ?", a)); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token= '" + key + "'", null)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token= '" + key + "'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token= '" + key + "'", null)); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5"); Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); + key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca"); + Log.d("DatabaseGeneralTest", "key = " + key); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT count(*) from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens where token GLOB '" + key + "*'", null)); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT token_index from tokens where token GLOB '" + key + "*'", null)); Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, "SELECT count(*) from tokens where token GLOB 'ab*'", null)); + + key = DatabaseUtils.getHexCollationKey("some string ok"); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null)); + Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null)); + + key = DatabaseUtils.getHexCollationKey("bar"); + Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, + "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null)); + Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase, + "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null)); } @MediumTest diff --git a/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java b/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java new file mode 100644 index 000000000000..2bdf1dd6122e --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/NeighboringCellInfoTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.unit_tests; + +import android.test.AndroidTestCase; +import android.telephony.NeighboringCellInfo; +import android.test. suitebuilder.annotation.SmallTest; + +public class NeighboringCellInfoTest extends AndroidTestCase { + @SmallTest + public void testConstructor() { + NeighboringCellInfo empty = new NeighboringCellInfo(); + assertEquals(NeighboringCellInfo.UNKNOWN_RSSI, empty.getRssi()); + assertEquals(NeighboringCellInfo.UNKNOWN_CID, empty.getCid()); + + int rssi = 31; + int cid = 0xffffffff; + NeighboringCellInfo max = new NeighboringCellInfo(rssi, cid); + assertEquals(rssi, max.getRssi()); + assertEquals(cid, max.getCid()); + } + + @SmallTest + public void testGetAndSet() { + int rssi = 16; + int cid = 0x12345678; + NeighboringCellInfo nc = new NeighboringCellInfo(); + nc.setRssi(rssi); + nc.setCid(cid); + assertEquals(rssi, nc.getRssi()); + assertEquals(cid, nc.getCid()); + } + + @SmallTest + public void testToString() { + NeighboringCellInfo empty = new NeighboringCellInfo(); + assertEquals("[/ at /]", empty.toString()); + + NeighboringCellInfo nc = new NeighboringCellInfo(16, 0x12345678); + assertEquals("[12345678 at 16]", nc.toString()); + } +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java index 360352b28882..9d44fd9e4071 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java @@ -69,10 +69,15 @@ public class SMSTest extends AndroidTestCase { SmsHeader header = sms.getUserDataHeader(); assertNotNull(header); - - Iterator<SmsHeader.Element> elements = header.getElements().iterator(); - assertNotNull(elements); - + assertNotNull(header.concatRef); + assertEquals(header.concatRef.refNumber, 42); + assertEquals(header.concatRef.msgCount, 2); + assertEquals(header.concatRef.seqNumber, 1); + assertEquals(header.concatRef.isEightBits, true); + assertNotNull(header.portAddrs); + assertEquals(header.portAddrs.destPort, 2948); + assertEquals(header.portAddrs.origPort, 9200); + assertEquals(header.portAddrs.areEightBits, false); pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F" + "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141" @@ -81,9 +86,15 @@ public class SMSTest extends AndroidTestCase { header = sms.getUserDataHeader(); assertNotNull(header); - - elements = header.getElements().iterator(); - assertNotNull(elements); + assertNotNull(header.concatRef); + assertEquals(header.concatRef.refNumber, 42); + assertEquals(header.concatRef.msgCount, 2); + assertEquals(header.concatRef.seqNumber, 2); + assertEquals(header.concatRef.isEightBits, true); + assertNotNull(header.portAddrs); + assertEquals(header.portAddrs.destPort, 2948); + assertEquals(header.portAddrs.origPort, 9200); + assertEquals(header.portAddrs.areEightBits, false); /* * UCS-2 encoded SMS diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java index f3c15422073b..c4f1ab6ad6b7 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java @@ -23,7 +23,10 @@ import android.app.ISearchManager; import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; +import android.server.search.SearchableInfo; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; @@ -37,12 +40,11 @@ import android.util.AndroidRuntimeException; * com.android.unit_tests/android.test.InstrumentationTestRunner */ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalActivity> { - - // If non-zero, enable a set of tests that start and stop the search manager. - // This is currently disabled because it's causing an unwanted jump from the unit test - // activity into the contacts activity. We'll put this back after we disable that jump. - private static final int TEST_SEARCH_START = 0; - + + private ComponentName SEARCHABLE_ACTIVITY = + new ComponentName("com.android.unit_tests", + "com.android.unit_tests.SearchableActivity"); + /* * Bug list of test ideas. * @@ -88,7 +90,30 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct super.setUp(); Activity testActivity = getActivity(); - mContext = (Context)testActivity; + mContext = testActivity; + } + + private ISearchManager getSearchManagerService() { + return ISearchManager.Stub.asInterface( + ServiceManager.getService(Context.SEARCH_SERVICE)); + } + + // Checks that the search UI is visible. + private void assertSearchVisible() { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertTrue("SearchManager thinks search UI isn't visible when it should be", + searchManager.isVisible()); + } + + // Checks that the search UI is not visible. + // This checks both the SearchManager and the SearchManagerService, + // since SearchManager keeps a local variable for the visibility. + private void assertSearchNotVisible() { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertFalse("SearchManager thinks search UI is visible when it shouldn't be", + searchManager.isVisible()); } /** @@ -97,29 +122,7 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct */ @MediumTest public void testSearchManagerInterfaceAvailable() { - ISearchManager searchManager1 = ISearchManager.Stub.asInterface( - ServiceManager.getService(Context.SEARCH_SERVICE)); - assertNotNull(searchManager1); - } - - /** - * The goal of this test is to confirm that we can *only* obtain a search manager - * interface from an Activity context. - */ - @MediumTest - public void testSearchManagerContextRestrictions() { - SearchManager searchManager1 = (SearchManager) - mContext.getSystemService(Context.SEARCH_SERVICE); - assertNotNull(searchManager1); - - Context applicationContext = mContext.getApplicationContext(); - // this should fail, because you can't get a SearchManager from a non-Activity context - try { - applicationContext.getSystemService(Context.SEARCH_SERVICE); - assertFalse("Shouldn't retrieve SearchManager from a non-Activity context", true); - } catch (AndroidRuntimeException e) { - // happy here - we should catch this. - } + assertNotNull(getSearchManagerService()); } /** @@ -135,38 +138,129 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct SearchManager searchManager2 = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); assertNotNull(searchManager2); - assertSame( searchManager1, searchManager2 ); + assertSame(searchManager1, searchManager2 ); } - + + @MediumTest + public void testSearchables() { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + SearchableInfo si; + + si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false); + assertNotNull(si); + assertFalse(searchManager.isDefaultSearchable(si)); + si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true); + assertNotNull(si); + assertTrue(searchManager.isDefaultSearchable(si)); + si = searchManager.getSearchableInfo(null, true); + assertNotNull(si); + assertTrue(searchManager.isDefaultSearchable(si)); + } + + /** + * Tests that rapid calls to start-stop-start doesn't cause problems. + */ + @MediumTest + public void testSearchManagerFastInvocations() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + + /** + * Tests that startSearch() is idempotent. + */ + @MediumTest + public void testStartSearchIdempotent() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + + /** + * Tests that stopSearch() is idempotent and can be called when the search UI is not visible. + */ + @MediumTest + public void testStopSearchIdempotent() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + /** * The goal of this test is to confirm that we can start and then * stop a simple search. */ - - @MediumTest - public void testSearchManagerInvocations() { + @MediumTest + public void testSearchManagerInvocations() throws Exception { SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); assertNotNull(searchManager); - - // TODO: make a real component name, or remove this need - final ComponentName cn = new ComponentName("", ""); - - if (TEST_SEARCH_START != 0) { - // These tests should simply run to completion w/o exceptions - searchManager.startSearch(null, false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("", false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("test search string", false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("test search string", true, cn, null, false); - searchManager.stopSearch(); - } - } + assertSearchNotVisible(); -} + // These tests should simply run to completion w/o exceptions + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch("", false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch("test search string", false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + + @MediumTest + public void testSearchDialogState() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + + Bundle searchState; + + // search dialog not visible, so no state should be stored + searchState = searchManager.saveSearchDialog(); + assertNull(searchState); + + searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false); + searchState = searchManager.saveSearchDialog(); + assertNotNull(searchState); + searchManager.stopSearch(); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java b/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java new file mode 100644 index 000000000000..53f40e992847 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests; + +import android.app.Activity; +import android.os.Bundle; + +public class SearchableActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java index bdf67ba86ac7..ecc8dfe90599 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java @@ -20,6 +20,7 @@ import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; @@ -71,19 +72,19 @@ public class SearchablesTest extends AndroidTestCase { * TODO: The metadata source needs to be mocked out because adding * searchability metadata via this test is causing it to leak into the * real system. So for now I'm just going to test for existence of the - * GoogleSearch app (which is searchable). + * GlobalSearch app (which is searchable). */ - public void testSearchableGoogleSearch() { + public void testSearchableGlobalSearch() { // test basic array & hashmap Searchables searchables = new Searchables(mContext); searchables.buildSearchableList(); // test linkage from another activity // TODO inject this via mocking into the package manager. - // TODO for now, just check for searchable GoogleSearch app (this isn't really a unit test) + // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test) ComponentName thisActivity = new ComponentName( - "com.android.googlesearch", - "com.android.googlesearch.GoogleSearch"); + "com.android.globalsearch", + "com.android.globalsearch.GlobalSearch"); SearchableInfo si = searchables.getSearchableInfo(thisActivity); assertNotNull(si); @@ -92,7 +93,7 @@ public class SearchablesTest extends AndroidTestCase { Context appContext = si.getActivityContext(mContext); assertNotNull(appContext); MoreAsserts.assertNotEqual(appContext, mContext); - assertEquals("Google Search", appContext.getString(si.getHintId())); + assertEquals("Android Search", appContext.getString(si.getHintId())); assertEquals("Google", appContext.getString(si.getLabelId())); } @@ -306,6 +307,14 @@ public class SearchablesTest extends AndroidTestCase { throws PackageManager.NameNotFoundException { return mRealContext.createPackageContext(packageName, flags); } + + /** + * Message broadcast. Pass through for now. + */ + @Override + public void sendBroadcast(Intent intent) { + mRealContext.sendBroadcast(intent); + } } /** @@ -361,7 +370,8 @@ public class SearchablesTest extends AndroidTestCase { @Override public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { assertNotNull(intent); - assertEquals(intent.getAction(), Intent.ACTION_SEARCH); + assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) + || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.queryIntentActivities(intent, flags); @@ -375,7 +385,8 @@ public class SearchablesTest extends AndroidTestCase { @Override public ResolveInfo resolveActivity(Intent intent, int flags) { assertNotNull(intent); - assertEquals(intent.getAction(), SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH) + || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.resolveActivity(intent, flags); @@ -438,6 +449,29 @@ public class SearchablesTest extends AndroidTestCase { throw new UnsupportedOperationException(); } } + + /** + * Get the activity information for a particular activity. + * + * @param name The name of the activity to find. + * @param flags Additional option flags. + * + * @return ActivityInfo Information about the activity, if found, else null. + */ + @Override + public ActivityInfo getActivityInfo(ComponentName name, int flags) + throws NameNotFoundException { + assertNotNull(name); + MoreAsserts.assertNotEqual(name, ""); + switch (mSearchablesMode) { + case SEARCHABLES_PASSTHROUGH: + return mRealPackageManager.getActivityInfo(name, flags); + case SEARCHABLES_MOCK_ZERO: + throw new NameNotFoundException(); + default: + throw new UnsupportedOperationException(); + } + } } } diff --git a/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java b/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java new file mode 100644 index 000000000000..bc61e27b10df --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests; + +import android.app.SearchManager; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Intent; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; + +/** Simple test provider that runs in the local process. + * + * Used by {@link SearchManagerTest}. + */ +public class SuggestionProvider extends ContentProvider { + private static final String TAG = "SuggestionProvider"; + + private static final int SEARCH_SUGGESTIONS = 1; + + private static final UriMatcher sURLMatcher = new UriMatcher( + UriMatcher.NO_MATCH); + + static { + sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY, + SEARCH_SUGGESTIONS); + sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*", + SEARCH_SUGGESTIONS); + } + + private static final String[] COLUMNS = new String[] { + "_id", + SearchManager.SUGGEST_COLUMN_TEXT_1, + SearchManager.SUGGEST_COLUMN_INTENT_ACTION, + SearchManager.SUGGEST_COLUMN_QUERY + }; + + public SuggestionProvider() { + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri url, String[] projectionIn, String selection, + String[] selectionArgs, String sort) { + int match = sURLMatcher.match(url); + switch (match) { + case SEARCH_SUGGESTIONS: + String query = url.getLastPathSegment(); + MatrixCursor cursor = new MatrixCursor(COLUMNS); + String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" }; + for (String suffix : suffixes) { + addRow(cursor, query + suffix); + } + return cursor; + default: + throw new IllegalArgumentException("Unknown URL: " + url); + } + } + + private void addRow(MatrixCursor cursor, String string) { + long id = cursor.getCount(); + cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string); + } + + @Override + public String getType(Uri url) { + int match = sURLMatcher.match(url); + switch (match) { + case SEARCH_SUGGESTIONS: + return SearchManager.SUGGEST_MIME_TYPE; + default: + throw new IllegalArgumentException("Unknown URL: " + url); + } + } + + @Override + public int update(Uri url, ContentValues values, String where, String[] whereArgs) { + throw new UnsupportedOperationException("update not supported"); + } + + @Override + public Uri insert(Uri url, ContentValues initialValues) { + throw new UnsupportedOperationException("insert not supported"); + } + + @Override + public int delete(Uri url, String where, String[] whereArgs) { + throw new UnsupportedOperationException("delete not supported"); + } +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java index 51e841cb8d3a..7720041fccca 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java @@ -276,6 +276,7 @@ public class TextUtilsTest extends TestCase { Spannable s3 = new SpannableString(s1); s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); TextPaint p = new TextPaint(); + p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG); for (int i = 0; i < 100; i++) { for (int j = 0; j < 3; j++) { diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java new file mode 100644 index 000000000000..b31ce18da429 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests.content; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.os.MemoryFile; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; + +/** Simple test provider that runs in the local process. */ +public class MemoryFileProvider extends ContentProvider { + private static final String TAG = "MemoryFileProvider"; + + private static final String DATA_FILE = "data.bin"; + + // some random data + public static final byte[] TEST_BLOB = new byte[] { + -12, 127, 0, 3, 1, 2, 3, 4, 5, 6, 1, -128, -1, -54, -65, 35, + -53, -96, -74, -74, -55, -43, -69, 3, 52, -58, + -121, 127, 87, -73, 16, -13, -103, -65, -128, -36, + 107, 24, 118, -17, 97, 97, -88, 19, -94, -54, + 53, 43, 44, -27, -124, 28, -74, 26, 35, -36, + 16, -124, -31, -31, -128, -79, 108, 116, 43, -17 }; + + private SQLiteOpenHelper mOpenHelper; + + private static final int DATA_ID_BLOB = 1; + private static final int HUGE = 2; + private static final int FILE = 3; + + private static final UriMatcher sURLMatcher = new UriMatcher( + UriMatcher.NO_MATCH); + + static { + sURLMatcher.addURI("*", "data/#/blob", DATA_ID_BLOB); + sURLMatcher.addURI("*", "huge", HUGE); + sURLMatcher.addURI("*", "file", FILE); + } + + private static class DatabaseHelper extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "local.db"; + private static final int DATABASE_VERSION = 1; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE data (" + + "_id INTEGER PRIMARY KEY," + + "_blob TEXT, " + + "integer INTEGER);"); + + // insert alarms + ContentValues values = new ContentValues(); + values.put("_id", 1); + values.put("_blob", TEST_BLOB); + values.put("integer", 100); + db.insert("data", null, values); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { + Log.w(TAG, "Upgrading test database from version " + + oldVersion + " to " + currentVersion + + ", which will destroy all old data"); + db.execSQL("DROP TABLE IF EXISTS data"); + onCreate(db); + } + } + + + public MemoryFileProvider() { + } + + @Override + public boolean onCreate() { + mOpenHelper = new DatabaseHelper(getContext()); + try { + OutputStream out = getContext().openFileOutput(DATA_FILE, Context.MODE_PRIVATE); + out.write(TEST_BLOB); + out.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + return true; + } + + @Override + public Cursor query(Uri url, String[] projectionIn, String selection, + String[] selectionArgs, String sort) { + throw new UnsupportedOperationException("query not supported"); + } + + @Override + public String getType(Uri url) { + int match = sURLMatcher.match(url); + switch (match) { + case DATA_ID_BLOB: + return "application/octet-stream"; + case FILE: + return "application/octet-stream"; + default: + throw new IllegalArgumentException("Unknown URL"); + } + } + + @Override + public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException { + int match = sURLMatcher.match(url); + switch (match) { + case DATA_ID_BLOB: + String sql = "SELECT _blob FROM data WHERE _id=" + url.getPathSegments().get(1); + return getBlobColumnAsAssetFile(url, mode, sql); + case HUGE: + try { + MemoryFile memoryFile = new MemoryFile(null, 5000000); + memoryFile.writeBytes(TEST_BLOB, 0, 1000000, TEST_BLOB.length); + memoryFile.deactivate(); + return AssetFileDescriptor.fromMemoryFile(memoryFile); + } catch (IOException ex) { + throw new FileNotFoundException("Error reading " + url + ":" + ex.toString()); + } + case FILE: + File file = getContext().getFileStreamPath(DATA_FILE); + ParcelFileDescriptor fd = + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + return new AssetFileDescriptor(fd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); + default: + throw new FileNotFoundException("No files supported by provider at " + url); + } + } + + private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql) + throws FileNotFoundException { + if (!"r".equals(mode)) { + throw new FileNotFoundException("Mode " + mode + " not supported for " + url); + } + try { + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + MemoryFile file = simpleQueryForBlobMemoryFile(db, sql); + if (file == null) throw new FileNotFoundException("No such entry: " + url); + AssetFileDescriptor afd = AssetFileDescriptor.fromMemoryFile(file); + file.deactivate(); + // need to dup and then close? openFileHelper() doesn't do that though + return afd; + } catch (IOException ex) { + throw new FileNotFoundException("Error reading " + url + ":" + ex.toString()); + } + } + + private MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql) throws IOException { + Cursor cursor = db.rawQuery(sql, null); + try { + if (!cursor.moveToFirst()) { + return null; + } + byte[] bytes = cursor.getBlob(0); + MemoryFile file = new MemoryFile(null, bytes.length); + file.writeBytes(bytes, 0, 0, bytes.length); + return file; + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + + @Override + public int update(Uri url, ContentValues values, String where, String[] whereArgs) { + throw new UnsupportedOperationException("update not supported"); + } + + @Override + public Uri insert(Uri url, ContentValues initialValues) { + throw new UnsupportedOperationException("insert not supported"); + } + + @Override + public int delete(Uri url, String where, String[] whereArgs) { + throw new UnsupportedOperationException("delete not supported"); + } +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java new file mode 100644 index 000000000000..f88a9dac3962 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests.content; + +import android.content.ContentResolver; +import android.net.Uri; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; + +import java.io.InputStream; +import java.util.Arrays; + +/** + * Tests reading a MemoryFile-based AssestFile from a ContentProvider running + * in a different process. + */ +public class MemoryFileProviderTest extends AndroidTestCase { + + // reads from a cross-process AssetFileDescriptor for a MemoryFile + @MediumTest + public void testRead() throws Exception { + ContentResolver resolver = getContext().getContentResolver(); + Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/data/1/blob"); + byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length]; + InputStream in = resolver.openInputStream(uri); + assertNotNull(in); + int count = in.read(buf); + assertEquals(buf.length, count); + assertEquals(-1, in.read()); + in.close(); + assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf)); + } + + // tests that we don't leak file descriptors or virtual address space + @MediumTest + public void testClose() throws Exception { + ContentResolver resolver = getContext().getContentResolver(); + // open enough file descriptors that we will crash something if we leak FDs + // or address space + for (int i = 0; i < 1025; i++) { + Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/huge"); + InputStream in = resolver.openInputStream(uri); + assertNotNull("Failed to open stream number " + i, in); + assertEquals(1000000, in.skip(1000000)); + byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length]; + int count = in.read(buf); + assertEquals(buf.length, count); + assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf)); + in.close(); + } + } + + // tests that we haven't broken AssestFileDescriptors for normal files. + @MediumTest + public void testFile() throws Exception { + ContentResolver resolver = getContext().getContentResolver(); + Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/file"); + byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length]; + InputStream in = resolver.openInputStream(uri); + assertNotNull(in); + int count = in.read(buf); + assertEquals(buf.length, count); + assertEquals(-1, in.read()); + in.close(); + assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf)); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java index 508afcf411b8..18b3d6321e83 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java @@ -17,18 +17,21 @@ package com.android.unit_tests.os; import android.os.MemoryFile; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import junit.framework.TestCase; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; - -import java.util.List; import java.util.ArrayList; +import java.util.List; -public class MemoryFileTest extends TestCase { +public class MemoryFileTest extends AndroidTestCase { private void compareBuffers(byte[] buffer1, byte[] buffer2, int length) throws Exception { for (int i = 0; i < length; i++) { @@ -94,6 +97,187 @@ public class MemoryFileTest extends TestCase { file.close(); } + // Tests for the IndexOutOfBoundsException cases in read(). + + private void readIndexOutOfBoundsException(int offset, int count, String msg) + throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", testString.length); + try { + file.writeBytes(testString, 0, 0, testString.length); + InputStream is = file.getInputStream(); + byte[] buffer = new byte[testString.length + 10]; + try { + is.read(buffer, offset, count); + fail(msg); + } catch (IndexOutOfBoundsException ex) { + // this is what should happen + } finally { + is.close(); + } + } finally { + file.close(); + } + } + + @SmallTest + public void testReadNegativeOffset() throws Exception { + readIndexOutOfBoundsException(-1, 5, + "read() with negative offset should throw IndexOutOfBoundsException"); + } + + @SmallTest + public void testReadNegativeCount() throws Exception { + readIndexOutOfBoundsException(5, -1, + "read() with negative length should throw IndexOutOfBoundsException"); + } + + @SmallTest + public void testReadOffsetOverflow() throws Exception { + readIndexOutOfBoundsException(testString.length + 10, 5, + "read() with offset outside buffer should throw IndexOutOfBoundsException"); + } + + @SmallTest + public void testReadOffsetCountOverflow() throws Exception { + readIndexOutOfBoundsException(testString.length, 11, + "read() with offset + count outside buffer should throw IndexOutOfBoundsException"); + } + + // Test behavior of read() at end of file + @SmallTest + public void testReadEOF() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", testString.length); + try { + file.writeBytes(testString, 0, 0, testString.length); + InputStream is = file.getInputStream(); + try { + byte[] buffer = new byte[testString.length + 10]; + // read() with count larger than data should succeed, and return # of bytes read + assertEquals(testString.length, is.read(buffer)); + compareBuffers(testString, buffer, testString.length); + // Read at EOF should return -1 + assertEquals(-1, is.read()); + } finally { + is.close(); + } + } finally { + file.close(); + } + } + + // Tests that close() is idempotent + @SmallTest + public void testCloseClose() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + file.close(); + file.close(); + } + + // Tests that we can't read from a closed memory file + @SmallTest + public void testCloseRead() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + file.close(); + + try { + byte[] data = new byte[512]; + assertEquals(128, file.readBytes(data, 0, 0, 128)); + fail("readBytes() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we can't write to a closed memory file + @SmallTest + public void testCloseWrite() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + file.close(); + + try { + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + fail("writeBytes() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we can't call allowPurging() after close() + @SmallTest + public void testCloseAllowPurging() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + file.close(); + + try { + file.allowPurging(true); + fail("allowPurging() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we don't leak file descriptors or mmap areas + @LargeTest + public void testCloseLeak() throws Exception { + // open enough memory files that we should run out of + // file descriptors or address space if we leak either. + for (int i = 0; i < 1025; i++) { + MemoryFile file = new MemoryFile("MemoryFileTest", 5000000); + file.writeBytes(testString, 0, 0, testString.length); + file.close(); + } + } + + @SmallTest + public void testIsMemoryFile() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + FileDescriptor fd = file.getFileDescriptor(); + assertNotNull(fd); + assertTrue(fd.valid()); + assertTrue(MemoryFile.isMemoryFile(fd)); + file.close(); + + assertFalse(MemoryFile.isMemoryFile(FileDescriptor.in)); + assertFalse(MemoryFile.isMemoryFile(FileDescriptor.out)); + assertFalse(MemoryFile.isMemoryFile(FileDescriptor.err)); + + File tempFile = File.createTempFile("MemoryFileTest",".tmp", getContext().getFilesDir()); + assertNotNull(file); + FileOutputStream out = null; + try { + out = new FileOutputStream(tempFile); + FileDescriptor fileFd = out.getFD(); + assertNotNull(fileFd); + assertFalse(MemoryFile.isMemoryFile(fileFd)); + } finally { + if (out != null) { + out.close(); + } + tempFile.delete(); + } + } + + @SmallTest + public void testFileDescriptor() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r"); + byte[] buffer; + + // write to original, read from reference + file.writeBytes(testString, 0, 2000, testString.length); + buffer = new byte[testString.length]; + ref.readBytes(buffer, 2000, 0, testString.length); + compareBuffers(testString, buffer, testString.length); + + file.close(); + ref.close(); // Doesn't actually do anything, since the file descriptor is not dup(2):ed + } + private static final byte[] testString = new byte[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4, 0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2, diff --git a/tests/CoreTests/android/location/LocationManagerProximityTest.java b/tests/CoreTests/android/location/LocationManagerProximityTest.java index 3f43bcf08148..e82d8780ddef 100644 --- a/tests/CoreTests/android/location/LocationManagerProximityTest.java +++ b/tests/CoreTests/android/location/LocationManagerProximityTest.java @@ -26,6 +26,7 @@ import android.location.LocationManager; import android.provider.Settings; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; import android.util.Log; /** @@ -37,9 +38,11 @@ import android.util.Log; * adb shell am instrument -e class android.location.LocationManagerProximityTest \ * -w android.core/android.test.InstrumentationTestRunner * - * This test requires that the "Allow mock locations" setting be enabled + * This test requires that the "Allow mock locations" setting be enabled. + * To ensure reliable results, all location providers should be disabled. * */ +@Suppress @MediumTest public class LocationManagerProximityTest extends AndroidTestCase { diff --git a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java index 5df89914ccca..3a9c5116f160 100644 --- a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java +++ b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java @@ -28,18 +28,20 @@ public class GsmAlphabetTest extends TestCase { @SmallTest public void test7bitWithHeader() throws Exception { - byte[] data = new byte[3]; - data[0] = (byte) 1; - data[1] = (byte) 2; - data[2] = (byte) 2; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = 1; + concatRef.seqNumber = 2; + concatRef.msgCount = 2; + concatRef.isEightBits = true; SmsHeader header = new SmsHeader(); - header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); + header.concatRef = concatRef; - String message = "aaaaaaaaaabbbbbbbbbbcccccccccc"; - byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header.toByteArray()); + String message = "aaaaaaaaaabbbbbbbbbbcccccccccc"; + byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, + SmsHeader.toByteArray(header)); int septetCount = GsmAlphabet.countGsmSeptets(message, false); String parsedMessage = GsmAlphabet.gsm7BitPackedToString( - userData, header.toByteArray().length+1, septetCount, 1); + userData, SmsHeader.toByteArray(header).length+2, septetCount, 1); assertEquals(message, parsedMessage); } @@ -306,4 +308,3 @@ public class GsmAlphabetTest extends TestCase { GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1)); } } - diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java index e8bd23999890..577d3844c73a 100644 --- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -93,6 +93,13 @@ public class PhoneNumberUtilsTest extends TestCase { assertEquals(b[i], bRet[i]); } + bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020"); + assertEquals(8, bRet.length); + assertEquals(bRet[0], 7); + for (int i = 1; i < 8; i++) { + assertEquals(b[i - 1], bRet[i]); + } + bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020"); assertEquals("7005550020", PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length)); diff --git a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java index 5d5d1f99ec05..8a66614af0bd 100644 --- a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java +++ b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java @@ -34,35 +34,38 @@ public class SMSDispatcherTest extends AndroidTestCase { public void testCMT1() throws Exception { SmsMessage sms; SmsHeader header; - Iterator<SmsHeader.Element> elements; String[] lines = new String[2]; - - lines[0] = "+CMT: ,158"; + + lines[0] = "+CMT: ,158"; lines[1] = "07914140279510F6440A8111110301003BF56080426101748A8C0B05040B" + "8423F000035502010106276170706C69636174696F6E2F766E642E776170" + "2E6D6D732D6D65737361676500AF848D0185B4848C8298524F347839776F" + "7547514D4141424C3641414141536741415A4B554141414141008D908918" + "802B31363530323438363137392F545950453D504C4D4E008A808E028000" + "88058103093A8083687474703A2F2F36"; - + sms = SmsMessage.newFromCMT(lines); header = sms.getUserDataHeader(); assertNotNull(header); assertNotNull(sms.getUserData()); - - elements = header.getElements().iterator(); - assertNotNull(elements); + assertNotNull(header.concatRef); + assertEquals(header.concatRef.refNumber, 85); + assertEquals(header.concatRef.msgCount, 2); + assertEquals(header.concatRef.seqNumber, 1); + assertEquals(header.concatRef.isEightBits, true); + assertNotNull(header.portAddrs); + assertEquals(header.portAddrs.destPort, 2948); + assertEquals(header.portAddrs.origPort, 9200); + assertEquals(header.portAddrs.areEightBits, false); } - + @MediumTest public void testCMT2() throws Exception { SmsMessage sms; SmsHeader header; - Iterator<SmsHeader.Element> elements; String[] lines = new String[2]; - lines[0] = "+CMT: ,77"; lines[1] = "07914140279510F6440A8111110301003BF56080426101848A3B0B05040B8423F" @@ -71,12 +74,17 @@ public class SMSDispatcherTest extends AndroidTestCase { sms = SmsMessage.newFromCMT(lines); header = sms.getUserDataHeader(); - System.out.println("header = " + header); assertNotNull(header); assertNotNull(sms.getUserData()); - - elements = header.getElements().iterator(); - assertNotNull(elements); + assertNotNull(header.concatRef); + assertEquals(header.concatRef.refNumber, 85); + assertEquals(header.concatRef.msgCount, 2); + assertEquals(header.concatRef.seqNumber, 2); + assertEquals(header.concatRef.isEightBits, true); + assertNotNull(header.portAddrs); + assertEquals(header.portAddrs.destPort, 2948); + assertEquals(header.portAddrs.origPort, 9200); + assertEquals(header.portAddrs.areEightBits, false); } @MediumTest diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml index f71cff22811b..64ad7beeda34 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 000000000000..f4dd543dc72f --- /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 000000000000..256d696df80e --- /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 000000000000..cdb4ac9f60bd --- /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 000000000000..ef924ac85a63 --- /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 3759622bbbec..5a9f3f548c7d 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/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml index 0e33d62ae5f1..efa41131be31 100644 --- a/tests/DumpRenderTree/AndroidManifest.xml +++ b/tests/DumpRenderTree/AndroidManifest.xml @@ -23,13 +23,15 @@ <category android:name="android.intent.category.TEST" /> </intent-filter> </activity> - <activity android:name="TestShellActivity" android:launchMode="singleTop"> - </activity> + <activity android:name="TestShellActivity" android:launchMode="singleTop" /> + <activity android:name="ReliabilityTestActivity" /> </application> <instrumentation android:name=".LayoutTestsAutoRunner" android:targetPackage="com.android.dumprendertree" android:label="Layout test automation runner" /> - <uses-permission android:name="android.permission.INTERNET"></uses-permission> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_SDCARD" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest> diff --git a/tests/DumpRenderTree/assets/results/layout_tests_failed.txt b/tests/DumpRenderTree/assets/results/layout_tests_failed.txt index 3cec40d20111..651b32494d0e 100644 --- a/tests/DumpRenderTree/assets/results/layout_tests_failed.txt +++ b/tests/DumpRenderTree/assets/results/layout_tests_failed.txt @@ -1,280 +1,698 @@ -/sdcard/android/layout_tests/fast/replaced/image-map-bug16782.html : different length -/sdcard/android/layout_tests/fast/replaced/table-percent-height.html : different length -/sdcard/android/layout_tests/fast/replaced/image-map.html : different length -/sdcard/android/layout_tests/fast/dynamic/paused-event-dispatch.html : @offset: 117 -/sdcard/android/layout_tests/fast/text/plain-text-line-breaks.html : different length -/sdcard/android/layout_tests/fast/text/zero-width-characters.html : different length -/sdcard/android/layout_tests/fast/text/reset-drag-on-mouse-down.html : TIMEDOUT -/sdcard/android/layout_tests/fast/encoding/invalid-xml.html : different length -/sdcard/android/layout_tests/fast/encoding/char-decoding-mac.html : different length -/sdcard/android/layout_tests/fast/encoding/frame-default-enc.html : @offset: 0 -/sdcard/android/layout_tests/fast/encoding/mailto-always-utf-8.html : different length -/sdcard/android/layout_tests/fast/encoding/char-decoding.html : different length -/sdcard/android/layout_tests/fast/encoding/url-host-name-non-ascii.html : different length -/sdcard/android/layout_tests/fast/encoding/idn-security.html : different length -/sdcard/android/layout_tests/fast/encoding/percent-escaping.html : TIMEDOUT -/sdcard/android/layout_tests/fast/encoding/xml-utf-8-default.xml : different length -/sdcard/android/layout_tests/fast/encoding/char-encoding-mac.html : different length -/sdcard/android/layout_tests/fast/encoding/charset-koi8-u.html : @offset: 147 -/sdcard/android/layout_tests/fast/encoding/preload-encoding.html : different length -/sdcard/android/layout_tests/fast/workers/worker-location.html : TIMEDOUT -/sdcard/android/layout_tests/fast/workers/worker-constructor.html : different length -/sdcard/android/layout_tests/fast/workers/worker-terminate.html : different length -/sdcard/android/layout_tests/fast/workers/worker-gc.html : different length -/sdcard/android/layout_tests/fast/workers/worker-navigator.html : different length -/sdcard/android/layout_tests/fast/workers/worker-replace-global-constructor.html : TIMEDOUT -/sdcard/android/layout_tests/fast/workers/worker-replace-self.html : TIMEDOUT -/sdcard/android/layout_tests/fast/workers/worker-event-listener.html : TIMEDOUT -/sdcard/android/layout_tests/fast/overflow/scroll-vertical-not-horizontal.html : different length -/sdcard/android/layout_tests/fast/events/onunload.html : different length -/sdcard/android/layout_tests/fast/events/mouseup-outside-document.html : different length -/sdcard/android/layout_tests/fast/events/offsetX-offsetY.html : different length -/sdcard/android/layout_tests/fast/events/scroll-event-does-not-bubble.html : different length -/sdcard/android/layout_tests/fast/events/mouseover-mouseout.html : different length -/sdcard/android/layout_tests/fast/events/option-tab.html : different length -/sdcard/android/layout_tests/fast/events/popup-blocking-click-in-iframe.html : different length -/sdcard/android/layout_tests/fast/events/onchange-passwordfield.html : different length -/sdcard/android/layout_tests/fast/events/drag-in-frames.html : different length -/sdcard/android/layout_tests/fast/events/frame-tab-focus.html : different length -/sdcard/android/layout_tests/fast/events/autoscroll-in-textfield.html : different length -/sdcard/android/layout_tests/fast/events/arrow-navigation.html : different length -/sdcard/android/layout_tests/fast/events/fire-scroll-event.html : different length -/sdcard/android/layout_tests/fast/events/mouseclick-target-and-positioning.html : different length -/sdcard/android/layout_tests/fast/events/keydown-keypress-focus-change.html : @offset: 172 -/sdcard/android/layout_tests/fast/events/input-image-scrolled-x-y.html : TIMEDOUT -/sdcard/android/layout_tests/fast/events/dblclick-addEventListener.html : different length -/sdcard/android/layout_tests/fast/events/frame-programmatic-focus.html : different length -/sdcard/android/layout_tests/fast/events/related-target.html : different length -/sdcard/android/layout_tests/fast/events/content-changed-during-drop.html : different length -/sdcard/android/layout_tests/fast/events/onload-fires-twice.html : different length -/sdcard/android/layout_tests/fast/events/autoscroll-with-non-scrollable-parent.html : different length -/sdcard/android/layout_tests/fast/events/window-events-capture.html : different length -/sdcard/android/layout_tests/fast/events/onchange-click-hang.html : TIMEDOUT -/sdcard/android/layout_tests/fast/events/onload-webkit-before-webcore.html : @offset: 105 -/sdcard/android/layout_tests/fast/events/window-events-bubble2.html : different length -/sdcard/android/layout_tests/fast/events/js-keyboard-event-creation.html : different length -/sdcard/android/layout_tests/fast/events/event-view-toString.html : TIMEDOUT -/sdcard/android/layout_tests/fast/events/onchange-select-popup.html : different length -/sdcard/android/layout_tests/fast/events/access-key-self-destruct.html : different length -/sdcard/android/layout_tests/fast/events/scrollbar-double-click.html : different length -/sdcard/android/layout_tests/fast/events/onunload-clears-onbeforeunload.html : different length -/sdcard/android/layout_tests/fast/events/tabindex-focus-chain.html : @offset: 0 -/sdcard/android/layout_tests/fast/events/capture-on-target.html : different length -/sdcard/android/layout_tests/fast/events/window-events-bubble.html : different length -/sdcard/android/layout_tests/fast/events/mouseup-from-button2.html : different length -/sdcard/android/layout_tests/fast/events/frame-click-focus.html : different length -/sdcard/android/layout_tests/fast/events/mouseout-on-window.html : different length -/sdcard/android/layout_tests/fast/events/keypress-insert-tab.html : @offset: 85 -/sdcard/android/layout_tests/fast/events/mouseout-dead-subframe.html : TIMEDOUT -/sdcard/android/layout_tests/fast/events/iframe-object-onload.html : different length -/sdcard/android/layout_tests/fast/events/onunload-not-on-body.html : different length -/sdcard/android/layout_tests/fast/events/mousemove-after-drag-over-scrollbar.html : different length -/sdcard/android/layout_tests/fast/events/contextmenu-scrolled-page-with-frame.html : different length -/sdcard/android/layout_tests/fast/events/key-events-in-input-button.html : different length -/sdcard/android/layout_tests/fast/events/arrow-keys-on-body.html : different length -/sdcard/android/layout_tests/fast/events/ondragenter.html : different length -/sdcard/android/layout_tests/fast/events/scroll-to-anchor-in-overflow-hidden.html : different length -/sdcard/android/layout_tests/fast/events/autoscroll-nonscrollable-iframe-in-scrollable-div.html : different length -/sdcard/android/layout_tests/fast/events/keypress-focus-change.html : different length -/sdcard/android/layout_tests/fast/events/key-events-in-input-text.html : different length -/sdcard/android/layout_tests/fast/events/drag-outside-window.html : @offset: 20 -/sdcard/android/layout_tests/fast/events/selectstart-during-autoscroll.html : different length -/sdcard/android/layout_tests/fast/events/click-count.html : different length -/sdcard/android/layout_tests/fast/events/onchange-searchfield.html : different length -/sdcard/android/layout_tests/fast/events/special-key-events-in-input-text.html : different length -/sdcard/android/layout_tests/fast/events/keydown-keypress-preventDefault.html : @offset: 228 -/sdcard/android/layout_tests/fast/events/mouse-click-events.html : different length -/sdcard/android/layout_tests/fast/events/onsearch-enter.html : different length -/sdcard/android/layout_tests/fast/events/mouseover-mouseout2.html : different length -/sdcard/android/layout_tests/fast/events/open-window-from-another-frame.html : different length -/sdcard/android/layout_tests/fast/events/init-events.html : different length -/sdcard/android/layout_tests/fast/events/onchange-textfield.html : different length -/sdcard/android/layout_tests/fast/events/onclick-list-marker.html : different length -/sdcard/android/layout_tests/fast/events/anchor-image-scrolled-x-y.html : TIMEDOUT -/sdcard/android/layout_tests/fast/html/tab-order.html : @offset: 246 -/sdcard/android/layout_tests/fast/js/exception-sequencing-binops.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/recursion-limit-equal.html : different length -/sdcard/android/layout_tests/fast/js/exception-sequencing-binops2.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/math-transforms.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/try-catch-crash.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/navigator-mimeTypes-length.html : different length -/sdcard/android/layout_tests/fast/js/global-constructors.html : different length -/sdcard/android/layout_tests/fast/js/uncaught-exception-line-number.html : different length -/sdcard/android/layout_tests/fast/js/exceptions-thrown-in-callbacks.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/toString-and-valueOf-override.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/exception-sequencing.html : TIMEDOUT -/sdcard/android/layout_tests/fast/js/exception-codegen-crash.html : different length -/sdcard/android/layout_tests/fast/dom/HTMLDocument/activeElement.html : different length -/sdcard/android/layout_tests/fast/dom/HTMLDocument/hasFocus.html : different length -/sdcard/android/layout_tests/fast/dom/HTMLSelectElement/listbox-select-reset.html : different length -/sdcard/android/layout_tests/fast/dom/Element/offsetLeft-offsetTop-body-quirk.html : different length -/sdcard/android/layout_tests/fast/dom/DOMException/XPathException.html : different length -/sdcard/android/layout_tests/fast/dom/getElementsByClassName/010.xml : different length -/sdcard/android/layout_tests/fast/dom/getElementsByClassName/011.xml : different length -/sdcard/android/layout_tests/fast/dom/Window/window-resize-and-move-arguments.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-function-name-getter-precedence.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-xy-properties.html : different length -/sdcard/android/layout_tests/fast/dom/Window/console-functions.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-screen-properties.html : @offset: 65 -/sdcard/android/layout_tests/fast/dom/Window/window-properties.html : TIMEDOUT -/sdcard/android/layout_tests/fast/dom/Window/window-onFocus.html : different length -/sdcard/android/layout_tests/fast/dom/Window/new-window-opener.html : TIMEDOUT -/sdcard/android/layout_tests/fast/dom/Window/window-open-pending-url.html : different length -/sdcard/android/layout_tests/fast/dom/Window/setting-properties-on-closed-window.html : TIMEDOUT -/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-window.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-resize.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-custom-prototype.html : different length -/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-iframe.html : different length -/sdcard/android/layout_tests/fast/dom/Window/Plug-ins.html : different length -/sdcard/android/layout_tests/fast/dom/Window/get-set-properties.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-scroll-arguments.html : different length -/sdcard/android/layout_tests/fast/dom/Window/window-early-properties.html : different length -/sdcard/android/layout_tests/fast/dom/StyleSheet/ownerNode-lifetime-2.html : different length -/sdcard/android/layout_tests/fast/dom/dom-constructors.html : different length -/sdcard/android/layout_tests/fast/dom/assign-to-window-status.html : different length -/sdcard/android/layout_tests/fast/dom/gc-8.html : different length -/sdcard/android/layout_tests/fast/dom/object-embed-plugin-scripting.html : different length -/sdcard/android/layout_tests/fast/dom/gc-9.html : different length -/sdcard/android/layout_tests/fast/dom/NamedNodeMap-setNamedItem-crash.html : different length -/sdcard/android/layout_tests/fast/dom/wrapper-classes.html : different length -/sdcard/android/layout_tests/fast/dom/select-selectedIndex.html : different length -/sdcard/android/layout_tests/fast/dom/document-width-height-force-layout.html : @offset: 142 -/sdcard/android/layout_tests/fast/dom/client-width-height.html : @offset: 119 -/sdcard/android/layout_tests/fast/dom/clone-node-form-elements-with-attr.html : different length -/sdcard/android/layout_tests/fast/dom/dom-add-optionelement.html : different length -/sdcard/android/layout_tests/fast/dom/location-assign.html : different length -/sdcard/android/layout_tests/fast/dom/javascript-backslash.html : different length -/sdcard/android/layout_tests/fast/dom/global-constructors.html : different length -/sdcard/android/layout_tests/fast/dom/client-width-height-quirks.html : @offset: 115 -/sdcard/android/layout_tests/fast/dom/javascript-url-crash-function.html : different length -/sdcard/android/layout_tests/fast/dom/location-hash.html : different length -/sdcard/android/layout_tests/fast/dom/constructors-cached.html : different length -/sdcard/android/layout_tests/fast/dom/documenturi-can-hold-arbitrary-string.html : different length -/sdcard/android/layout_tests/fast/dom/documenturi-not-affected-by-base-tag.html : different length -/sdcard/android/layout_tests/fast/dom/open-and-close-by-DOM.html : different length -/sdcard/android/layout_tests/fast/dom/document_write_params.html : TIMEDOUT -/sdcard/android/layout_tests/fast/dom/tabindex-clamp.html : different length -/sdcard/android/layout_tests/fast/dom/constructors-cached-navigate.html : different length -/sdcard/android/layout_tests/fast/dom/frame-loading-via-document-write.html : TIMEDOUT -/sdcard/android/layout_tests/fast/dom/ImageDocument-image-deletion.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/advanced-get.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain-latin-1.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-multipart-form-data.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-always-utf-8.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-append-query.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-x-www-form-urlencoded.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-overwrite-query.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-x-www-form-urlencoded.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-text-plain.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-text-plain.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain-with-accept-charset.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain.html : different length -/sdcard/android/layout_tests/fast/forms/mailto/advanced-put.html : different length -/sdcard/android/layout_tests/fast/forms/listbox-typeahead-scroll.html : different length -/sdcard/android/layout_tests/fast/forms/select-empty-list.html : different length -/sdcard/android/layout_tests/fast/forms/select-accesskey.html : different length -/sdcard/android/layout_tests/fast/forms/focus2.html : different length -/sdcard/android/layout_tests/fast/forms/password-doubleclick-selection.html : different length -/sdcard/android/layout_tests/fast/forms/textfield-inside-anchor.html : different length -/sdcard/android/layout_tests/fast/forms/input-maxlength.html : different length -/sdcard/android/layout_tests/fast/forms/input-text-option-delete.html : TIMEDOUT -/sdcard/android/layout_tests/fast/forms/selection-functions.html : @offset: 306 -/sdcard/android/layout_tests/fast/forms/textfield-to-password-on-focus.html : different length -/sdcard/android/layout_tests/fast/forms/focus-selection-textarea.html : different length -/sdcard/android/layout_tests/fast/forms/enter-clicks-buttons.html : different length -/sdcard/android/layout_tests/fast/forms/menulist-no-renderer-onmousedown.html : different length -/sdcard/android/layout_tests/fast/forms/select-display-none-style-resolve.html : TIMEDOUT -/sdcard/android/layout_tests/fast/forms/radio_checked_name.html : @offset: 303 -/sdcard/android/layout_tests/fast/forms/select-double-onchange.html : different length -/sdcard/android/layout_tests/fast/forms/button-enter-click.html : different length -/sdcard/android/layout_tests/fast/forms/11423.html : different length -/sdcard/android/layout_tests/fast/forms/search-click-in-placeholder.html : @offset: 1 -/sdcard/android/layout_tests/fast/forms/autofocus-opera-003.html : @offset: 51 -/sdcard/android/layout_tests/fast/forms/search-hidden-cancel-button.html : different length -/sdcard/android/layout_tests/fast/forms/willvalidate-004.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-paste-newline.html : different length -/sdcard/android/layout_tests/fast/forms/drag-into-textarea.html : different length -/sdcard/android/layout_tests/fast/forms/onselect-textfield.html : different length -/sdcard/android/layout_tests/fast/forms/input-implicit-length-limit.html : TIMEDOUT -/sdcard/android/layout_tests/fast/forms/form-and-frame-interaction-retains-values.html : different length -/sdcard/android/layout_tests/fast/forms/input-appearance-focus.html : TIMEDOUT -/sdcard/android/layout_tests/fast/forms/slider-transformed.html : different length -/sdcard/android/layout_tests/fast/forms/listbox-select-all.html : different length -/sdcard/android/layout_tests/fast/forms/textfield-onchange-deletion.html : different length -/sdcard/android/layout_tests/fast/forms/focus-control-to-page.html : different length -/sdcard/android/layout_tests/fast/forms/select-type-ahead-non-latin.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-no-scroll-on-blur.html : @offset: 79 -/sdcard/android/layout_tests/fast/forms/focus-selection-input.html : different length -/sdcard/android/layout_tests/fast/forms/listbox-onchange.html : different length -/sdcard/android/layout_tests/fast/forms/button-spacebar-click.html : different length -/sdcard/android/layout_tests/fast/forms/search-event-delay.html : different length -/sdcard/android/layout_tests/fast/forms/search-cancel-button-mouseup.html : different length -/sdcard/android/layout_tests/fast/forms/select-enter-key.html : different length -/sdcard/android/layout_tests/fast/forms/drag-out-of-textarea.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-hard-linewrap.html : different length -/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-keyboard.html : different length -/sdcard/android/layout_tests/fast/forms/dragging-to-disabled-file-input.html : different length -/sdcard/android/layout_tests/fast/forms/input-radio-checked-tab.html : @offset: 115 -/sdcard/android/layout_tests/fast/forms/plaintext-mode-1.html : different length -/sdcard/android/layout_tests/fast/forms/option-in-optgroup-removal.html : different length -/sdcard/android/layout_tests/fast/forms/search-display-none-cancel-button.html : TIMEDOUT -/sdcard/android/layout_tests/fast/forms/check-box-enter-key.html : different length -/sdcard/android/layout_tests/fast/forms/input-select-on-click.html : different length -/sdcard/android/layout_tests/fast/forms/button-state-restore.html : different length -/sdcard/android/layout_tests/fast/forms/access-key.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-scrolled-endline-caret.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-type-spaces.html : different length -/sdcard/android/layout_tests/fast/forms/slider-mouse-events.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-selection-preservation.html : different length -/sdcard/android/layout_tests/fast/forms/slider-onchange-event.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-appearance-wrap.html : different length -/sdcard/android/layout_tests/fast/forms/onselect-textarea.html : different length -/sdcard/android/layout_tests/fast/forms/textarea-initial-caret-position.html : different length -/sdcard/android/layout_tests/fast/forms/listbox-selection.html : different length -/sdcard/android/layout_tests/fast/css/variables/color-hex-test.html : different length -/sdcard/android/layout_tests/fast/css/dashboard-region-parser.html : different length -/sdcard/android/layout_tests/fast/css/hover-affects-child.html : different length -/sdcard/android/layout_tests/fast/css/html-attr-case-sensitivity.html : TIMEDOUT -/sdcard/android/layout_tests/fast/css/computed-style.html : different length -/sdcard/android/layout_tests/fast/css/getComputedStyle-transform.html : different length -/sdcard/android/layout_tests/fast/css/computed-style-without-renderer.html : different length -/sdcard/android/layout_tests/fast/parser/entity-comment-in-iframe.html : @offset: 0 -/sdcard/android/layout_tests/fast/parser/external-entities.xml : different length -/sdcard/android/layout_tests/fast/parser/script-tag-with-trailing-slash.html : different length -/sdcard/android/layout_tests/fast/parser/comment-in-iframe.html : @offset: 0 -/sdcard/android/layout_tests/fast/parser/xml-declaration-missing-ending-mark.html : different length -/sdcard/android/layout_tests/fast/parser/entity-end-script-tag.html : different length -/sdcard/android/layout_tests/fast/parser/tabindex-parsing.html : different length -/sdcard/android/layout_tests/fast/history/subframe-is-visited.html : different length -/sdcard/android/layout_tests/fast/history/window-open.html : TIMEDOUT -/sdcard/android/layout_tests/fast/history/go-back-to-changed-name.html : different length -/sdcard/android/layout_tests/fast/loader/cancel-load-during-port-block-timer.html : TIMEDOUT -/sdcard/android/layout_tests/fast/loader/local-iFrame-source-from-local.html : different length -/sdcard/android/layout_tests/fast/loader/null-request-after-willSendRequest.html : different length -/sdcard/android/layout_tests/fast/loader/stop-provisional-loads.html : different length -/sdcard/android/layout_tests/fast/loader/xmlhttprequest-missing-file-exception.html : different length -/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash-2.html : different length -/sdcard/android/layout_tests/fast/loader/plain-text-document.html : different length -/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash.html : different length -/sdcard/android/layout_tests/fast/loader/local-image-from-local.html : different length -/sdcard/android/layout_tests/fast/loader/local-CSS-from-local.html : TIMEDOUT -/sdcard/android/layout_tests/fast/loader/local-JavaScript-from-local.html : different length -/sdcard/android/layout_tests/fast/loader/data-url-encoding-svg.html : different length -/sdcard/android/layout_tests/fast/loader/opaque-base-url.html : @offset: 129 -/sdcard/android/layout_tests/fast/canvas/canvas-alphaImageData-behavior.html : different length -/sdcard/android/layout_tests/fast/canvas/pointInPath.html : different length -/sdcard/android/layout_tests/fast/canvas/toDataURL-supportedTypes.html : different length -/sdcard/android/layout_tests/fast/canvas/canvas-getImageData.html : different length -/sdcard/android/layout_tests/fast/canvas/canvas-longlived-context.html : TIMEDOUT -/sdcard/android/layout_tests/fast/canvas/canvas-save-restore-with-path.html : different length -/sdcard/android/layout_tests/fast/canvas/set-colors.html : different length -/sdcard/android/layout_tests/fast/frames/viewsource-empty-attribute-value.html : different length -/sdcard/android/layout_tests/fast/frames/frame-deep-nested-resize.html : different length -/sdcard/android/layout_tests/fast/frames/iframe-window-focus.html : different length -/sdcard/android/layout_tests/fast/frames/frameElement-widthheight.html : different length -/sdcard/android/layout_tests/fast/frames/removal-before-attach-crash.html : different length -/sdcard/android/layout_tests/fast/frames/frame-js-url-clientWidth.html : different length +/sdcard/android/layout_tests/webarchive/loading/test-loading-archive.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement16.html +/sdcard/android/layout_tests/media/video-error-does-not-exist.html +/sdcard/android/layout_tests/media/audio-constructor.html +/sdcard/android/layout_tests/media/video-start.html +/sdcard/android/layout_tests/media/video-loopend.html +/sdcard/android/layout_tests/media/video-play-empty-events.html +/sdcard/android/layout_tests/media/constructors.html +/sdcard/android/layout_tests/media/video-append-source.html +/sdcard/android/layout_tests/media/unsupported-rtsp.html +/sdcard/android/layout_tests/media/video-dom-autoplay.html +/sdcard/android/layout_tests/media/video-currentTime-set2.html +/sdcard/android/layout_tests/media/video-poster.html +/sdcard/android/layout_tests/media/video-source-media.html +/sdcard/android/layout_tests/media/video-muted.html +/sdcard/android/layout_tests/media/progress-event.html +/sdcard/android/layout_tests/media/video-source-type.html +/sdcard/android/layout_tests/media/video-seek-past-end-playing.html +/sdcard/android/layout_tests/media/video-dom-src.html +/sdcard/android/layout_tests/media/remove-from-document.html +/sdcard/android/layout_tests/media/video-end.html +/sdcard/android/layout_tests/media/video-src-change.html +/sdcard/android/layout_tests/media/video-seekable.html +/sdcard/android/layout_tests/media/video-click-dblckick-standalone.html +/sdcard/android/layout_tests/media/video-src-set.html +/sdcard/android/layout_tests/media/video-seeking.html +/sdcard/android/layout_tests/media/video-controls-transformed.html +/sdcard/android/layout_tests/media/loopend-limits.html +/sdcard/android/layout_tests/media/video-volume.html +/sdcard/android/layout_tests/media/video-size.html +/sdcard/android/layout_tests/media/video-width-height.html +/sdcard/android/layout_tests/media/video-loopstart.html +/sdcard/android/layout_tests/media/video-source.html +/sdcard/android/layout_tests/media/video-currentTime.html +/sdcard/android/layout_tests/media/video-dom-loopstart.html +/sdcard/android/layout_tests/media/broken-video.html +/sdcard/android/layout_tests/media/video-buffered.html +/sdcard/android/layout_tests/media/video-load-readyState.html +/sdcard/android/layout_tests/media/video-dom-end.html +/sdcard/android/layout_tests/media/fallback.html +/sdcard/android/layout_tests/media/video-load-networkState.html +/sdcard/android/layout_tests/media/unsupported-tracks.html +/sdcard/android/layout_tests/media/video-dom-start.html +/sdcard/android/layout_tests/media/video-seek-past-end-paused.html +/sdcard/android/layout_tests/media/video-play-pause-events.html +/sdcard/android/layout_tests/media/video-autoplay.html +/sdcard/android/layout_tests/media/video-controls.html +/sdcard/android/layout_tests/media/loopstart-limits.html +/sdcard/android/layout_tests/media/video-src-source.html +/sdcard/android/layout_tests/media/video-currentTime-set.html +/sdcard/android/layout_tests/media/video-source-type-params.html +/sdcard/android/layout_tests/media/video-no-autoplay.html +/sdcard/android/layout_tests/media/video-pause-empty-events.html +/sdcard/android/layout_tests/media/video-play-pause-exception.html +/sdcard/android/layout_tests/media/video-dom-loopend.html +/sdcard/android/layout_tests/media/video-loopcount.html +/sdcard/android/layout_tests/media/video-src-remove.html +/sdcard/android/layout_tests/media/video-src.html +/sdcard/android/layout_tests/media/video-error-abort.html +/sdcard/android/layout_tests/media/video-dom-loopcount.html +/sdcard/android/layout_tests/media/progress-event-total.html +/sdcard/android/layout_tests/media/audio-constructor-src.html +/sdcard/android/layout_tests/plugins/throw-on-dealloc.html +/sdcard/android/layout_tests/plugins/invoke.html +/sdcard/android/layout_tests/plugins/plugin-remove-subframe.html +/sdcard/android/layout_tests/plugins/netscape-identifier-conversion.html +/sdcard/android/layout_tests/plugins/call-as-function-test.html +/sdcard/android/layout_tests/plugins/npruntime.html +/sdcard/android/layout_tests/plugins/netscape-construct.html +/sdcard/android/layout_tests/plugins/root-object-premature-delete-crash.html +/sdcard/android/layout_tests/plugins/netscape-get-property-return-value.html +/sdcard/android/layout_tests/plugins/mouse-events.html +/sdcard/android/layout_tests/plugins/return-error-from-new-stream-doesnt-invoke-destroy-stream.html +/sdcard/android/layout_tests/plugins/destroy-stream-twice.html +/sdcard/android/layout_tests/plugins/jsobjc-simple.html +/sdcard/android/layout_tests/plugins/embed-attributes-setting.html +/sdcard/android/layout_tests/plugins/inner-html-display-none.html +/sdcard/android/layout_tests/plugins/netscape-invoke-default.html +/sdcard/android/layout_tests/plugins/undefined-property-crash.html +/sdcard/android/layout_tests/plugins/netscape-plugin-setwindow-size-2.html +/sdcard/android/layout_tests/plugins/jsobjc-dom-wrappers.html +/sdcard/android/layout_tests/plugins/plugin-javascript-access.html +/sdcard/android/layout_tests/plugins/getintidentifier-special-values.html +/sdcard/android/layout_tests/plugins/geturl-replace-query.html +/sdcard/android/layout_tests/plugins/netscape-destroy-plugin-script-objects.html +/sdcard/android/layout_tests/plugins/open-and-close-window-with-plugin.html +/sdcard/android/layout_tests/plugins/netscape-enumerate.html +/sdcard/android/layout_tests/plugins/get-url-that-the-resource-load-delegate-will-disallow.html +/sdcard/android/layout_tests/plugins/netscape-plugin-setwindow-size.html +/sdcard/android/layout_tests/plugins/embed-inside-object.html +/sdcard/android/layout_tests/plugins/netscape-throw-exception.html +/sdcard/android/layout_tests/plugins/get-url-with-blank-target.html +/sdcard/android/layout_tests/plugins/bindings-test.html +/sdcard/android/layout_tests/editing/input/textarea-arrow-navigation.html +/sdcard/android/layout_tests/editing/inserting/typing-tab-designmode.html +/sdcard/android/layout_tests/editing/inserting/5994480-2.html +/sdcard/android/layout_tests/editing/execCommand/queryCommandState-01.html +/sdcard/android/layout_tests/editing/execCommand/5543472-3.html +/sdcard/android/layout_tests/editing/execCommand/enabling-and-selection.html +/sdcard/android/layout_tests/editing/execCommand/5658933-2.html +/sdcard/android/layout_tests/editing/execCommand/insert-line-break-no-scroll.html +/sdcard/android/layout_tests/editing/execCommand/enabling-and-selection-2.html +/sdcard/android/layout_tests/editing/execCommand/delete-no-scroll.html +/sdcard/android/layout_tests/editing/execCommand/forward-delete-no-scroll.html +/sdcard/android/layout_tests/editing/execCommand/19089.html +/sdcard/android/layout_tests/editing/execCommand/unlink.html +/sdcard/android/layout_tests/editing/execCommand/5543472-2.html +/sdcard/android/layout_tests/editing/execCommand/copy-without-selection.html +/sdcard/android/layout_tests/editing/execCommand/findString-diacriticals.html +/sdcard/android/layout_tests/editing/execCommand/5658933-3.html +/sdcard/android/layout_tests/editing/execCommand/createLink.html +/sdcard/android/layout_tests/editing/execCommand/5543472-1.html +/sdcard/android/layout_tests/editing/execCommand/5939887.html +/sdcard/android/layout_tests/editing/pasteboard/paste-into-anchor-text.html +/sdcard/android/layout_tests/editing/pasteboard/copy-crash.html +/sdcard/android/layout_tests/editing/pasteboard/5665299.html +/sdcard/android/layout_tests/editing/pasteboard/5761530-1.html +/sdcard/android/layout_tests/editing/pasteboard/copy-in-password-field.html +/sdcard/android/layout_tests/editing/pasteboard/paste-plaintext-user-select-none.html +/sdcard/android/layout_tests/editing/pasteboard/drag-image-in-about-blank-frame.html +/sdcard/android/layout_tests/editing/pasteboard/4744008.html +/sdcard/android/layout_tests/editing/pasteboard/4922709.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-013.html +/sdcard/android/layout_tests/editing/pasteboard/5780697-2.html +/sdcard/android/layout_tests/editing/pasteboard/4840662.html +/sdcard/android/layout_tests/editing/pasteboard/paste-table-002.html +/sdcard/android/layout_tests/editing/selection/drag-text-delay.html +/sdcard/android/layout_tests/editing/selection/skip-non-editable-1.html +/sdcard/android/layout_tests/editing/selection/move-by-line-003.html +/sdcard/android/layout_tests/editing/selection/anchor-focus1.html +/sdcard/android/layout_tests/editing/selection/getRangeAt.html +/sdcard/android/layout_tests/editing/selection/select-all-textarea.html +/sdcard/android/layout_tests/editing/selection/doubleclick-whitespace.html +/sdcard/android/layout_tests/editing/selection/after-line-break.html +/sdcard/android/layout_tests/editing/selection/legal-positions.html +/sdcard/android/layout_tests/editing/selection/anchor-focus3.html +/sdcard/android/layout_tests/editing/selection/toString.html +/sdcard/android/layout_tests/editing/selection/extend-selection-bidi.html +/sdcard/android/layout_tests/editing/selection/inactive-selection.html +/sdcard/android/layout_tests/editing/selection/removeAllRanges.html +/sdcard/android/layout_tests/editing/selection/5209984.html +/sdcard/android/layout_tests/editing/selection/skip-non-editable-2.html +/sdcard/android/layout_tests/editing/selection/toString-1.html +/sdcard/android/layout_tests/editing/selection/anchor-focus2.html +/sdcard/android/layout_tests/editing/selection/move-begin-end.html +/sdcard/android/layout_tests/editing/selection/move-left-right.html +/sdcard/android/layout_tests/editing/selection/click-before-and-after-table.html +/sdcard/android/layout_tests/editing/undo/undo-iframe-location-change.html +/sdcard/android/layout_tests/editing/deleting/pruning-after-merge-1.html +/sdcard/android/layout_tests/editing/deleting/5546763.html +/sdcard/android/layout_tests/editing/deleting/5729680.html +/sdcard/android/layout_tests/editing/deleting/4916235-1.html +/sdcard/android/layout_tests/editing/deleting/delete-ligature-001.html +/sdcard/android/layout_tests/editing/deleting/delete-ligature-003.html +/sdcard/android/layout_tests/editing/deleting/5890684.html +/sdcard/android/layout_tests/editing/deleting/smart-editing-disabled.html +/sdcard/android/layout_tests/editing/deleting/delete-ligature-002.html +/sdcard/android/layout_tests/editing/deleting/delete-all-text-in-text-field-assertion.html +/sdcard/android/layout_tests/accessibility/image-map1.html +/sdcard/android/layout_tests/accessibility/aria-labelledby-on-input.html +/sdcard/android/layout_tests/accessibility/content-editable.html +/sdcard/android/layout_tests/accessibility/frame-with-title.html +/sdcard/android/layout_tests/accessibility/textarea-insertion-point-line-number.html +/sdcard/android/layout_tests/accessibility/table-with-rules.html +/sdcard/android/layout_tests/accessibility/aria-describedby-on-input.html +/sdcard/android/layout_tests/accessibility/radio-button-checkbox-size.html +/sdcard/android/layout_tests/accessibility/table-detection.html +/sdcard/android/layout_tests/accessibility/th-as-title-ui.html +/sdcard/android/layout_tests/accessibility/table-with-aria-role.html +/sdcard/android/layout_tests/accessibility/accesskey.html +/sdcard/android/layout_tests/accessibility/image-map2.html +/sdcard/android/layout_tests/accessibility/iframe-bastardization.html +/sdcard/android/layout_tests/accessibility/aria-spinbutton.html +/sdcard/android/layout_tests/accessibility/button-press-action.html +/sdcard/android/layout_tests/accessibility/table-cell-spans.html +/sdcard/android/layout_tests/accessibility/table-cells.html +/sdcard/android/layout_tests/accessibility/double-title.html +/sdcard/android/layout_tests/accessibility/lists.html +/sdcard/android/layout_tests/accessibility/plugin.html +/sdcard/android/layout_tests/accessibility/table-sections.html +/sdcard/android/layout_tests/accessibility/textarea-selected-text-range.html +/sdcard/android/layout_tests/accessibility/input-image-url.html +/sdcard/android/layout_tests/accessibility/aria-range-value.html +/sdcard/android/layout_tests/accessibility/table-one-cell.html +/sdcard/android/layout_tests/accessibility/internal-link-anchors2.html +/sdcard/android/layout_tests/accessibility/table-nofirstbody.html +/sdcard/android/layout_tests/accessibility/table-modification-crash.html +/sdcard/android/layout_tests/accessibility/radio-button-group-members.html +/sdcard/android/layout_tests/accessibility/aria-link-supports-press.html +/sdcard/android/layout_tests/accessibility/document-attributes.html +/sdcard/android/layout_tests/accessibility/table-notbody.html +/sdcard/android/layout_tests/accessibility/aria-range.html +/sdcard/android/layout_tests/accessibility/bounds-for-range.html +/sdcard/android/layout_tests/accessibility/table-attributes.html +/sdcard/android/layout_tests/accessibility/textarea-line-for-index.html +/sdcard/android/layout_tests/accessibility/aria-slider.html +/sdcard/android/layout_tests/accessibility/document-links.html +/sdcard/android/layout_tests/accessibility/legend.html +/sdcard/android/layout_tests/accessibility/aria-roles.html +/sdcard/android/layout_tests/accessibility/nochildren-elements.html +/sdcard/android/layout_tests/accessibility/internal-link-anchors.html +/sdcard/android/layout_tests/fast/replaced/image-map-bug16782.html +/sdcard/android/layout_tests/fast/replaced/table-percent-height.html +/sdcard/android/layout_tests/fast/replaced/image-map.html +/sdcard/android/layout_tests/fast/dynamic/paused-event-dispatch.html +/sdcard/android/layout_tests/fast/text/plain-text-line-breaks.html +/sdcard/android/layout_tests/fast/text/zero-width-characters.html +/sdcard/android/layout_tests/fast/text/reset-drag-on-mouse-down.html +/sdcard/android/layout_tests/fast/encoding/invalid-xml.html +/sdcard/android/layout_tests/fast/encoding/char-decoding-mac.html +/sdcard/android/layout_tests/fast/encoding/frame-default-enc.html +/sdcard/android/layout_tests/fast/encoding/mailto-always-utf-8.html +/sdcard/android/layout_tests/fast/encoding/char-decoding.html +/sdcard/android/layout_tests/fast/encoding/url-host-name-non-ascii.html +/sdcard/android/layout_tests/fast/encoding/idn-security.html +/sdcard/android/layout_tests/fast/encoding/percent-escaping.html +/sdcard/android/layout_tests/fast/encoding/xml-utf-8-default.xml +/sdcard/android/layout_tests/fast/encoding/char-encoding-mac.html +/sdcard/android/layout_tests/fast/encoding/yahoo-mail.html +/sdcard/android/layout_tests/fast/encoding/charset-koi8-u.html +/sdcard/android/layout_tests/fast/encoding/ahram-org-eg.html +/sdcard/android/layout_tests/fast/encoding/noscript-in-head.html +/sdcard/android/layout_tests/fast/workers/worker-location.html +/sdcard/android/layout_tests/fast/workers/worker-constructor.html +/sdcard/android/layout_tests/fast/workers/stress-js-execution.html +/sdcard/android/layout_tests/fast/workers/worker-terminate.html +/sdcard/android/layout_tests/fast/workers/worker-gc.html +/sdcard/android/layout_tests/fast/workers/worker-navigator.html +/sdcard/android/layout_tests/fast/workers/worker-replace-global-constructor.html +/sdcard/android/layout_tests/fast/workers/worker-replace-self.html +/sdcard/android/layout_tests/fast/workers/worker-event-listener.html +/sdcard/android/layout_tests/fast/selectors/lang-inheritance.html +/sdcard/android/layout_tests/fast/selectors/lang-vs-xml-lang.html +/sdcard/android/layout_tests/fast/selectors/lang-inheritance2.html +/sdcard/android/layout_tests/fast/overflow/scroll-vertical-not-horizontal.html +/sdcard/android/layout_tests/fast/events/onunload.html +/sdcard/android/layout_tests/fast/events/mouseup-outside-document.html +/sdcard/android/layout_tests/fast/events/offsetX-offsetY.html +/sdcard/android/layout_tests/fast/events/scroll-event-does-not-bubble.html +/sdcard/android/layout_tests/fast/events/mouseover-mouseout.html +/sdcard/android/layout_tests/fast/events/option-tab.html +/sdcard/android/layout_tests/fast/events/popup-blocking-click-in-iframe.html +/sdcard/android/layout_tests/fast/events/tabindex-focus-blur-all.html +/sdcard/android/layout_tests/fast/events/onchange-passwordfield.html +/sdcard/android/layout_tests/fast/events/drag-in-frames.html +/sdcard/android/layout_tests/fast/events/frame-tab-focus.html +/sdcard/android/layout_tests/fast/events/autoscroll-in-textfield.html +/sdcard/android/layout_tests/fast/events/arrow-navigation.html +/sdcard/android/layout_tests/fast/events/fire-scroll-event.html +/sdcard/android/layout_tests/fast/events/attempt-scroll-with-no-scrollbars.html +/sdcard/android/layout_tests/fast/events/mouseclick-target-and-positioning.html +/sdcard/android/layout_tests/fast/events/keydown-keypress-focus-change.html +/sdcard/android/layout_tests/fast/events/input-image-scrolled-x-y.html +/sdcard/android/layout_tests/fast/events/dblclick-addEventListener.html +/sdcard/android/layout_tests/fast/events/frame-programmatic-focus.html +/sdcard/android/layout_tests/fast/events/context-onmousedown-event.html +/sdcard/android/layout_tests/fast/events/content-changed-during-drop.html +/sdcard/android/layout_tests/fast/events/autoscroll-with-non-scrollable-parent.html +/sdcard/android/layout_tests/fast/events/window-events-capture.html +/sdcard/android/layout_tests/fast/events/onchange-click-hang.html +/sdcard/android/layout_tests/fast/events/onload-webkit-before-webcore.html +/sdcard/android/layout_tests/fast/events/window-events-bubble2.html +/sdcard/android/layout_tests/fast/events/js-keyboard-event-creation.html +/sdcard/android/layout_tests/fast/events/event-view-toString.html +/sdcard/android/layout_tests/fast/events/onchange-select-popup.html +/sdcard/android/layout_tests/fast/events/access-key-self-destruct.html +/sdcard/android/layout_tests/fast/events/scrollbar-double-click.html +/sdcard/android/layout_tests/fast/events/onunload-clears-onbeforeunload.html +/sdcard/android/layout_tests/fast/events/tabindex-focus-chain.html +/sdcard/android/layout_tests/fast/events/capture-on-target.html +/sdcard/android/layout_tests/fast/events/window-events-bubble.html +/sdcard/android/layout_tests/fast/events/mouseup-from-button2.html +/sdcard/android/layout_tests/fast/events/frame-click-focus.html +/sdcard/android/layout_tests/fast/events/mouseout-on-window.html +/sdcard/android/layout_tests/fast/events/keypress-insert-tab.html +/sdcard/android/layout_tests/fast/events/mouseout-dead-subframe.html +/sdcard/android/layout_tests/fast/events/iframe-object-onload.html +/sdcard/android/layout_tests/fast/events/onunload-not-on-body.html +/sdcard/android/layout_tests/fast/events/mousemove-after-drag-over-scrollbar.html +/sdcard/android/layout_tests/fast/events/contextmenu-scrolled-page-with-frame.html +/sdcard/android/layout_tests/fast/events/key-events-in-input-button.html +/sdcard/android/layout_tests/fast/events/arrow-keys-on-body.html +/sdcard/android/layout_tests/fast/events/ondragenter.html +/sdcard/android/layout_tests/fast/events/pointer-events.html +/sdcard/android/layout_tests/fast/events/scroll-to-anchor-in-overflow-hidden.html +/sdcard/android/layout_tests/fast/events/autoscroll-nonscrollable-iframe-in-scrollable-div.html +/sdcard/android/layout_tests/fast/events/keypress-focus-change.html +/sdcard/android/layout_tests/fast/events/key-events-in-input-text.html +/sdcard/android/layout_tests/fast/events/pointer-events-2.html +/sdcard/android/layout_tests/fast/events/drag-outside-window.html +/sdcard/android/layout_tests/fast/events/click-count.html +/sdcard/android/layout_tests/fast/events/onchange-searchfield.html +/sdcard/android/layout_tests/fast/events/special-key-events-in-input-text.html +/sdcard/android/layout_tests/fast/events/keydown-keypress-preventDefault.html +/sdcard/android/layout_tests/fast/events/mouse-click-events.html +/sdcard/android/layout_tests/fast/events/onsearch-enter.html +/sdcard/android/layout_tests/fast/events/mouseover-mouseout2.html +/sdcard/android/layout_tests/fast/events/open-window-from-another-frame.html +/sdcard/android/layout_tests/fast/events/init-events.html +/sdcard/android/layout_tests/fast/events/onchange-textfield.html +/sdcard/android/layout_tests/fast/events/onclick-list-marker.html +/sdcard/android/layout_tests/fast/events/anchor-image-scrolled-x-y.html +/sdcard/android/layout_tests/fast/html/tab-order.html +/sdcard/android/layout_tests/fast/regex/test1.html +/sdcard/android/layout_tests/fast/js/kde/garbage-n.html +/sdcard/android/layout_tests/fast/js/kde/Number.html +/sdcard/android/layout_tests/fast/js/kde/string-2-n.html +/sdcard/android/layout_tests/fast/js/kde/encode_decode_uri.html +/sdcard/android/layout_tests/fast/js/kde/string-1-n.html +/sdcard/android/layout_tests/fast/js/exception-sequencing-binops.html +/sdcard/android/layout_tests/fast/js/recursion-limit-equal.html +/sdcard/android/layout_tests/fast/js/exception-sequencing-binops2.html +/sdcard/android/layout_tests/fast/js/math-transforms.html +/sdcard/android/layout_tests/fast/js/try-catch-crash.html +/sdcard/android/layout_tests/fast/js/array-iterate-backwards.html +/sdcard/android/layout_tests/fast/js/navigator-mimeTypes-length.html +/sdcard/android/layout_tests/fast/js/global-constructors.html +/sdcard/android/layout_tests/fast/js/uncaught-exception-line-number.html +/sdcard/android/layout_tests/fast/js/exceptions-thrown-in-callbacks.html +/sdcard/android/layout_tests/fast/js/toString-and-valueOf-override.html +/sdcard/android/layout_tests/fast/js/function-toString-parentheses.html +/sdcard/android/layout_tests/fast/js/large-expressions.html +/sdcard/android/layout_tests/fast/js/exception-sequencing.html +/sdcard/android/layout_tests/fast/js/exception-codegen-crash.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/activeElement.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/document-special-properties.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/url-getset.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/hasFocus.html +/sdcard/android/layout_tests/fast/dom/HTMLSelectElement/listbox-select-reset.html +/sdcard/android/layout_tests/fast/dom/Document/early-document-access.html +/sdcard/android/layout_tests/fast/dom/Element/offsetLeft-offsetTop-body-quirk.html +/sdcard/android/layout_tests/fast/dom/DOMException/XPathException.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/010.xml +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/011.xml +/sdcard/android/layout_tests/fast/dom/Window/window-resize-and-move-arguments.html +/sdcard/android/layout_tests/fast/dom/Window/timeout-released-on-close.html +/sdcard/android/layout_tests/fast/dom/Window/window-function-name-getter-precedence.html +/sdcard/android/layout_tests/fast/dom/Window/window-xy-properties.html +/sdcard/android/layout_tests/fast/dom/Window/console-functions.html +/sdcard/android/layout_tests/fast/dom/Window/window-screen-properties.html +/sdcard/android/layout_tests/fast/dom/Window/window-properties.html +/sdcard/android/layout_tests/fast/dom/Window/window-onFocus.html +/sdcard/android/layout_tests/fast/dom/Window/new-window-opener.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-pending-url.html +/sdcard/android/layout_tests/fast/dom/Window/setting-properties-on-closed-window.html +/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-window.html +/sdcard/android/layout_tests/fast/dom/Window/window-resize.html +/sdcard/android/layout_tests/fast/dom/Window/closure-access-after-navigation-window.html +/sdcard/android/layout_tests/fast/dom/Window/window-special-properties.html +/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-iframe.html +/sdcard/android/layout_tests/fast/dom/Window/Plug-ins.html +/sdcard/android/layout_tests/fast/dom/Window/get-set-properties.html +/sdcard/android/layout_tests/fast/dom/Window/window-scroll-arguments.html +/sdcard/android/layout_tests/fast/dom/Window/window-early-properties.html +/sdcard/android/layout_tests/fast/dom/StyleSheet/ownerNode-lifetime-2.html +/sdcard/android/layout_tests/fast/dom/dom-constructors.html +/sdcard/android/layout_tests/fast/dom/assign-to-window-status.html +/sdcard/android/layout_tests/fast/dom/gc-8.html +/sdcard/android/layout_tests/fast/dom/object-embed-plugin-scripting.html +/sdcard/android/layout_tests/fast/dom/node-filter-gc.html +/sdcard/android/layout_tests/fast/dom/noscript-canvas-in-created-html-document.html +/sdcard/android/layout_tests/fast/dom/DOMParser-assign-variable.html +/sdcard/android/layout_tests/fast/dom/timer-clear-interval-in-handler.html +/sdcard/android/layout_tests/fast/dom/implementation-createHTMLDocument.html +/sdcard/android/layout_tests/fast/dom/iframe-document.html +/sdcard/android/layout_tests/fast/dom/document-all-input.html +/sdcard/android/layout_tests/fast/dom/null-document-location-href-put-crash.html +/sdcard/android/layout_tests/fast/dom/getelementsbytagnamens-mixed-namespaces.html +/sdcard/android/layout_tests/fast/dom/object-plugin-hides-properties.html +/sdcard/android/layout_tests/fast/dom/gc-2.html +/sdcard/android/layout_tests/fast/dom/computed-style-set-property.html +/sdcard/android/layout_tests/fast/dom/inner-text-001.html +/sdcard/android/layout_tests/fast/dom/css-selectorText.html +/sdcard/android/layout_tests/fast/dom/replace-first-child.html +/sdcard/android/layout_tests/fast/dom/importNode-null.html +/sdcard/android/layout_tests/fast/dom/select-selectedIndex-multiple.html +/sdcard/android/layout_tests/fast/dom/gc-9.html +/sdcard/android/layout_tests/fast/dom/NamedNodeMap-setNamedItem-crash.html +/sdcard/android/layout_tests/fast/dom/wrapper-classes.html +/sdcard/android/layout_tests/fast/dom/select-selectedIndex.html +/sdcard/android/layout_tests/fast/dom/document-width-height-force-layout.html +/sdcard/android/layout_tests/fast/dom/gc-acid3.html +/sdcard/android/layout_tests/fast/dom/client-width-height.html +/sdcard/android/layout_tests/fast/dom/global-constructors.html +/sdcard/android/layout_tests/fast/dom/client-width-height-quirks.html +/sdcard/android/layout_tests/fast/dom/javascript-url-crash-function.html +/sdcard/android/layout_tests/fast/dom/location-hash.html +/sdcard/android/layout_tests/fast/dom/constructors-cached.html +/sdcard/android/layout_tests/fast/dom/non-numeric-values-numeric-parameters.html +/sdcard/android/layout_tests/fast/dom/documenturi-can-hold-arbitrary-string.html +/sdcard/android/layout_tests/fast/dom/documenturi-not-affected-by-base-tag.html +/sdcard/android/layout_tests/fast/dom/open-and-close-by-DOM.html +/sdcard/android/layout_tests/fast/dom/set-frame-src-while-running-script-in-frame.html +/sdcard/android/layout_tests/fast/dom/document_write_params.html +/sdcard/android/layout_tests/fast/dom/namednodemap-namelookup.html +/sdcard/android/layout_tests/fast/dom/null-document-location-replace-crash.html +/sdcard/android/layout_tests/fast/dom/htmlcollection-detectability.html +/sdcard/android/layout_tests/fast/dom/documenturi-assigned-junk-implies-relative-urls-do-not-resolve.html +/sdcard/android/layout_tests/fast/dom/collection-namedItem-via-item.html +/sdcard/android/layout_tests/fast/dom/set-inner-text-newlines.html +/sdcard/android/layout_tests/fast/dom/document-dir-property.html +/sdcard/android/layout_tests/fast/dom/undetectable-style-filter.html +/sdcard/android/layout_tests/fast/dom/tabindex-clamp.html +/sdcard/android/layout_tests/fast/dom/constructors-cached-navigate.html +/sdcard/android/layout_tests/fast/dom/frame-loading-via-document-write.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/od_20000608.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/namespace-nodes.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/rs_20010831.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/sr_20021217.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/kd_20010423.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Borrowed/cz_20030217.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_boolean_expr.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_core_functions.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_location_path.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_node_test.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_literal_expr.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_predicate_list.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_parser.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_nodeset_expr.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_numeric_expr.html +/sdcard/android/layout_tests/fast/xpath/4XPath/Core/test_step.html +/sdcard/android/layout_tests/fast/xpath/ancestor-axis.html +/sdcard/android/layout_tests/fast/xpath/string-value.html +/sdcard/android/layout_tests/fast/xpath/name-null-namespace.html +/sdcard/android/layout_tests/fast/xpath/id-simple.html +/sdcard/android/layout_tests/fast/xpath/id-path.html +/sdcard/android/layout_tests/fast/xpath/implicit-node-args.html +/sdcard/android/layout_tests/fast/xpath/text-nodes.html +/sdcard/android/layout_tests/fast/xpath/invalid-functions.html +/sdcard/android/layout_tests/fast/xpath/xpath-namespaces.html +/sdcard/android/layout_tests/fast/xpath/xpath-functional-test.html +/sdcard/android/layout_tests/fast/xpath/complex-id.html +/sdcard/android/layout_tests/fast/xpath/attr-namespace.html +/sdcard/android/layout_tests/fast/xpath/substring-after.html +/sdcard/android/layout_tests/fast/xpath/evaluate-twice.html +/sdcard/android/layout_tests/fast/xpath/empty-string-substring.html +/sdcard/android/layout_tests/fast/xpath/document-order.html +/sdcard/android/layout_tests/fast/xpath/nodeset-duplicates.html +/sdcard/android/layout_tests/fast/xpath/nan-to-boolean.html +/sdcard/android/layout_tests/fast/xpath/reverse-axes.html +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain.html +/sdcard/android/layout_tests/fast/forms/mailto/advanced-get.html +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain-latin-1.html +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-multipart-form-data.html +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items.html +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-always-utf-8.html +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items.html +/sdcard/android/layout_tests/fast/forms/mailto/post-append-query.html +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-x-www-form-urlencoded.html +/sdcard/android/layout_tests/fast/forms/mailto/get-overwrite-query.html +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-x-www-form-urlencoded.html +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-text-plain.html +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-text-plain.html +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii.html +/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain-with-accept-charset.html +/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain.html +/sdcard/android/layout_tests/fast/forms/mailto/advanced-put.html +/sdcard/android/layout_tests/fast/forms/listbox-typeahead-scroll.html +/sdcard/android/layout_tests/fast/forms/select-empty-list.html +/sdcard/android/layout_tests/fast/forms/select-accesskey.html +/sdcard/android/layout_tests/fast/forms/focus2.html +/sdcard/android/layout_tests/fast/forms/password-doubleclick-selection.html +/sdcard/android/layout_tests/fast/forms/textfield-inside-anchor.html +/sdcard/android/layout_tests/fast/forms/textarea-trailing-newline.html +/sdcard/android/layout_tests/fast/forms/legend-access-key.html +/sdcard/android/layout_tests/fast/forms/input-maxlength.html +/sdcard/android/layout_tests/fast/forms/selection-functions.html +/sdcard/android/layout_tests/fast/forms/textfield-to-password-on-focus.html +/sdcard/android/layout_tests/fast/forms/select-reset.html +/sdcard/android/layout_tests/fast/forms/focus-selection-textarea.html +/sdcard/android/layout_tests/fast/forms/enter-clicks-buttons.html +/sdcard/android/layout_tests/fast/forms/radio_checked_name.html +/sdcard/android/layout_tests/fast/forms/radio-check-click-and-drag.html +/sdcard/android/layout_tests/fast/forms/select-double-onchange.html +/sdcard/android/layout_tests/fast/forms/button-enter-click.html +/sdcard/android/layout_tests/fast/forms/search-click-in-placeholder.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-003.html +/sdcard/android/layout_tests/fast/forms/search-hidden-cancel-button.html +/sdcard/android/layout_tests/fast/forms/dragging-to-file-input.html +/sdcard/android/layout_tests/fast/forms/textarea-paste-newline.html +/sdcard/android/layout_tests/fast/forms/drag-into-textarea.html +/sdcard/android/layout_tests/fast/forms/onselect-textfield.html +/sdcard/android/layout_tests/fast/forms/input-implicit-length-limit.html +/sdcard/android/layout_tests/fast/forms/form-and-frame-interaction-retains-values.html +/sdcard/android/layout_tests/fast/forms/slider-transformed.html +/sdcard/android/layout_tests/fast/forms/listbox-select-all.html +/sdcard/android/layout_tests/fast/forms/textfield-onchange-deletion.html +/sdcard/android/layout_tests/fast/forms/focus-control-to-page.html +/sdcard/android/layout_tests/fast/forms/select-type-ahead-non-latin.html +/sdcard/android/layout_tests/fast/forms/textarea-no-scroll-on-blur.html +/sdcard/android/layout_tests/fast/forms/focus-selection-input.html +/sdcard/android/layout_tests/fast/forms/listbox-onchange.html +/sdcard/android/layout_tests/fast/forms/button-spacebar-click.html +/sdcard/android/layout_tests/fast/forms/search-event-delay.html +/sdcard/android/layout_tests/fast/forms/search-cancel-button-mouseup.html +/sdcard/android/layout_tests/fast/forms/select-enter-key.html +/sdcard/android/layout_tests/fast/forms/drag-out-of-textarea.html +/sdcard/android/layout_tests/fast/forms/textarea-hard-linewrap.html +/sdcard/android/layout_tests/fast/forms/dragging-to-disabled-file-input.html +/sdcard/android/layout_tests/fast/forms/input-radio-checked-tab.html +/sdcard/android/layout_tests/fast/forms/plaintext-mode-1.html +/sdcard/android/layout_tests/fast/forms/check-box-enter-key.html +/sdcard/android/layout_tests/fast/forms/input-select-on-click.html +/sdcard/android/layout_tests/fast/forms/button-state-restore.html +/sdcard/android/layout_tests/fast/forms/input-appearance-elementFromPoint.html +/sdcard/android/layout_tests/fast/forms/select-set-inner.html +/sdcard/android/layout_tests/fast/forms/missing-action.html +/sdcard/android/layout_tests/fast/forms/access-key.html +/sdcard/android/layout_tests/fast/forms/textarea-scrolled-endline-caret.html +/sdcard/android/layout_tests/fast/forms/textarea-scrollbar-height.html +/sdcard/android/layout_tests/fast/forms/textarea-type-spaces.html +/sdcard/android/layout_tests/fast/forms/slider-mouse-events.html +/sdcard/android/layout_tests/fast/forms/textarea-selection-preservation.html +/sdcard/android/layout_tests/fast/forms/slider-onchange-event.html +/sdcard/android/layout_tests/fast/forms/add-remove-form-elements-stress-test.html +/sdcard/android/layout_tests/fast/forms/onchange-enter-submit.html +/sdcard/android/layout_tests/fast/forms/textarea-appearance-wrap.html +/sdcard/android/layout_tests/fast/forms/willvalidate-006.html +/sdcard/android/layout_tests/fast/forms/onselect-textarea.html +/sdcard/android/layout_tests/fast/forms/textarea-initial-caret-position.html +/sdcard/android/layout_tests/fast/forms/listbox-selection.html +/sdcard/android/layout_tests/fast/css/variables/color-hex-test.html +/sdcard/android/layout_tests/fast/css/dashboard-region-parser.html +/sdcard/android/layout_tests/fast/css/text-align.html +/sdcard/android/layout_tests/fast/css/hover-affects-child.html +/sdcard/android/layout_tests/fast/css/html-attr-case-sensitivity.html +/sdcard/android/layout_tests/fast/css/computed-style.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-transform.html +/sdcard/android/layout_tests/fast/css/computed-style-without-renderer.html +/sdcard/android/layout_tests/fast/css/invalid-percentage-property.html +/sdcard/android/layout_tests/fast/parser/entity-comment-in-iframe.html +/sdcard/android/layout_tests/fast/parser/external-entities.xml +/sdcard/android/layout_tests/fast/parser/script-tag-with-trailing-slash.html +/sdcard/android/layout_tests/fast/parser/comment-in-iframe.html +/sdcard/android/layout_tests/fast/parser/external-entities-in-xslt.xml +/sdcard/android/layout_tests/fast/parser/xml-declaration-missing-ending-mark.html +/sdcard/android/layout_tests/fast/parser/entity-end-script-tag.html +/sdcard/android/layout_tests/fast/parser/tabindex-parsing.html +/sdcard/android/layout_tests/fast/history/subframe-is-visited.html +/sdcard/android/layout_tests/fast/history/window-open.html +/sdcard/android/layout_tests/fast/history/history_reload.html +/sdcard/android/layout_tests/fast/history/go-back-to-changed-name.html +/sdcard/android/layout_tests/fast/loader/cancel-load-during-port-block-timer.html +/sdcard/android/layout_tests/fast/loader/local-iFrame-source-from-local.html +/sdcard/android/layout_tests/fast/loader/null-request-after-willSendRequest.html +/sdcard/android/layout_tests/fast/loader/stop-provisional-loads.html +/sdcard/android/layout_tests/fast/loader/xmlhttprequest-missing-file-exception.html +/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash-2.html +/sdcard/android/layout_tests/fast/loader/plain-text-document.html +/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash.html +/sdcard/android/layout_tests/fast/loader/frame-creation-removal.html +/sdcard/android/layout_tests/fast/loader/local-image-from-local.html +/sdcard/android/layout_tests/fast/loader/local-CSS-from-local.html +/sdcard/android/layout_tests/fast/loader/local-JavaScript-from-local.html +/sdcard/android/layout_tests/fast/loader/data-url-encoding-svg.html +/sdcard/android/layout_tests/fast/loader/opaque-base-url.html +/sdcard/android/layout_tests/fast/xsl/sort-locale.xml +/sdcard/android/layout_tests/fast/xsl/transformToFragment-XML-declaration.html +/sdcard/android/layout_tests/fast/xsl/extra-lf-at-end.html +/sdcard/android/layout_tests/fast/xsl/xslt-doc-noenc.xml +/sdcard/android/layout_tests/fast/xsl/import-after-comment.xml +/sdcard/android/layout_tests/fast/xsl/xslt-processor.html +/sdcard/android/layout_tests/fast/xsl/sort-unicode.xml +/sdcard/android/layout_tests/fast/xsl/default-html.html +/sdcard/android/layout_tests/fast/xsl/nbsp-in-stylesheet.html +/sdcard/android/layout_tests/fast/xsl/xslt-string-parameters.html +/sdcard/android/layout_tests/fast/xsl/xslt-entity-enc.xml +/sdcard/android/layout_tests/fast/xsl/xslt-text.html +/sdcard/android/layout_tests/fast/xsl/xslt-nested-stylesheets.xml +/sdcard/android/layout_tests/fast/xsl/xslt-url.xml +/sdcard/android/layout_tests/fast/xsl/xslt-doc-enc.xml +/sdcard/android/layout_tests/fast/xsl/xslt-recursion.xml +/sdcard/android/layout_tests/fast/xsl/exslt-node-set.xml +/sdcard/android/layout_tests/fast/xsl/subframe-location.html +/sdcard/android/layout_tests/fast/xsl/xslt-second-level-import.xml +/sdcard/android/layout_tests/fast/xsl/dtd-in-source-document.xml +/sdcard/android/layout_tests/fast/xsl/mozilla-tests.xml +/sdcard/android/layout_tests/fast/canvas/canvas-alphaImageData-behavior.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-non-invertible.html +/sdcard/android/layout_tests/fast/canvas/pointInPath.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-infinity.html +/sdcard/android/layout_tests/fast/canvas/toDataURL-supportedTypes.html +/sdcard/android/layout_tests/fast/canvas/canvas-getImageData.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-skewed.html +/sdcard/android/layout_tests/fast/canvas/canvas-longlived-context.html +/sdcard/android/layout_tests/fast/canvas/canvas-save-restore-with-path.html +/sdcard/android/layout_tests/fast/canvas/set-colors.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-nan.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-identity.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-multiply.html +/sdcard/android/layout_tests/fast/frames/viewsource-empty-attribute-value.html +/sdcard/android/layout_tests/fast/frames/frame-deep-nested-resize.html +/sdcard/android/layout_tests/fast/frames/iframe-window-focus.html +/sdcard/android/layout_tests/fast/frames/frameElement-widthheight.html +/sdcard/android/layout_tests/fast/frames/removal-before-attach-crash.html +/sdcard/android/layout_tests/fast/frames/frame-js-url-clientWidth.html +/sdcard/android/layout_tests/fast/frames/frame-length-fractional.html +/sdcard/android/layout_tests/fast/frames/frame-limit.html +/sdcard/android/layout_tests/fast/frames/frame-display-none-focus.html +/sdcard/android/layout_tests/fast/frames/iframe-name-and-id.html +/sdcard/android/layout_tests/fast/reflections/teardown-crash.html +/sdcard/android/layout_tests/fast/reflections/reflection-computed-style.html +/sdcard/android/layout_tests/transforms/2d/transform-2d.html +/sdcard/android/layout_tests/transforms/2d/compound-2d-transforms.html +/sdcard/android/layout_tests/transforms/2d/transform-value-types.html +/sdcard/android/layout_tests/transforms/2d/computed-style-origin.html +/sdcard/android/layout_tests/transforms/2d/transform-accuracy.html +/sdcard/android/layout_tests/wml/variable-reference-valid.html +/sdcard/android/layout_tests/wml/go-task-get-method-external-deck-with-href.html +/sdcard/android/layout_tests/wml/variable-reference-invalid-character.html +/sdcard/android/layout_tests/wml/go-task-animation.html +/sdcard/android/layout_tests/wml/go-task-get-method-external-deck.html +/sdcard/android/layout_tests/wml/go-task-get-method-same-deck.html +/sdcard/android/layout_tests/animations/animation-css-rule-types.html +/sdcard/android/layout_tests/animations/animation-events-create.html +/sdcard/android/layout_tests/animations/animation-start-event-destroy-renderer.html +/sdcard/android/layout_tests/animations/combo-transform-translate+scale.html +/sdcard/android/layout_tests/animations/simultaneous-start-transform.html +/sdcard/android/layout_tests/animations/lineheight-animation.html +/sdcard/android/layout_tests/animations/import.html +/sdcard/android/layout_tests/animations/simultaneous-start-left.html +/sdcard/android/layout_tests/animations/fill-unset-properties.html +/sdcard/android/layout_tests/animations/keyframes-to-missing.html +/sdcard/android/layout_tests/animations/multiple-keyframes.html +/sdcard/android/layout_tests/animations/matrix-anim.html +/sdcard/android/layout_tests/animations/keyframes-comma-separated.html +/sdcard/android/layout_tests/animations/change-one-anim.html +/sdcard/android/layout_tests/animations/keyframes-rule.html +/sdcard/android/layout_tests/animations/generic-from-to.html +/sdcard/android/layout_tests/animations/big-rotation.html +/sdcard/android/layout_tests/animations/animation-controller-drt-api.html +/sdcard/android/layout_tests/animations/keyframe-timing-functions.html +/sdcard/android/layout_tests/animations/keyframes-from-missing.html +/sdcard/android/layout_tests/animations/transition-and-animation-1.html +/sdcard/android/layout_tests/animations/computed-style.html +/sdcard/android/layout_tests/animations/animation-iteration-event-destroy-renderer.html +/sdcard/android/layout_tests/animations/keyframes.html +/sdcard/android/layout_tests/animations/multiple-animations.html +/sdcard/android/layout_tests/animations/animation-end-event-destroy-renderer.html +/sdcard/android/layout_tests/animations/change-keyframes-name.html +/sdcard/android/layout_tests/animations/combo-transform-rotate+scale.html +/sdcard/android/layout_tests/animations/change-keyframes.html +/sdcard/android/layout_tests/traversal/hixie-node-iterator/010.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/001.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/002.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/003.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/004.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/005.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/006.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/007.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/008.xml +/sdcard/android/layout_tests/traversal/hixie-node-iterator/009.xml +/sdcard/android/layout_tests/storage/domstorage/localstorage/index-get-and-set.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/onstorage-attribute-setattribute.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/clear.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/enumerate-storage.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/simple-usage.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/simple-events.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/onstorage-attribute-markup.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/iframe-events.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/delete-removal.html +/sdcard/android/layout_tests/storage/domstorage/localstorage/window-open.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/index-get-and-set.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/onstorage-attribute-setattribute.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/clear.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/enumerate-storage.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/simple-usage.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/simple-events.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/onstorage-attribute-markup.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/iframe-events.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/delete-removal.html +/sdcard/android/layout_tests/storage/domstorage/sessionstorage/window-open.html +/sdcard/android/layout_tests/storage/domstorage/window-attributes-exist.html +/sdcard/android/layout_tests/storage/open-database-empty-version.html +/sdcard/android/layout_tests/storage/close-during-stress-test.html +/sdcard/android/layout_tests/storage/execute-sql-args.html +/sdcard/android/layout_tests/storage/quota-tracking.html +/sdcard/android/layout_tests/storage/transaction_callback_exception_crash.html +/sdcard/android/layout_tests/storage/multiple-databases-garbage-collection.html +/sdcard/android/layout_tests/storage/empty-statement.html +/sdcard/android/layout_tests/storage/multiple-transactions.html +/sdcard/android/layout_tests/storage/success-callback.html +/sdcard/android/layout_tests/storage/sql-data-types.html +/sdcard/android/layout_tests/security/set-form-autocomplete-attribute.html +/sdcard/android/layout_tests/security/autocomplete-cleared-on-back.html +/sdcard/android/layout_tests/security/block-test.html +/sdcard/android/layout_tests/transitions/transition-shorthand-delay.html +/sdcard/android/layout_tests/transitions/transition-drt-api-delay.html +/sdcard/android/layout_tests/transitions/opacity-transition-zindex.html +/sdcard/android/layout_tests/transitions/shorthand-transitions.html +/sdcard/android/layout_tests/transitions/hang-with-bad-transition-list.html +/sdcard/android/layout_tests/transitions/zero-duration-in-list.html +/sdcard/android/layout_tests/transitions/shorthand-border-transitions.html +/sdcard/android/layout_tests/transitions/zero-duration-with-non-zero-delay-end.html +/sdcard/android/layout_tests/transitions/repeated-firing-background-color.html +/sdcard/android/layout_tests/transitions/start-transform-transition.html diff --git a/tests/DumpRenderTree/assets/results/layout_tests_nontext.txt b/tests/DumpRenderTree/assets/results/layout_tests_nontext.txt index c9e166cbeed9..eb5b6e0846c3 100644 --- a/tests/DumpRenderTree/assets/results/layout_tests_nontext.txt +++ b/tests/DumpRenderTree/assets/results/layout_tests_nontext.txt @@ -1,3 +1,770 @@ +/sdcard/android/layout_tests/webarchive/loading/cache-expired-subresource.html +/sdcard/android/layout_tests/webarchive/test-css-import.html +/sdcard/android/layout_tests/webarchive/test-img-src.html +/sdcard/android/layout_tests/webarchive/archive-empty-frame-dom.html +/sdcard/android/layout_tests/webarchive/test-frameset.html +/sdcard/android/layout_tests/webarchive/test-body-background.html +/sdcard/android/layout_tests/webarchive/test-input-src.html +/sdcard/android/layout_tests/webarchive/archive-empty-frame-source.html +/sdcard/android/layout_tests/webarchive/doctype.html +/sdcard/android/layout_tests/webarchive/test-css-url-resources-inline-styles.html +/sdcard/android/layout_tests/webarchive/test-table-background.html +/sdcard/android/layout_tests/webarchive/test-object-data.html +/sdcard/android/layout_tests/webarchive/test-css-url-resources-in-stylesheets.html +/sdcard/android/layout_tests/webarchive/archive-with-unencoded-url.html +/sdcard/android/layout_tests/webarchive/test-link-href.html +/sdcard/android/layout_tests/webarchive/test-duplicate-resources.html +/sdcard/android/layout_tests/webarchive/test-xml-stylesheet.xml +/sdcard/android/layout_tests/webarchive/test-td-background.html +/sdcard/android/layout_tests/webarchive/test-script-src.html +/sdcard/android/layout_tests/media/video-transformed.html +/sdcard/android/layout_tests/media/video-display-toggle.html +/sdcard/android/layout_tests/media/audio-controls-rendering.html +/sdcard/android/layout_tests/media/video-controls-visible-audio-only.html +/sdcard/android/layout_tests/media/video-zoom.html +/sdcard/android/layout_tests/media/video-controls-rendering.html +/sdcard/android/layout_tests/media/video-aspect-ratio.html +/sdcard/android/layout_tests/media/video-layer-crash.html +/sdcard/android/layout_tests/plugins/netscape-dom-access.html +/sdcard/android/layout_tests/plugins/embed-attributes-style.html +/sdcard/android/layout_tests/editing/input/emacs-ctrl-o.html +/sdcard/android/layout_tests/editing/style/style-3681552-fix-001.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-013.html +/sdcard/android/layout_tests/editing/style/style-3690704-fix.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-005.html +/sdcard/android/layout_tests/editing/style/style-boundary-002.html +/sdcard/android/layout_tests/editing/style/remove-underline-after-paragraph.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-009.html +/sdcard/android/layout_tests/editing/style/5228141.html +/sdcard/android/layout_tests/editing/style/block-style-004.html +/sdcard/android/layout_tests/editing/style/apple-style-editable-mix.html +/sdcard/android/layout_tests/editing/style/unbold-in-bold.html +/sdcard/android/layout_tests/editing/style/typing-style-001.html +/sdcard/android/layout_tests/editing/style/remove-underline-from-stylesheet.html +/sdcard/android/layout_tests/editing/style/relative-font-size-change-004.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-010.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-002.html +/sdcard/android/layout_tests/editing/style/style-3681552-fix-002.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-006.html +/sdcard/android/layout_tests/editing/style/style-boundary-003.html +/sdcard/android/layout_tests/editing/style/block-style-001.html +/sdcard/android/layout_tests/editing/style/smoosh-styles-001.html +/sdcard/android/layout_tests/editing/style/font-family-with-space.html +/sdcard/android/layout_tests/editing/style/5084241.html +/sdcard/android/layout_tests/editing/style/5065910.html +/sdcard/android/layout_tests/editing/style/block-style-005.html +/sdcard/android/layout_tests/editing/style/remove-underline-across-paragraph-in-bold.html +/sdcard/android/layout_tests/editing/style/5279521.html +/sdcard/android/layout_tests/editing/style/non-inheritable-styles.html +/sdcard/android/layout_tests/editing/style/5046875-1.html +/sdcard/android/layout_tests/editing/style/style-3998892-fix.html +/sdcard/android/layout_tests/editing/style/remove-underline-in-bold.html +/sdcard/android/layout_tests/editing/style/typing-style-002.html +/sdcard/android/layout_tests/editing/style/relative-font-size-change-001.html +/sdcard/android/layout_tests/editing/style/4916887.html +/sdcard/android/layout_tests/editing/style/remove-underline-across-paragraph.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-011.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-003.html +/sdcard/android/layout_tests/editing/style/remove-underline.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-007.html +/sdcard/android/layout_tests/editing/style/style-boundary-004.html +/sdcard/android/layout_tests/editing/style/5017613-1.html +/sdcard/android/layout_tests/editing/style/block-style-002.html +/sdcard/android/layout_tests/editing/style/smoosh-styles-002.html +/sdcard/android/layout_tests/editing/style/block-style-006.html +/sdcard/android/layout_tests/editing/style/fontsize-1.html +/sdcard/android/layout_tests/editing/style/5046875-2.html +/sdcard/android/layout_tests/editing/style/typing-style-003.html +/sdcard/android/layout_tests/editing/style/relative-font-size-change-002.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-012.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-004.html +/sdcard/android/layout_tests/editing/style/designmode.html +/sdcard/android/layout_tests/editing/style/block-styles-007.html +/sdcard/android/layout_tests/editing/style/style-boundary-001.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-008.html +/sdcard/android/layout_tests/editing/style/style-boundary-005.html +/sdcard/android/layout_tests/editing/style/underline.html +/sdcard/android/layout_tests/editing/style/5017613-2.html +/sdcard/android/layout_tests/editing/style/block-style-003.html +/sdcard/android/layout_tests/editing/style/smoosh-styles-003.html +/sdcard/android/layout_tests/editing/style/remove-underline-after-paragraph-in-bold.html +/sdcard/android/layout_tests/editing/style/highlight.html +/sdcard/android/layout_tests/editing/style/table-selection.html +/sdcard/android/layout_tests/editing/style/relative-font-size-change-003.html +/sdcard/android/layout_tests/editing/style/create-block-for-style-001.html +/sdcard/android/layout_tests/editing/unsupported-content/table-delete-001.html +/sdcard/android/layout_tests/editing/unsupported-content/table-type-after.html +/sdcard/android/layout_tests/editing/unsupported-content/table-delete-002.html +/sdcard/android/layout_tests/editing/unsupported-content/table-type-before.html +/sdcard/android/layout_tests/editing/unsupported-content/table-delete-003.html +/sdcard/android/layout_tests/editing/unsupported-content/list-delete-001.html +/sdcard/android/layout_tests/editing/unsupported-content/list-type-after.html +/sdcard/android/layout_tests/editing/unsupported-content/list-type-before.html +/sdcard/android/layout_tests/editing/unsupported-content/list-delete-003.html +/sdcard/android/layout_tests/editing/inserting/insert-div-011.html +/sdcard/android/layout_tests/editing/inserting/4960120-1.html +/sdcard/android/layout_tests/editing/inserting/insert-div-023.html +/sdcard/android/layout_tests/editing/inserting/insert-paragraph-04.html +/sdcard/android/layout_tests/editing/inserting/insert-div-007.html +/sdcard/android/layout_tests/editing/inserting/5058163-2.html +/sdcard/android/layout_tests/editing/inserting/insert-div-019.html +/sdcard/android/layout_tests/editing/inserting/insert-br-at-tabspan-003.html +/sdcard/android/layout_tests/editing/inserting/5607069-2.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-003.html +/sdcard/android/layout_tests/editing/inserting/insert-br-003.html +/sdcard/android/layout_tests/editing/inserting/4875189-1.html +/sdcard/android/layout_tests/editing/inserting/typing-003.html +/sdcard/android/layout_tests/editing/inserting/insert-3907422-fix.html +/sdcard/android/layout_tests/editing/inserting/insert-div-020.html +/sdcard/android/layout_tests/editing/inserting/before-after-input-element.html +/sdcard/android/layout_tests/editing/inserting/insert-div-004.html +/sdcard/android/layout_tests/editing/inserting/insert-paragraph-01.html +/sdcard/android/layout_tests/editing/inserting/insert-div-016.html +/sdcard/android/layout_tests/editing/inserting/4959067.html +/sdcard/android/layout_tests/editing/inserting/insert-br-008.html +/sdcard/android/layout_tests/editing/inserting/insert-text-at-tabspan-001.html +/sdcard/android/layout_tests/editing/inserting/insert-div-001.html +/sdcard/android/layout_tests/editing/inserting/paragraph-separator-02.html +/sdcard/android/layout_tests/editing/inserting/insert-div-013.html +/sdcard/android/layout_tests/editing/inserting/insert-div-025.html +/sdcard/android/layout_tests/editing/inserting/insert-3654864-fix.html +/sdcard/android/layout_tests/editing/inserting/insert-div-009.html +/sdcard/android/layout_tests/editing/inserting/return-key-with-selection-002.html +/sdcard/android/layout_tests/editing/inserting/editable-html-element.html +/sdcard/android/layout_tests/editing/inserting/5418891.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-005.html +/sdcard/android/layout_tests/editing/inserting/insert-tab-002.html +/sdcard/android/layout_tests/editing/inserting/insert-br-005.html +/sdcard/android/layout_tests/editing/inserting/line-break.html +/sdcard/android/layout_tests/editing/inserting/5549929-3.html +/sdcard/android/layout_tests/editing/inserting/typing-tab-designmode-forms.html +/sdcard/android/layout_tests/editing/inserting/insert-div-010.html +/sdcard/android/layout_tests/editing/inserting/insert-div-022.html +/sdcard/android/layout_tests/editing/inserting/insert-paragraph-03.html +/sdcard/android/layout_tests/editing/inserting/insert-div-006.html +/sdcard/android/layout_tests/editing/inserting/5058163-1.html +/sdcard/android/layout_tests/editing/inserting/insert-div-018.html +/sdcard/android/layout_tests/editing/inserting/paragraph-separator-in-table-2.html +/sdcard/android/layout_tests/editing/inserting/insert-br-at-tabspan-002.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-002.html +/sdcard/android/layout_tests/editing/inserting/insert-br-002.html +/sdcard/android/layout_tests/editing/inserting/typing-002.html +/sdcard/android/layout_tests/editing/inserting/insert-3800346-fix.html +/sdcard/android/layout_tests/editing/inserting/insert-text-at-tabspan-003.html +/sdcard/android/layout_tests/editing/inserting/typing-around-image-001.html +/sdcard/android/layout_tests/editing/inserting/insert-text-with-newlines.html +/sdcard/android/layout_tests/editing/inserting/insert-div-003.html +/sdcard/android/layout_tests/editing/inserting/insert-div-015.html +/sdcard/android/layout_tests/editing/inserting/insert-at-end-02.html +/sdcard/android/layout_tests/editing/inserting/insert-div-027.html +/sdcard/android/layout_tests/editing/inserting/insert-3659587-fix.html +/sdcard/android/layout_tests/editing/inserting/insert-br-007.html +/sdcard/android/layout_tests/editing/inserting/insert-tab-004.html +/sdcard/android/layout_tests/editing/inserting/editable-inline-element.html +/sdcard/android/layout_tests/editing/inserting/insert-3851164-fix.html +/sdcard/android/layout_tests/editing/inserting/paragraph-separator-01.html +/sdcard/android/layout_tests/editing/inserting/5156401-2.html +/sdcard/android/layout_tests/editing/inserting/4960120-2.html +/sdcard/android/layout_tests/editing/inserting/insert-div-012.html +/sdcard/android/layout_tests/editing/inserting/insert-div-024.html +/sdcard/android/layout_tests/editing/inserting/insert-div-008.html +/sdcard/android/layout_tests/editing/inserting/insert-paragraph-05.html +/sdcard/android/layout_tests/editing/inserting/multiple-lines-selected.html +/sdcard/android/layout_tests/editing/inserting/insert-3778059-fix.html +/sdcard/android/layout_tests/editing/inserting/return-key-with-selection-001.html +/sdcard/android/layout_tests/editing/inserting/5607069-3.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-004.html +/sdcard/android/layout_tests/editing/inserting/insert-br-004.html +/sdcard/android/layout_tests/editing/inserting/insert-tab-001.html +/sdcard/android/layout_tests/editing/inserting/4875189-2.html +/sdcard/android/layout_tests/editing/inserting/5549929-2.html +/sdcard/android/layout_tests/editing/inserting/editing-empty-divs.html +/sdcard/android/layout_tests/editing/inserting/insert-div-021.html +/sdcard/android/layout_tests/editing/inserting/insert-div-005.html +/sdcard/android/layout_tests/editing/inserting/insert-paragraph-02.html +/sdcard/android/layout_tests/editing/inserting/insert-3786362-fix.html +/sdcard/android/layout_tests/editing/inserting/insert-div-017.html +/sdcard/android/layout_tests/editing/inserting/paragraph-separator-in-table-1.html +/sdcard/android/layout_tests/editing/inserting/insert-br-at-tabspan-001.html +/sdcard/android/layout_tests/editing/inserting/insert-space-in-empty-doc.html +/sdcard/android/layout_tests/editing/inserting/insert-after-delete-001.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-001.html +/sdcard/android/layout_tests/editing/inserting/insert-br-001.html +/sdcard/android/layout_tests/editing/inserting/typing-001.html +/sdcard/android/layout_tests/editing/inserting/4278698.html +/sdcard/android/layout_tests/editing/inserting/insert-br-009.html +/sdcard/android/layout_tests/editing/inserting/insert-text-at-tabspan-002.html +/sdcard/android/layout_tests/editing/inserting/5002441.html +/sdcard/android/layout_tests/editing/inserting/insert-div-002.html +/sdcard/android/layout_tests/editing/inserting/paragraph-separator-03.html +/sdcard/android/layout_tests/editing/inserting/12882.html +/sdcard/android/layout_tests/editing/inserting/insert-3775316-fix.html +/sdcard/android/layout_tests/editing/inserting/edited-whitespace-1.html +/sdcard/android/layout_tests/editing/inserting/insert-div-014.html +/sdcard/android/layout_tests/editing/inserting/insert-at-end-01.html +/sdcard/android/layout_tests/editing/inserting/redo.html +/sdcard/android/layout_tests/editing/inserting/insert-div-026.html +/sdcard/android/layout_tests/editing/inserting/4840662.html +/sdcard/android/layout_tests/editing/inserting/typing-around-br-001.html +/sdcard/android/layout_tests/editing/inserting/return-key-with-selection-003.html +/sdcard/android/layout_tests/editing/inserting/insert-br-quoted-006.html +/sdcard/android/layout_tests/editing/inserting/insert-tab-003.html +/sdcard/android/layout_tests/editing/inserting/insert-br-006.html +/sdcard/android/layout_tests/editing/execCommand/5142012-1.html +/sdcard/android/layout_tests/editing/execCommand/hilitecolor.html +/sdcard/android/layout_tests/editing/execCommand/5120591.html +/sdcard/android/layout_tests/editing/execCommand/format-block-with-braces.html +/sdcard/android/layout_tests/editing/execCommand/4920742-1.html +/sdcard/android/layout_tests/editing/execCommand/4924441.html +/sdcard/android/layout_tests/editing/execCommand/create-list-with-hr.html +/sdcard/android/layout_tests/editing/execCommand/5207369.html +/sdcard/android/layout_tests/editing/execCommand/format-block-with-trailing-br.html +/sdcard/android/layout_tests/editing/execCommand/4916583.html +/sdcard/android/layout_tests/editing/execCommand/remove-formatting-2.html +/sdcard/android/layout_tests/editing/execCommand/outdent-selection.html +/sdcard/android/layout_tests/editing/execCommand/strikethroughSelection.html +/sdcard/android/layout_tests/editing/execCommand/insert-list-with-id.html +/sdcard/android/layout_tests/editing/execCommand/5432254-1.html +/sdcard/android/layout_tests/editing/execCommand/5482023.html +/sdcard/android/layout_tests/editing/execCommand/5062376.html +/sdcard/android/layout_tests/editing/execCommand/4786404-1.html +/sdcard/android/layout_tests/editing/execCommand/remove-formatting.html +/sdcard/android/layout_tests/editing/execCommand/findString.html +/sdcard/android/layout_tests/editing/execCommand/5142012-3.html +/sdcard/android/layout_tests/editing/execCommand/5700414-1.html +/sdcard/android/layout_tests/editing/execCommand/italicizeByCharacter.html +/sdcard/android/layout_tests/editing/execCommand/switch-list-type.html +/sdcard/android/layout_tests/editing/execCommand/create-list-from-range-selection.html +/sdcard/android/layout_tests/editing/execCommand/5190926.html +/sdcard/android/layout_tests/editing/execCommand/remove-list-1.html +/sdcard/android/layout_tests/editing/execCommand/insertHorizontalRule.html +/sdcard/android/layout_tests/editing/execCommand/paste-1.html +/sdcard/android/layout_tests/editing/execCommand/nsresponder-outdent.html +/sdcard/android/layout_tests/editing/execCommand/5080333-2.html +/sdcard/android/layout_tests/editing/execCommand/5119244.html +/sdcard/android/layout_tests/editing/execCommand/5049671.html +/sdcard/android/layout_tests/editing/execCommand/4920488.html +/sdcard/android/layout_tests/editing/execCommand/5164796.html +/sdcard/android/layout_tests/editing/execCommand/nsresponder-indent.html +/sdcard/android/layout_tests/editing/execCommand/indent-list-item.html +/sdcard/android/layout_tests/editing/execCommand/insert-list-empty-div.html +/sdcard/android/layout_tests/editing/execCommand/5138441.html +/sdcard/android/layout_tests/editing/execCommand/4916402.html +/sdcard/android/layout_tests/editing/execCommand/5481523.html +/sdcard/android/layout_tests/editing/execCommand/print.html +/sdcard/android/layout_tests/editing/execCommand/4641880-2.html +/sdcard/android/layout_tests/editing/execCommand/4580583-2.html +/sdcard/android/layout_tests/editing/execCommand/remove-list-item-1.html +/sdcard/android/layout_tests/editing/execCommand/5569741.html +/sdcard/android/layout_tests/editing/execCommand/findString-2.html +/sdcard/android/layout_tests/editing/execCommand/modifyForeColorByCharacter.html +/sdcard/android/layout_tests/editing/execCommand/5142012-2.html +/sdcard/android/layout_tests/editing/execCommand/find-after-replace.html +/sdcard/android/layout_tests/editing/execCommand/format-block.html +/sdcard/android/layout_tests/editing/execCommand/5080333-1.html +/sdcard/android/layout_tests/editing/execCommand/remove-list-from-range-selection.html +/sdcard/android/layout_tests/editing/execCommand/remove-list-items.html +/sdcard/android/layout_tests/editing/execCommand/5432254-2.html +/sdcard/android/layout_tests/editing/execCommand/5144139-1.html +/sdcard/android/layout_tests/editing/execCommand/5136770.html +/sdcard/android/layout_tests/editing/execCommand/4916541.html +/sdcard/android/layout_tests/editing/execCommand/4786404-2.html +/sdcard/android/layout_tests/editing/execCommand/insertImage.html +/sdcard/android/layout_tests/editing/execCommand/insert-list-and-stitch.html +/sdcard/android/layout_tests/editing/execCommand/5573879.html +/sdcard/android/layout_tests/editing/execCommand/5210032.html +/sdcard/android/layout_tests/editing/execCommand/5700414-2.html +/sdcard/android/layout_tests/editing/execCommand/boldSelection.html +/sdcard/android/layout_tests/editing/execCommand/insertHTML.html +/sdcard/android/layout_tests/editing/execCommand/4641880-1.html +/sdcard/android/layout_tests/editing/execCommand/4747450.html +/sdcard/android/layout_tests/editing/execCommand/4580583-1.html +/sdcard/android/layout_tests/editing/execCommand/format-block-from-range-selection.html +/sdcard/android/layout_tests/editing/execCommand/indent-empty-root.html +/sdcard/android/layout_tests/editing/execCommand/indent-selection.html +/sdcard/android/layout_tests/editing/execCommand/selectAll.html +/sdcard/android/layout_tests/editing/execCommand/paste-2.html +/sdcard/android/layout_tests/editing/pasteboard/subframe-dragndrop-1.html +/sdcard/android/layout_tests/editing/pasteboard/innerText-inline-table.html +/sdcard/android/layout_tests/editing/pasteboard/5156401-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-list-001.html +/sdcard/android/layout_tests/editing/pasteboard/5032095.html +/sdcard/android/layout_tests/editing/pasteboard/4242293-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-004.html +/sdcard/android/layout_tests/editing/pasteboard/5247341.html +/sdcard/android/layout_tests/editing/pasteboard/paste-4035648-fix.html +/sdcard/android/layout_tests/editing/pasteboard/4700297.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-003.html +/sdcard/android/layout_tests/editing/pasteboard/drop-link.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-into-blockquote-3.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-into-blockquote.html +/sdcard/android/layout_tests/editing/pasteboard/4944770-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-015.html +/sdcard/android/layout_tests/editing/pasteboard/5075944.html +/sdcard/android/layout_tests/editing/pasteboard/paste-match-style-001.html +/sdcard/android/layout_tests/editing/pasteboard/paste-4039777-fix.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-3.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-007.html +/sdcard/android/layout_tests/editing/pasteboard/5483567.html +/sdcard/android/layout_tests/editing/pasteboard/7955.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-blockquote.html +/sdcard/android/layout_tests/editing/pasteboard/testcase-9507.html +/sdcard/android/layout_tests/editing/pasteboard/input-field-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-001.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-at-tabspan-001.html +/sdcard/android/layout_tests/editing/pasteboard/interchange-newline-2.html +/sdcard/android/layout_tests/editing/pasteboard/block-wrappers-necessary.html +/sdcard/android/layout_tests/editing/pasteboard/4861080.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-009.html +/sdcard/android/layout_tests/editing/pasteboard/merge-after-delete.html +/sdcard/android/layout_tests/editing/pasteboard/5478250.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-012.html +/sdcard/android/layout_tests/editing/pasteboard/prevent-block-nesting-01.html +/sdcard/android/layout_tests/editing/pasteboard/8145-2.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-borders.html +/sdcard/android/layout_tests/editing/pasteboard/5027857.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-004.html +/sdcard/android/layout_tests/editing/pasteboard/merge-start-list.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-008.html +/sdcard/android/layout_tests/editing/pasteboard/4806874.html +/sdcard/android/layout_tests/editing/pasteboard/emacs-ctrl-a-k-y.html +/sdcard/android/layout_tests/editing/pasteboard/bad-placeholder.html +/sdcard/android/layout_tests/editing/pasteboard/displaced-placeholder.html +/sdcard/android/layout_tests/editing/pasteboard/5387578.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-010.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-006.html +/sdcard/android/layout_tests/editing/pasteboard/5601583-1.html +/sdcard/android/layout_tests/editing/pasteboard/4947130.html +/sdcard/android/layout_tests/editing/pasteboard/pasting-tabs.html +/sdcard/android/layout_tests/editing/pasteboard/merge-after-delete-2.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-001.html +/sdcard/android/layout_tests/editing/pasteboard/4076267-2.html +/sdcard/android/layout_tests/editing/pasteboard/paste-table-001.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-005.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-017.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-5.html +/sdcard/android/layout_tests/editing/pasteboard/drop-text-without-selection.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-at-tabspan-003.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-003.html +/sdcard/android/layout_tests/editing/pasteboard/drag-drop-modifies-page.html +/sdcard/android/layout_tests/editing/pasteboard/emacs-ctrl-k-y-001.html +/sdcard/android/layout_tests/editing/pasteboard/5071074.html +/sdcard/android/layout_tests/editing/pasteboard/interchange-newline-4.html +/sdcard/android/layout_tests/editing/pasteboard/styled-element-markup.html +/sdcard/android/layout_tests/editing/pasteboard/paste-4038267-fix.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-002.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-into-blockquote-2.html +/sdcard/android/layout_tests/editing/pasteboard/paste-pre-002.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-014.html +/sdcard/android/layout_tests/editing/pasteboard/drag-drop-dead-frame.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-2.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-006.html +/sdcard/android/layout_tests/editing/pasteboard/5368833.html +/sdcard/android/layout_tests/editing/pasteboard/select-element-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-3.html +/sdcard/android/layout_tests/editing/pasteboard/4641033.html +/sdcard/android/layout_tests/editing/pasteboard/drag-image-to-contenteditable-in-iframe.html +/sdcard/android/layout_tests/editing/pasteboard/interchange-newline-1.html +/sdcard/android/layout_tests/editing/pasteboard/copy-paste-bidi.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-008.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-011.html +/sdcard/android/layout_tests/editing/pasteboard/8145-1.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-003.html +/sdcard/android/layout_tests/editing/pasteboard/paste-table-003.html +/sdcard/android/layout_tests/editing/pasteboard/5075944-3.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-007.html +/sdcard/android/layout_tests/editing/pasteboard/paste-TIFF.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-019.html +/sdcard/android/layout_tests/editing/pasteboard/undoable-fragment-removes.html +/sdcard/android/layout_tests/editing/pasteboard/nested-blocks-with-text-field.html +/sdcard/android/layout_tests/editing/pasteboard/4989774.html +/sdcard/android/layout_tests/editing/pasteboard/paste-unrendered-select.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-005.html +/sdcard/android/layout_tests/editing/pasteboard/emacs-cntl-y-001.html +/sdcard/android/layout_tests/editing/pasteboard/merge-after-delete-1.html +/sdcard/android/layout_tests/editing/pasteboard/unrendered-br.html +/sdcard/android/layout_tests/editing/pasteboard/4631972.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-004.html +/sdcard/android/layout_tests/editing/pasteboard/quirks-mode-br-1.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-into-blockquote-4.html +/sdcard/android/layout_tests/editing/pasteboard/4944770-2.html +/sdcard/android/layout_tests/editing/pasteboard/paste-table-cells.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-016.html +/sdcard/android/layout_tests/editing/pasteboard/paste-match-style-002.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-4.html +/sdcard/android/layout_tests/editing/pasteboard/drag-selected-image-to-contenteditable.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-008.html +/sdcard/android/layout_tests/editing/pasteboard/3976872.html +/sdcard/android/layout_tests/editing/pasteboard/pasting-object.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-list.html +/sdcard/android/layout_tests/editing/pasteboard/copy-standalone-image.html +/sdcard/android/layout_tests/editing/pasteboard/displaced-generic-placeholder.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-002.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-at-tabspan-002.html +/sdcard/android/layout_tests/editing/pasteboard/interchange-newline-3.html +/sdcard/android/layout_tests/editing/pasteboard/5071074-2.html +/sdcard/android/layout_tests/editing/pasteboard/display-block-on-spans.html +/sdcard/android/layout_tests/editing/pasteboard/4242293.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-001.html +/sdcard/android/layout_tests/editing/pasteboard/paste-pre-001.html +/sdcard/android/layout_tests/editing/pasteboard/merge-start-blockquote.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-1.html +/sdcard/android/layout_tests/editing/pasteboard/8145-3.html +/sdcard/android/layout_tests/editing/pasteboard/5006779.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-005.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-009.html +/sdcard/android/layout_tests/editing/pasteboard/paste-blockquote-2.html +/sdcard/android/layout_tests/editing/pasteboard/merge-end-table.html +/sdcard/android/layout_tests/editing/pasteboard/5065605.html +/sdcard/android/layout_tests/editing/pasteboard/paste-line-endings-007.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-010.html +/sdcard/android/layout_tests/editing/pasteboard/5028447.html +/sdcard/android/layout_tests/editing/pasteboard/nested-blocks-with-text-area.html +/sdcard/android/layout_tests/editing/pasteboard/4076267-3.html +/sdcard/android/layout_tests/editing/pasteboard/4076267.html +/sdcard/android/layout_tests/editing/pasteboard/smart-paste-002.html +/sdcard/android/layout_tests/editing/pasteboard/5075944-2.html +/sdcard/android/layout_tests/editing/pasteboard/5134759.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-006.html +/sdcard/android/layout_tests/editing/pasteboard/paste-text-018.html +/sdcard/android/layout_tests/editing/pasteboard/paste-RTFD.html +/sdcard/android/layout_tests/editing/pasteboard/cut-text-001.html +/sdcard/android/layout_tests/editing/selection/image-before-linebreak.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-9.html +/sdcard/android/layout_tests/editing/selection/move-between-blocks-no-001.html +/sdcard/android/layout_tests/editing/selection/fake-drag.html +/sdcard/android/layout_tests/editing/selection/wrapped-line-caret-2.html +/sdcard/android/layout_tests/editing/selection/5131716-3.html +/sdcard/android/layout_tests/editing/selection/5081257-2.html +/sdcard/android/layout_tests/editing/selection/3690703-2.html +/sdcard/android/layout_tests/editing/selection/unrendered-002.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-005.html +/sdcard/android/layout_tests/editing/selection/5354455-1.html +/sdcard/android/layout_tests/editing/selection/caret-rtl-2.html +/sdcard/android/layout_tests/editing/selection/paragraph-granularity.html +/sdcard/android/layout_tests/editing/selection/expanding-selections2.html +/sdcard/android/layout_tests/editing/selection/clear-selection.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-6.html +/sdcard/android/layout_tests/editing/selection/display-table-text.html +/sdcard/android/layout_tests/editing/selection/13804.html +/sdcard/android/layout_tests/editing/selection/drag-in-iframe.html +/sdcard/android/layout_tests/editing/selection/table-caret-2.html +/sdcard/android/layout_tests/editing/selection/expanding-selections.html +/sdcard/android/layout_tests/editing/selection/unrendered-space.html +/sdcard/android/layout_tests/editing/selection/node-removal-1.html +/sdcard/android/layout_tests/editing/selection/5240265.html +/sdcard/android/layout_tests/editing/selection/select-all-005.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-002.html +/sdcard/android/layout_tests/editing/selection/4932260-2.html +/sdcard/android/layout_tests/editing/selection/5076323-3.html +/sdcard/android/layout_tests/editing/selection/select-element-paragraph-boundary.html +/sdcard/android/layout_tests/editing/selection/line-wrap-1.html +/sdcard/android/layout_tests/editing/selection/replace-selection-1.html +/sdcard/android/layout_tests/editing/selection/caret-rtl.html +/sdcard/android/layout_tests/editing/selection/5109817.html +/sdcard/android/layout_tests/editing/selection/extend-by-sentence-001.html +/sdcard/android/layout_tests/editing/selection/3690719.html +/sdcard/android/layout_tests/editing/selection/5136696.html +/sdcard/android/layout_tests/editing/selection/editable-non-editable-crash.html +/sdcard/android/layout_tests/editing/selection/4397952.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-3.html +/sdcard/android/layout_tests/editing/selection/caret-and-focus-ring.html +/sdcard/android/layout_tests/editing/selection/4895428-4.html +/sdcard/android/layout_tests/editing/selection/replaced-boundaries-3.html +/sdcard/android/layout_tests/editing/selection/4947387.html +/sdcard/android/layout_tests/editing/selection/move-by-sentence-linebreak.html +/sdcard/android/layout_tests/editing/selection/move-by-character-005.html +/sdcard/android/layout_tests/editing/selection/select-from-textfield-outwards.html +/sdcard/android/layout_tests/editing/selection/leave-requested-block.html +/sdcard/android/layout_tests/editing/selection/select-all-002.html +/sdcard/android/layout_tests/editing/selection/end-of-document.html +/sdcard/android/layout_tests/editing/selection/selectNode.html +/sdcard/android/layout_tests/editing/selection/editable-links.html +/sdcard/android/layout_tests/editing/selection/unrendered-004.html +/sdcard/android/layout_tests/editing/selection/7152-2.html +/sdcard/android/layout_tests/editing/selection/5195166-1.html +/sdcard/android/layout_tests/editing/selection/editable-html-element.html +/sdcard/android/layout_tests/editing/selection/4895428-1.html +/sdcard/android/layout_tests/editing/selection/4866671.html +/sdcard/android/layout_tests/editing/selection/move-by-character-002.html +/sdcard/android/layout_tests/editing/selection/extend-by-word-002.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-8.html +/sdcard/android/layout_tests/editing/selection/wrapped-line-caret-1.html +/sdcard/android/layout_tests/editing/selection/selection-actions.html +/sdcard/android/layout_tests/editing/selection/14971.html +/sdcard/android/layout_tests/editing/selection/5131716-2.html +/sdcard/android/layout_tests/editing/selection/4402375.html +/sdcard/android/layout_tests/editing/selection/unrendered-001.html +/sdcard/android/layout_tests/editing/selection/move-3875641-fix.html +/sdcard/android/layout_tests/editing/selection/5081257-1.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-004.html +/sdcard/android/layout_tests/editing/selection/5099303.html +/sdcard/android/layout_tests/editing/selection/contenteditable-click-inside.html +/sdcard/android/layout_tests/editing/selection/move-by-line-002.html +/sdcard/android/layout_tests/editing/selection/addRange.html +/sdcard/android/layout_tests/editing/selection/select-missing-image.html +/sdcard/android/layout_tests/editing/selection/selection-3748164-fix.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-5.html +/sdcard/android/layout_tests/editing/selection/4983858.html +/sdcard/android/layout_tests/editing/selection/move-by-sentence-001.html +/sdcard/android/layout_tests/editing/selection/table-caret-1.html +/sdcard/android/layout_tests/editing/selection/5007143.html +/sdcard/android/layout_tests/editing/selection/fake-doubleclick.html +/sdcard/android/layout_tests/editing/selection/move-by-word-001.html +/sdcard/android/layout_tests/editing/selection/select-all-004.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-001.html +/sdcard/android/layout_tests/editing/selection/4932260-1.html +/sdcard/android/layout_tests/editing/selection/5076323-2.html +/sdcard/android/layout_tests/editing/selection/5234383-2.html +/sdcard/android/layout_tests/editing/selection/5057506.html +/sdcard/android/layout_tests/editing/selection/focus_editable_html.html +/sdcard/android/layout_tests/editing/selection/move-3875618-fix.html +/sdcard/android/layout_tests/editing/selection/4818145.html +/sdcard/android/layout_tests/editing/selection/move-backwords-by-word-001.html +/sdcard/android/layout_tests/editing/selection/inline-table.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-2.html +/sdcard/android/layout_tests/editing/selection/4895428-3.html +/sdcard/android/layout_tests/editing/selection/replaced-boundaries-2.html +/sdcard/android/layout_tests/editing/selection/move-by-character-004.html +/sdcard/android/layout_tests/editing/selection/caret-before-select.html +/sdcard/android/layout_tests/editing/selection/4889598.html +/sdcard/android/layout_tests/editing/selection/select-all-001.html +/sdcard/android/layout_tests/editing/selection/5131716-4.html +/sdcard/android/layout_tests/editing/selection/3690703.html +/sdcard/android/layout_tests/editing/selection/5333725.html +/sdcard/android/layout_tests/editing/selection/unrendered-003.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-006.html +/sdcard/android/layout_tests/editing/selection/7152-1.html +/sdcard/android/layout_tests/editing/selection/5213963.html +/sdcard/android/layout_tests/editing/selection/5354455-2.html +/sdcard/android/layout_tests/editing/selection/selection-background.html +/sdcard/android/layout_tests/editing/selection/iframe.html +/sdcard/android/layout_tests/editing/selection/extend-by-word-001.html +/sdcard/android/layout_tests/editing/selection/move-by-character-001.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-7.html +/sdcard/android/layout_tests/editing/selection/doubleclick-crash.html +/sdcard/android/layout_tests/editing/selection/table-caret-3.html +/sdcard/android/layout_tests/editing/selection/5131716-1.html +/sdcard/android/layout_tests/editing/selection/select-all-iframe.html +/sdcard/android/layout_tests/editing/selection/node-removal-2.html +/sdcard/android/layout_tests/editing/selection/drag-select-1.html +/sdcard/android/layout_tests/editing/selection/select-all-006.html +/sdcard/android/layout_tests/editing/selection/extend-by-character-003.html +/sdcard/android/layout_tests/editing/selection/4932260-3.html +/sdcard/android/layout_tests/editing/selection/4960116.html +/sdcard/android/layout_tests/editing/selection/after-line-wrap.html +/sdcard/android/layout_tests/editing/selection/line-wrap-2.html +/sdcard/android/layout_tests/editing/selection/drag-to-contenteditable-iframe.html +/sdcard/android/layout_tests/editing/selection/move-by-line-001.html +/sdcard/android/layout_tests/editing/selection/selectNodeContents.html +/sdcard/android/layout_tests/editing/selection/move-between-blocks-yes-001.html +/sdcard/android/layout_tests/editing/selection/6476.html +/sdcard/android/layout_tests/editing/selection/contains-boundaries.html +/sdcard/android/layout_tests/editing/selection/click-start-of-line.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-4.html +/sdcard/android/layout_tests/editing/selection/focus-body.html +/sdcard/android/layout_tests/editing/selection/triple-click-in-pre.html +/sdcard/android/layout_tests/editing/selection/move-past-trailing-space.html +/sdcard/android/layout_tests/editing/selection/inline-closest-leaf-child.html +/sdcard/android/layout_tests/editing/selection/5007143-2.html +/sdcard/android/layout_tests/editing/selection/designmode-no-caret.html +/sdcard/android/layout_tests/editing/selection/select-all-003.html +/sdcard/android/layout_tests/editing/selection/5076323-1.html +/sdcard/android/layout_tests/editing/selection/5234383-1.html +/sdcard/android/layout_tests/editing/selection/5057506-2.html +/sdcard/android/layout_tests/editing/selection/5232159.html +/sdcard/android/layout_tests/editing/selection/4975120.html +/sdcard/android/layout_tests/editing/selection/4960137.html +/sdcard/android/layout_tests/editing/selection/unrendered-005.html +/sdcard/android/layout_tests/editing/selection/5195166-2.html +/sdcard/android/layout_tests/editing/selection/contenteditable-click-outside.html +/sdcard/android/layout_tests/editing/selection/previous-line-position.html +/sdcard/android/layout_tests/editing/selection/mixed-editability-1.html +/sdcard/android/layout_tests/editing/selection/select-box.html +/sdcard/android/layout_tests/editing/selection/4895428-2.html +/sdcard/android/layout_tests/editing/selection/replaced-boundaries-1.html +/sdcard/android/layout_tests/editing/selection/4776665.html +/sdcard/android/layout_tests/editing/selection/move-by-character-003.html +/sdcard/android/layout_tests/editing/selection/word-granularity.html +/sdcard/android/layout_tests/editing/selection/move-by-character-6.html +/sdcard/android/layout_tests/editing/undo/undo-misspellings.html +/sdcard/android/layout_tests/editing/undo/undo-combined-delete.html +/sdcard/android/layout_tests/editing/undo/undo-delete-boundary.html +/sdcard/android/layout_tests/editing/undo/undo-forward-delete-boundary.html +/sdcard/android/layout_tests/editing/undo/4063751.html +/sdcard/android/layout_tests/editing/undo/redo-typing-001.html +/sdcard/android/layout_tests/editing/undo/5378473.html +/sdcard/android/layout_tests/editing/undo/undo-combined-delete-boundary.html +/sdcard/android/layout_tests/editing/undo/undo-delete.html +/sdcard/android/layout_tests/editing/undo/undo-forward-delete.html +/sdcard/android/layout_tests/editing/undo/undo-typing-001.html +/sdcard/android/layout_tests/editing/deleting/delete-3608462-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-image-001.html +/sdcard/android/layout_tests/editing/deleting/delete-3928305-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-012.html +/sdcard/android/layout_tests/editing/deleting/5115601.html +/sdcard/android/layout_tests/editing/deleting/deletionUI-single-instance.html +/sdcard/android/layout_tests/editing/deleting/delete-block-contents-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-024.html +/sdcard/android/layout_tests/editing/deleting/delete-after-span-ws-001.html +/sdcard/android/layout_tests/editing/deleting/delete-line-011.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-008.html +/sdcard/android/layout_tests/editing/deleting/delete-listitem-001.html +/sdcard/android/layout_tests/editing/deleting/5300379.html +/sdcard/android/layout_tests/editing/deleting/delete-line-007.html +/sdcard/android/layout_tests/editing/deleting/5433862-2.html +/sdcard/android/layout_tests/editing/deleting/5026848-1.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-003.html +/sdcard/android/layout_tests/editing/deleting/delete-br-012.html +/sdcard/android/layout_tests/editing/deleting/delete-br-008.html +/sdcard/android/layout_tests/editing/deleting/5206311-2.html +/sdcard/android/layout_tests/editing/deleting/delete-at-start-or-end.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-021.html +/sdcard/android/layout_tests/editing/deleting/delete-ws-fixup-004.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-005.html +/sdcard/android/layout_tests/editing/deleting/delete-select-all-003.html +/sdcard/android/layout_tests/editing/deleting/smart-delete-002.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-017.html +/sdcard/android/layout_tests/editing/deleting/table-cells.html +/sdcard/android/layout_tests/editing/deleting/5156801-2.html +/sdcard/android/layout_tests/editing/deleting/delete-leading-ws-001.html +/sdcard/android/layout_tests/editing/deleting/delete-line-004.html +/sdcard/android/layout_tests/editing/deleting/delete-3857753-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-3959464-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-line-016.html +/sdcard/android/layout_tests/editing/deleting/delete-trailing-ws-001.html +/sdcard/android/layout_tests/editing/deleting/delete-br-005.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-008.html +/sdcard/android/layout_tests/editing/deleting/delete-tab-004.html +/sdcard/android/layout_tests/editing/deleting/merge-whitespace-pre.html +/sdcard/android/layout_tests/editing/deleting/delete-ws-fixup-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-002.html +/sdcard/android/layout_tests/editing/deleting/delete-line-end-ws-002.html +/sdcard/android/layout_tests/editing/deleting/merge-unrendered-space.html +/sdcard/android/layout_tests/editing/deleting/delete-by-word-002.html +/sdcard/android/layout_tests/editing/deleting/delete-image-003.html +/sdcard/android/layout_tests/editing/deleting/delete-selection-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-014.html +/sdcard/android/layout_tests/editing/deleting/merge-different-styles.html +/sdcard/android/layout_tests/editing/deleting/delete-block-contents-003.html +/sdcard/android/layout_tests/editing/deleting/delete-line-001.html +/sdcard/android/layout_tests/editing/deleting/delete-after-span-ws-003.html +/sdcard/android/layout_tests/editing/deleting/delete-line-013.html +/sdcard/android/layout_tests/editing/deleting/merge-into-empty-block-1.html +/sdcard/android/layout_tests/editing/deleting/5156801.html +/sdcard/android/layout_tests/editing/deleting/delete-3865854-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-line-009.html +/sdcard/android/layout_tests/editing/deleting/5026848-3.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-005.html +/sdcard/android/layout_tests/editing/deleting/delete-br-002.html +/sdcard/android/layout_tests/editing/deleting/delete-tab-001.html +/sdcard/android/layout_tests/editing/deleting/4866671.html +/sdcard/android/layout_tests/editing/deleting/whitespace-pre-1.html +/sdcard/android/layout_tests/editing/deleting/delete-block-table.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-011.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-023.html +/sdcard/android/layout_tests/editing/deleting/delete-line-010.html +/sdcard/android/layout_tests/editing/deleting/5032066.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-007.html +/sdcard/android/layout_tests/editing/deleting/smart-delete-004.html +/sdcard/android/layout_tests/editing/deleting/5144139-2.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-019.html +/sdcard/android/layout_tests/editing/deleting/delete-line-006.html +/sdcard/android/layout_tests/editing/deleting/5126166.html +/sdcard/android/layout_tests/editing/deleting/delete-to-end-of-paragraph.html +/sdcard/android/layout_tests/editing/deleting/5408255.html +/sdcard/android/layout_tests/editing/deleting/5099303.html +/sdcard/android/layout_tests/editing/deleting/4845371.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-002.html +/sdcard/android/layout_tests/editing/deleting/4922367.html +/sdcard/android/layout_tests/editing/deleting/delete-br-011.html +/sdcard/android/layout_tests/editing/deleting/delete-br-007.html +/sdcard/android/layout_tests/editing/deleting/move-nodes-001.html +/sdcard/android/layout_tests/editing/deleting/merge-no-br.html +/sdcard/android/layout_tests/editing/deleting/delete-3800834-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-4038408-fix.html +/sdcard/android/layout_tests/editing/deleting/5206311-1.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-020.html +/sdcard/android/layout_tests/editing/deleting/delete-ws-fixup-003.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-004.html +/sdcard/android/layout_tests/editing/deleting/smart-delete-001.html +/sdcard/android/layout_tests/editing/deleting/delete-to-select-table.html +/sdcard/android/layout_tests/editing/deleting/delete-select-all-002.html +/sdcard/android/layout_tests/editing/deleting/delete-contiguous-ws-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-016.html +/sdcard/android/layout_tests/editing/deleting/delete-line-003.html +/sdcard/android/layout_tests/editing/deleting/delete-line-015.html +/sdcard/android/layout_tests/editing/deleting/5390681.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-011.html +/sdcard/android/layout_tests/editing/deleting/forward-delete.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-007.html +/sdcard/android/layout_tests/editing/deleting/delete-br-004.html +/sdcard/android/layout_tests/editing/deleting/delete-tab-003.html +/sdcard/android/layout_tests/editing/deleting/collapse-whitespace-3587601-fix.html +/sdcard/android/layout_tests/editing/deleting/pruning-after-merge-2.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-001.html +/sdcard/android/layout_tests/editing/deleting/delete-line-end-ws-001.html +/sdcard/android/layout_tests/editing/deleting/delete-by-word-001.html +/sdcard/android/layout_tests/editing/deleting/delete-image-002.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-013.html +/sdcard/android/layout_tests/editing/deleting/delete-block-contents-002.html +/sdcard/android/layout_tests/editing/deleting/paragraph-in-preserveNewline.html +/sdcard/android/layout_tests/editing/deleting/delete-after-span-ws-002.html +/sdcard/android/layout_tests/editing/deleting/5272440.html +/sdcard/android/layout_tests/editing/deleting/delete-line-012.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-009.html +/sdcard/android/layout_tests/editing/deleting/delete-listitem-002.html +/sdcard/android/layout_tests/editing/deleting/delete-character-001.html +/sdcard/android/layout_tests/editing/deleting/delete-line-008.html +/sdcard/android/layout_tests/editing/deleting/5483370.html +/sdcard/android/layout_tests/editing/deleting/5026848-2.html +/sdcard/android/layout_tests/editing/deleting/delete-br-001.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-004.html +/sdcard/android/layout_tests/editing/deleting/5091898.html +/sdcard/android/layout_tests/editing/deleting/delete-br-009.html +/sdcard/android/layout_tests/editing/deleting/transpose-empty.html +/sdcard/android/layout_tests/editing/deleting/merge-endOfParagraph.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-010.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-022.html +/sdcard/android/layout_tests/editing/deleting/delete-3775172-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-mixed-editable-content-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-006.html +/sdcard/android/layout_tests/editing/deleting/smart-delete-003.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-018.html +/sdcard/android/layout_tests/editing/deleting/delete-line-005.html +/sdcard/android/layout_tests/editing/deleting/delete-first-list-item.html +/sdcard/android/layout_tests/editing/deleting/delete-line-017.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-001.html +/sdcard/android/layout_tests/editing/deleting/delete-trailing-ws-002.html +/sdcard/android/layout_tests/editing/deleting/delete-br-010.html +/sdcard/android/layout_tests/editing/deleting/delete-and-undo.html +/sdcard/android/layout_tests/editing/deleting/delete-br-006.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-009.html +/sdcard/android/layout_tests/editing/deleting/delete-hr.html +/sdcard/android/layout_tests/editing/deleting/4875189.html +/sdcard/android/layout_tests/editing/deleting/delete-4083333-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-3608445-fix.html +/sdcard/android/layout_tests/editing/deleting/delete-ws-fixup-002.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-003.html +/sdcard/android/layout_tests/editing/deleting/delete-image-004.html +/sdcard/android/layout_tests/editing/deleting/delete-select-all-001.html +/sdcard/android/layout_tests/editing/deleting/delete-block-merge-contents-015.html +/sdcard/android/layout_tests/editing/deleting/delete-line-002.html +/sdcard/android/layout_tests/editing/deleting/delete-line-014.html +/sdcard/android/layout_tests/editing/deleting/merge-into-empty-block-2.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-010.html +/sdcard/android/layout_tests/editing/deleting/5390681-2.html +/sdcard/android/layout_tests/editing/deleting/5369009.html +/sdcard/android/layout_tests/editing/deleting/delete-br-003.html +/sdcard/android/layout_tests/editing/deleting/delete-at-paragraph-boundaries-006.html +/sdcard/android/layout_tests/editing/deleting/delete-tab-002.html +/sdcard/android/layout_tests/editing/deleting/list-item-1.html +/sdcard/android/layout_tests/editing/deleting/5168598.html +/sdcard/android/layout_tests/editing/deleting/delete-3608430-fix.html +/sdcard/android/layout_tests/editing/spelling/spelling.html +/sdcard/android/layout_tests/editing/spelling/inline_spelling_markers.html +/sdcard/android/layout_tests/printing/media-queries-print.html /sdcard/android/layout_tests/fast/media/mq-relative-constraints-05.html /sdcard/android/layout_tests/fast/media/mq-grid-02.html /sdcard/android/layout_tests/fast/media/mq-compound-query-02.html @@ -144,6 +911,7 @@ /sdcard/android/layout_tests/fast/dynamic/014.html /sdcard/android/layout_tests/fast/dynamic/006.html /sdcard/android/layout_tests/fast/dynamic/flash-replacement-test.html +/sdcard/android/layout_tests/fast/dynamic/insertAdjacentElement.html /sdcard/android/layout_tests/fast/dynamic/insert-before-table-part-in-continuation.html /sdcard/android/layout_tests/fast/dynamic/staticY.html /sdcard/android/layout_tests/fast/dynamic/anonymous-block-orphaned-lines.html @@ -306,6 +1074,7 @@ /sdcard/android/layout_tests/fast/encoding/utf-16-big-endian.html /sdcard/android/layout_tests/fast/encoding/xmacroman-encoding-test.html /sdcard/android/layout_tests/fast/encoding/invalid-UTF-8.html +/sdcard/android/layout_tests/fast/multicol/float-avoidance.html /sdcard/android/layout_tests/fast/multicol/negativeColumnWidth.html /sdcard/android/layout_tests/fast/multicol/column-rules.html /sdcard/android/layout_tests/fast/multicol/zeroColumnCount.html @@ -345,7 +1114,6 @@ /sdcard/android/layout_tests/fast/css-generated-content/007.html /sdcard/android/layout_tests/fast/css-generated-content/009.html /sdcard/android/layout_tests/fast/css-generated-content/wbr-with-before-content.html -/sdcard/android/layout_tests/fast/workers/stress-js-execution.html : has expected results /sdcard/android/layout_tests/fast/lists/decimal-leading-zero.html /sdcard/android/layout_tests/fast/lists/ol-display-types.html /sdcard/android/layout_tests/fast/lists/li-style-alpha-huge-value-crash.html @@ -446,7 +1214,6 @@ /sdcard/android/layout_tests/fast/selectors/044.html /sdcard/android/layout_tests/fast/selectors/008.html /sdcard/android/layout_tests/fast/selectors/072.html -/sdcard/android/layout_tests/fast/selectors/lang-inheritance.html : has expected results /sdcard/android/layout_tests/fast/selectors/064.html /sdcard/android/layout_tests/fast/selectors/056.html /sdcard/android/layout_tests/fast/selectors/018b.html @@ -463,8 +1230,6 @@ /sdcard/android/layout_tests/fast/selectors/077b.html /sdcard/android/layout_tests/fast/selectors/177b.html /sdcard/android/layout_tests/fast/selectors/169a.html -/sdcard/android/layout_tests/fast/selectors/lang-vs-xml-lang.html : has expected results -/sdcard/android/layout_tests/fast/selectors/lang-inheritance2.html : has expected results /sdcard/android/layout_tests/fast/selectors/001.html /sdcard/android/layout_tests/fast/selectors/021.html /sdcard/android/layout_tests/fast/selectors/unqualified-hover-quirks.html @@ -563,19 +1328,13 @@ /sdcard/android/layout_tests/fast/overflow/hidden-scrollbar-resize.html /sdcard/android/layout_tests/fast/events/autoscroll.html /sdcard/android/layout_tests/fast/events/5056619.html -/sdcard/android/layout_tests/fast/events/attempt-scroll-with-no-scrollbars.html : has expected results /sdcard/android/layout_tests/fast/events/updateLayoutForHitTest.html /sdcard/android/layout_tests/fast/events/label-focus.html -/sdcard/android/layout_tests/fast/events/context-onmousedown-event.html : has expected results -/sdcard/android/layout_tests/fast/events/onsubmit-bubbling.html : has expected results /sdcard/android/layout_tests/fast/events/onloadFrameCrash.html /sdcard/android/layout_tests/fast/events/event-listener-on-link.html /sdcard/android/layout_tests/fast/events/keydown-1.html /sdcard/android/layout_tests/fast/events/onload-re-entry.html /sdcard/android/layout_tests/fast/events/standalone-image-drag-to-editable.html -/sdcard/android/layout_tests/fast/events/pointer-events.html : has expected results -/sdcard/android/layout_tests/fast/events/pointer-events-2.html : has expected results -/sdcard/android/layout_tests/fast/events/nested-window-event.html : has expected results /sdcard/android/layout_tests/fast/events/focusingUnloadedFrame.html /sdcard/android/layout_tests/fast/events/event-sender-mouse-moved.html /sdcard/android/layout_tests/fast/events/mouseout-dead-node.html @@ -655,6 +1414,8 @@ /sdcard/android/layout_tests/fast/box-shadow/border-radius-big.html /sdcard/android/layout_tests/fast/box-shadow/basic-shadows.html /sdcard/android/layout_tests/fast/js/exception-linenums-in-html-3.html +/sdcard/android/layout_tests/fast/js/missing-style-end-tag-js.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/010.html /sdcard/android/layout_tests/fast/body-propagation/background-image/001.html /sdcard/android/layout_tests/fast/body-propagation/background-image/002.html /sdcard/android/layout_tests/fast/body-propagation/background-image/003.html @@ -698,21 +1459,13 @@ /sdcard/android/layout_tests/fast/dom/HTMLTableElement/colSpan.html /sdcard/android/layout_tests/fast/dom/HTMLTableElement/createCaption.html /sdcard/android/layout_tests/fast/dom/HTMLDocument/frameless-location-bugzilla10837.html -/sdcard/android/layout_tests/fast/dom/Document/early-document-access.html : has expected results /sdcard/android/layout_tests/fast/dom/Element/null-offset-parent.html /sdcard/android/layout_tests/fast/dom/Element/class-attribute-whitespace.html /sdcard/android/layout_tests/fast/dom/HTMLLinkElement/pending-stylesheet-count.html /sdcard/android/layout_tests/fast/dom/HTMLStyleElement/insert-parser-generated.html /sdcard/android/layout_tests/fast/dom/HTMLInputElement/input-image-alt-text.html -/sdcard/android/layout_tests/fast/dom/Window/timeout-released-on-close.html : has expected results /sdcard/android/layout_tests/fast/dom/Window/open-existing-pop-up-blocking.html -/sdcard/android/layout_tests/fast/dom/Window/global-opener-function.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/closure-access-after-navigation-window.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/remove-timeout-crash.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/window-special-properties.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/timeout-callback-scope.html : has expected results -/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing-name.html : has expected results +/sdcard/android/layout_tests/fast/dom/Window/btoa-pnglet.html /sdcard/android/layout_tests/fast/dom/HTMLObjectElement/vspace-hspace-as-number.html /sdcard/android/layout_tests/fast/dom/HTMLElement/bdo.html /sdcard/android/layout_tests/fast/dom/Range/surroundContents-1.html @@ -742,10 +1495,7 @@ /sdcard/android/layout_tests/fast/dom/inner-text.html /sdcard/android/layout_tests/fast/dom/isindex-002.html /sdcard/android/layout_tests/fast/dom/outerText.html -/sdcard/android/layout_tests/fast/dom/setAttributeNS.html : has expected results -/sdcard/android/layout_tests/fast/dom/anchor-toString.html : has expected results -/sdcard/android/layout_tests/fast/dom/documenturi-affects-relative-paths.html : has expected results -/sdcard/android/layout_tests/fast/dom/setAttribute-using-initial-input-value.html : has expected results +/sdcard/android/layout_tests/fast/dom/row-inner-text.html /sdcard/android/layout_tests/fast/dom/blur-contenteditable.html /sdcard/android/layout_tests/fast/dom/setPrimitiveValue.html /sdcard/android/layout_tests/fast/dom/delete-contents.html @@ -756,11 +1506,13 @@ /sdcard/android/layout_tests/fast/dom/comment-not-documentElement.html /sdcard/android/layout_tests/fast/dom/clone-node-dynamic-style.html /sdcard/android/layout_tests/fast/dom/css-mediarule-insertRule-update.html -/sdcard/android/layout_tests/fast/dom/set-frame-src-while-running-script-in-frame.html : has expected results +/sdcard/android/layout_tests/fast/dom/css-insert-import-rule.html /sdcard/android/layout_tests/fast/dom/stripNullFromTextNodes.html -/sdcard/android/layout_tests/fast/dom/null-document-location-put-crash.html : has expected results -/sdcard/android/layout_tests/fast/dom/document-scripts.html : has expected results -/sdcard/android/layout_tests/fast/invalid/test-case-tr-th-td-should-not-close-dl-list.html : has expected results +/sdcard/android/layout_tests/fast/gradients/generated-gradients.html +/sdcard/android/layout_tests/fast/gradients/list-item-gradient.html +/sdcard/android/layout_tests/fast/gradients/border-image-gradient-sides-and-corners.html +/sdcard/android/layout_tests/fast/gradients/simple-gradients.html +/sdcard/android/layout_tests/fast/gradients/border-image-gradient.html /sdcard/android/layout_tests/fast/invalid/table-inside-stray-table-content.html /sdcard/android/layout_tests/fast/invalid/010.html /sdcard/android/layout_tests/fast/invalid/002.html @@ -801,14 +1553,14 @@ /sdcard/android/layout_tests/fast/forms/plaintext-mode-2.html /sdcard/android/layout_tests/fast/forms/input-double-click-selection-gap-bug.html /sdcard/android/layout_tests/fast/forms/preserveFormDuringResidualStyle.html +/sdcard/android/layout_tests/fast/forms/search-rtl.html /sdcard/android/layout_tests/fast/forms/select-change-popup-to-listbox.html /sdcard/android/layout_tests/fast/forms/input-text-maxlength.html /sdcard/android/layout_tests/fast/forms/blankbuttons.html /sdcard/android/layout_tests/fast/forms/password-placeholder-text-security.html /sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label05.html /sdcard/android/layout_tests/fast/forms/visual-hebrew-text-field.html -/sdcard/android/layout_tests/fast/forms/textarea-trailing-newline.html : has expected results -/sdcard/android/layout_tests/fast/forms/legend-access-key.html : has expected results +/sdcard/android/layout_tests/fast/forms/input-text-option-delete.html /sdcard/android/layout_tests/fast/forms/textfield-overflow.html /sdcard/android/layout_tests/fast/forms/005.html /sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label02.html @@ -828,13 +1580,13 @@ /sdcard/android/layout_tests/fast/forms/option-text-clip.html /sdcard/android/layout_tests/fast/forms/textfield-drag-into-disabled.html /sdcard/android/layout_tests/fast/forms/input-text-paste-maxlength.html -/sdcard/android/layout_tests/fast/forms/tabs-with-modifiers.html : has expected results -/sdcard/android/layout_tests/fast/forms/saved-state-adoptNode-crash.html : has expected results +/sdcard/android/layout_tests/fast/forms/formmove.html +/sdcard/android/layout_tests/fast/forms/select-display-none-style-resolve.html /sdcard/android/layout_tests/fast/forms/input-readonly-autoscroll.html /sdcard/android/layout_tests/fast/forms/thumbslider-no-parent-slider.html /sdcard/android/layout_tests/fast/forms/textfield-outline.html /sdcard/android/layout_tests/fast/forms/button-text-transform.html -/sdcard/android/layout_tests/fast/forms/radio-check-click-and-drag.html : has expected results +/sdcard/android/layout_tests/fast/forms/listbox-clip.html /sdcard/android/layout_tests/fast/forms/textarea-scroll-height.html /sdcard/android/layout_tests/fast/forms/button-table-styles.html /sdcard/android/layout_tests/fast/forms/textarea-setinnerhtml.html @@ -842,14 +1594,16 @@ /sdcard/android/layout_tests/fast/forms/box-shadow-override.html /sdcard/android/layout_tests/fast/forms/button-cannot-be-nested.html /sdcard/android/layout_tests/fast/forms/input-type-change.html +/sdcard/android/layout_tests/fast/forms/searchfield-heights.html /sdcard/android/layout_tests/fast/forms/checkbox-radio-onchange.html /sdcard/android/layout_tests/fast/forms/input-spaces.html -/sdcard/android/layout_tests/fast/forms/dragging-to-file-input.html : has expected results /sdcard/android/layout_tests/fast/forms/menulist-option-wrap.html +/sdcard/android/layout_tests/fast/forms/select-empty-option-height.html +/sdcard/android/layout_tests/fast/forms/textarea-scrollbar.html /sdcard/android/layout_tests/fast/forms/float-before-fieldset.html /sdcard/android/layout_tests/fast/forms/radio_checked.html /sdcard/android/layout_tests/fast/forms/minWidthPercent.html -/sdcard/android/layout_tests/fast/forms/form-post-urlencoded.html : has expected results +/sdcard/android/layout_tests/fast/forms/input-appearance-focus.html /sdcard/android/layout_tests/fast/forms/input-value.html /sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label06.html /sdcard/android/layout_tests/fast/forms/placeholder-pseudo-style.html @@ -868,11 +1622,16 @@ /sdcard/android/layout_tests/fast/forms/input-text-click-outside.html /sdcard/android/layout_tests/fast/forms/input-baseline.html /sdcard/android/layout_tests/fast/forms/targeted-frame-submission.html +/sdcard/android/layout_tests/fast/forms/input-text-scroll-left-on-blur.html +/sdcard/android/layout_tests/fast/forms/form-element-geometry.html /sdcard/android/layout_tests/fast/forms/input-table.html +/sdcard/android/layout_tests/fast/forms/textarea-scrolled-type.html /sdcard/android/layout_tests/fast/forms/select-change-listbox-to-popup.html /sdcard/android/layout_tests/fast/forms/select-align.html /sdcard/android/layout_tests/fast/forms/radio_checked_dynamic.html /sdcard/android/layout_tests/fast/forms/select-writing-direction-natural.html +/sdcard/android/layout_tests/fast/forms/search-cancel-button-style-sharing.html +/sdcard/android/layout_tests/fast/forms/tabbing-input-iframe.html /sdcard/android/layout_tests/fast/forms/hidden-input-file.html /sdcard/android/layout_tests/fast/forms/menulist-deselect-update.html /sdcard/android/layout_tests/fast/forms/button-sizes.html @@ -899,9 +1658,11 @@ /sdcard/android/layout_tests/fast/forms/listbox-selection-2.html /sdcard/android/layout_tests/fast/forms/input-readonly-empty.html /sdcard/android/layout_tests/fast/forms/input-align-image.html -/sdcard/android/layout_tests/fast/forms/input-selection-hidden.html : has expected results +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label07.html /sdcard/android/layout_tests/fast/forms/option-index.html +/sdcard/android/layout_tests/fast/forms/menulist-clip.html /sdcard/android/layout_tests/fast/forms/indeterminate.html +/sdcard/android/layout_tests/fast/forms/search-display-none-cancel-button.html /sdcard/android/layout_tests/fast/forms/negativeLineHeight.html /sdcard/android/layout_tests/fast/forms/007.html /sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label04.html @@ -916,10 +1677,9 @@ /sdcard/android/layout_tests/fast/forms/search-placeholder-value-changed.html /sdcard/android/layout_tests/fast/forms/input-field-text-truncated.html /sdcard/android/layout_tests/fast/forms/floating-textfield-relayout.html -/sdcard/android/layout_tests/fast/forms/add-remove-form-elements-stress-test.html : has expected results /sdcard/android/layout_tests/fast/forms/button-inner-block-reuse.html /sdcard/android/layout_tests/fast/forms/input-type-text-min-width.html -/sdcard/android/layout_tests/fast/forms/onchange-enter-submit.html : has expected results +/sdcard/android/layout_tests/fast/forms/001.html /sdcard/android/layout_tests/fast/forms/slider-thumb-shared-style.html /sdcard/android/layout_tests/fast/forms/option-script.html /sdcard/android/layout_tests/fast/forms/input-paste-undo.html @@ -934,6 +1694,7 @@ /sdcard/android/layout_tests/fast/forms/button-submit.html /sdcard/android/layout_tests/fast/forms/disabled-select-change-index.html /sdcard/android/layout_tests/fast/forms/formmove3.html +/sdcard/android/layout_tests/fast/forms/form-hides-table.html /sdcard/android/layout_tests/fast/forms/listbox-width-change.html /sdcard/android/layout_tests/fast/forms/input-text-self-emptying-click.html /sdcard/android/layout_tests/fast/forms/input-appearance-bkcolor.html @@ -941,7 +1702,7 @@ /sdcard/android/layout_tests/fast/forms/search-transformed.html /sdcard/android/layout_tests/fast/forms/image-border.html /sdcard/android/layout_tests/fast/forms/encoding-test.html -/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-mouse.html : has expected results +/sdcard/android/layout_tests/fast/forms/control-clip.html /sdcard/android/layout_tests/fast/compact/001.html /sdcard/android/layout_tests/fast/compact/002.html /sdcard/android/layout_tests/fast/compact/003.html @@ -1138,7 +1899,6 @@ /sdcard/android/layout_tests/fast/css/first-letter-skip-out-of-flow.html /sdcard/android/layout_tests/fast/css/font-face-multiple-remote-sources.html /sdcard/android/layout_tests/fast/css/hover-subselector.html -/sdcard/android/layout_tests/fast/css/text-align.html : has expected results /sdcard/android/layout_tests/fast/css/margin-bottom-form-element-strict.html /sdcard/android/layout_tests/fast/css/shadow-multiple.html /sdcard/android/layout_tests/fast/css/import_with_baseurl.html @@ -1185,6 +1945,7 @@ /sdcard/android/layout_tests/fast/css/zoom-property-parsing.html /sdcard/android/layout_tests/fast/css/style-outside-head.html /sdcard/android/layout_tests/fast/css/first-letter-capitalized.html +/sdcard/android/layout_tests/fast/css/font-face-locally-installed.html /sdcard/android/layout_tests/fast/css/word-space-extra.html /sdcard/android/layout_tests/fast/css/first-letter-float.html /sdcard/android/layout_tests/fast/css/simple-selector-chain-parsing.html @@ -1192,6 +1953,7 @@ /sdcard/android/layout_tests/fast/css/continuationCrash.html /sdcard/android/layout_tests/fast/css/vertical-align-lengths.html /sdcard/android/layout_tests/fast/css/first-child-pseudo-class.html +/sdcard/android/layout_tests/fast/css/beforeSelectorOnCodeElement.html /sdcard/android/layout_tests/fast/css/getFloatValueForUnit.html /sdcard/android/layout_tests/fast/css/first-letter-detach.html /sdcard/android/layout_tests/fast/css/line-height-font-order.html @@ -1210,7 +1972,6 @@ /sdcard/android/layout_tests/fast/css/link-outside-head.html /sdcard/android/layout_tests/fast/css/font-size-negative.html /sdcard/android/layout_tests/fast/css/position-negative-top-margin.html -/sdcard/android/layout_tests/fast/css/invalid-percentage-property.html : has expected results /sdcard/android/layout_tests/fast/css/ZeroOpacityLayers.html /sdcard/android/layout_tests/fast/css/only-of-type-pseudo-class.html /sdcard/android/layout_tests/fast/css/004.html @@ -1223,6 +1984,7 @@ /sdcard/android/layout_tests/fast/css/textCapitalizeEdgeCases.html /sdcard/android/layout_tests/fast/css/001.html /sdcard/android/layout_tests/fast/css/hsl-color.html +/sdcard/android/layout_tests/fast/css/font-face-implicit-local-font.html /sdcard/android/layout_tests/fast/css/first-letter-recalculation.html /sdcard/android/layout_tests/fast/css/inline-properties-important.html /sdcard/android/layout_tests/fast/css/dynamic-sibling-selector.html @@ -1502,7 +2264,6 @@ /sdcard/android/layout_tests/fast/parser/001.html /sdcard/android/layout_tests/fast/parser/open-comment-in-textarea.html /sdcard/android/layout_tests/fast/parser/tabs-in-scripts.html -/sdcard/android/layout_tests/fast/parser/external-entities-in-xslt.xml : has expected results /sdcard/android/layout_tests/fast/parser/parseCommentsInTitles.html /sdcard/android/layout_tests/fast/parser/remove-block-in-residual-style.html /sdcard/android/layout_tests/fast/parser/open-comment-in-style.html @@ -1523,7 +2284,6 @@ /sdcard/android/layout_tests/fast/layers/scroll-rect-to-visible.html /sdcard/android/layout_tests/fast/layers/opacity-stacking.html /sdcard/android/layout_tests/fast/history/clicked-link-is-visited.html -/sdcard/android/layout_tests/fast/history/history_reload.html : has expected results /sdcard/android/layout_tests/fast/backgrounds/repeat/mask-negative-offset-repeat.html /sdcard/android/layout_tests/fast/backgrounds/repeat/negative-offset-repeat.html /sdcard/android/layout_tests/fast/backgrounds/repeat/negative-offset-repeat-transformed.html @@ -1681,20 +2441,29 @@ /sdcard/android/layout_tests/fast/repaint/layer-child-outline.html /sdcard/android/layout_tests/fast/loader/start-load-in-unload.html /sdcard/android/layout_tests/fast/loader/text-document-wrapping.html +/sdcard/android/layout_tests/fast/xsl/document-function.xml +/sdcard/android/layout_tests/fast/xsl/xslt-extra-content-at-end.xml +/sdcard/android/layout_tests/fast/xsl/xslt-enc.xml +/sdcard/android/layout_tests/fast/xsl/xslt_unicode.xml +/sdcard/android/layout_tests/fast/xsl/xslt-import-depth.xml +/sdcard/android/layout_tests/fast/xsl/xslt-enc16.xml +/sdcard/android/layout_tests/fast/xsl/xslt-enc16to16.xml +/sdcard/android/layout_tests/fast/xsl/xslt-missing-namespace-in-xslt.xml +/sdcard/android/layout_tests/fast/xsl/xslt-enc-cyr.xml +/sdcard/android/layout_tests/fast/xsl/xslt-relative-path.xml +/sdcard/android/layout_tests/fast/xsl/xslt-mismatched-tags-in-xslt.xml +/sdcard/android/layout_tests/fast/xsl/xslt-entity.xml /sdcard/android/layout_tests/fast/canvas/fillrect-gradient-zero-stops.html /sdcard/android/layout_tests/fast/canvas/canvas-transforms-during-path.html /sdcard/android/layout_tests/fast/canvas/drawImage.html /sdcard/android/layout_tests/fast/canvas/shadow-offset-7.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-non-invertible.html : has expected results /sdcard/android/layout_tests/fast/canvas/shadow-offset-4.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-infinity.html : has expected results /sdcard/android/layout_tests/fast/canvas/canvas-text-baseline.html /sdcard/android/layout_tests/fast/canvas/canvas-as-image-incremental-repaint.html /sdcard/android/layout_tests/fast/canvas/shadow-offset-1.html /sdcard/android/layout_tests/fast/canvas/canvas-size-change-after-layout.html /sdcard/android/layout_tests/fast/canvas/canvas-resize-reset.html /sdcard/android/layout_tests/fast/canvas/canvas-bg.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-skewed.html : has expected results /sdcard/android/layout_tests/fast/canvas/zero-size-fill-rect.html /sdcard/android/layout_tests/fast/canvas/shadow-offset-6.html /sdcard/android/layout_tests/fast/canvas/patternfill-repeat.html @@ -1704,17 +2473,14 @@ /sdcard/android/layout_tests/fast/canvas/gradient-add-second-start-end-stop.html /sdcard/android/layout_tests/fast/canvas/quadraticCurveTo.xml /sdcard/android/layout_tests/fast/canvas/fill-stroke-clip-reset-path.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-nan.html : has expected results /sdcard/android/layout_tests/fast/canvas/canvas-text-alignment.html /sdcard/android/layout_tests/fast/canvas/canvas-incremental-repaint.html /sdcard/android/layout_tests/fast/canvas/canvasDrawingIntoSelf.html /sdcard/android/layout_tests/fast/canvas/canvas-as-image.html /sdcard/android/layout_tests/fast/canvas/canvas-before-css.html /sdcard/android/layout_tests/fast/canvas/canvas-incremental-repaint-2.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-identity.html : has expected results /sdcard/android/layout_tests/fast/canvas/shadow-offset-5.html /sdcard/android/layout_tests/fast/canvas/fillrect_gradient.html -/sdcard/android/layout_tests/fast/canvas/canvas-transform-multiply.html : has expected results /sdcard/android/layout_tests/fast/canvas/shadow-offset-2.html /sdcard/android/layout_tests/fast/frames/contentWindow_Frame.html /sdcard/android/layout_tests/fast/frames/onlyCommentInIFrame.html @@ -1739,15 +2505,1576 @@ /sdcard/android/layout_tests/fast/frames/iframe-text-contents.html /sdcard/android/layout_tests/fast/frames/frame-scrolling-attribute.html /sdcard/android/layout_tests/fast/frames/contentWindow_iFrame.html -/sdcard/android/layout_tests/fast/frames/frame-length-fractional.html : has expected results /sdcard/android/layout_tests/fast/frames/calculate-fixed.html /sdcard/android/layout_tests/fast/frames/frame-element-name.html /sdcard/android/layout_tests/fast/frames/frame-set-whitespace-attributes.html /sdcard/android/layout_tests/fast/frames/iframe-with-frameborder.html /sdcard/android/layout_tests/fast/frames/frameElement-iframe.html -/sdcard/android/layout_tests/fast/frames/iframe-name-and-id.html : has expected results /sdcard/android/layout_tests/fast/reflections/inline-crash.html /sdcard/android/layout_tests/fast/reflections/reflection-nesting.html /sdcard/android/layout_tests/fast/reflections/reflection-overflow-hidden.html /sdcard/android/layout_tests/fast/reflections/table-cell.html +/sdcard/android/layout_tests/fast/reflections/reflection-masks.html /sdcard/android/layout_tests/fast/reflections/reflection-direction.html +/sdcard/android/layout_tests/transforms/2d/zoom-menulist.html +/sdcard/android/layout_tests/transforms/2d/transform-origin-borderbox.html +/sdcard/android/layout_tests/transforms/2d/transform-borderbox.html +/sdcard/android/layout_tests/transforms/2d/compound-transforms-vs-containers.html +/sdcard/android/layout_tests/animations/animation-drt-api-multiple-keyframes.html +/sdcard/android/layout_tests/animations/animation-drt-api.html +/sdcard/android/layout_tests/scrollbars/scrollbar-orientation.html +/sdcard/android/layout_tests/scrollbars/scrollbar-buttons.html +/sdcard/android/layout_tests/scrollbars/basic-scrollbar.html +/sdcard/android/layout_tests/scrollbars/overflow-scrollbar-combinations.html +/sdcard/android/layout_tests/scrollbars/disabled-scrollbar.html +/sdcard/android/layout_tests/scrollbars/listbox-scrollbar-combinations.html +/sdcard/android/layout_tests/css2.1/t040103-ident-03-c.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-padn-l-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t1205-c563-list-type-00-b.html +/sdcard/android/layout_tests/css2.1/t090402-c42-ibx-pad-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t120401-scope-00-b.html +/sdcard/android/layout_tests/css2.1/t010403-shand-border-00-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5518-ibrdr-t-00-a.html +/sdcard/android/layout_tests/css2.1/t1202-counter-03-b.html +/sdcard/android/layout_tests/css2.1/t1008-c44-ln-box-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-14-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-34-d.html +/sdcard/android/layout_tests/css2.1/t1204-implied-00-b.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-03-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-54-d.html +/sdcard/android/layout_tests/css2.1/t100801-c548-ln-ht-00-c-a.html +/sdcard/android/layout_tests/css2.1/t0905-c5526-fltclr-00-c-ag.html +/sdcard/android/layout_tests/css2.1/bogus-color-span.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-74-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-03-b-a.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-04-c-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-94-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-03-b-a.html +/sdcard/android/layout_tests/css2.1/t040307-syntax-01-b.html +/sdcard/android/layout_tests/css2.1/t100801-c548-ln-ht-04-d-ag.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-06-b.html +/sdcard/android/layout_tests/css2.1/t040306-c63-color-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t1202-counters-02-b.html +/sdcard/android/layout_tests/css2.1/t080301-c411-vt-mrgn-00-b.html +/sdcard/android/layout_tests/css2.1/t010403-shand-font-03-b.html +/sdcard/android/layout_tests/css2.1/t051103-c21-focus-ln-00-e-i.html +/sdcard/android/layout_tests/css2.1/t120403-display-none-00-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5512-brdr-rw-01-b-g.html +/sdcard/android/layout_tests/css2.1/t040103-ident-12-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5515-ibrdr-00-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5520-brdr-b-00-a.html +/sdcard/android/layout_tests/css2.1/t1505-c524-font-var-00-b.html +/sdcard/android/layout_tests/css2.1/t0509-c15-ids-00-a.html +/sdcard/android/layout_tests/css2.1/t060403-c21-pseu-cls-00-e-i.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-03-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-12-b.html +/sdcard/android/layout_tests/css2.1/t0402-c71-fwd-parsing-04-f.html +/sdcard/android/layout_tests/css2.1/t040303-c62-percent-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-23-d.html +/sdcard/android/layout_tests/css2.1/t140201-c535-bg-fixd-00-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-43-d.html +/sdcard/android/layout_tests/css2.1/t1204-increment-02-c-o.html +/sdcard/android/layout_tests/css2.1/t0602-c13-inh-underlin-00-e.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-63-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-83-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-16-f.html +/sdcard/android/layout_tests/css2.1/t040302-c61-phys-len-00-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5516-ibrdr-c-00-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5521-brdr-l-01-e.html +/sdcard/android/layout_tests/css2.1/t100801-c544-valgn-03-d-agi.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-06-b.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-03-f.html +/sdcard/android/layout_tests/css2.1/t050803-c14-classes-00-e.html +/sdcard/android/layout_tests/css2.1/t140201-c537-bgfxps-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t1002-c5523-width-02-b-g.html +/sdcard/android/layout_tests/css2.1/t1004-c5524-width-00-b-g.html +/sdcard/android/layout_tests/css2.1/t1202-counters-11-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-06-b-ag.html +/sdcard/android/layout_tests/css2.1/t0805-c5522-brdr-02-e.html +/sdcard/android/layout_tests/css2.1/t0511-c21-pseud-anch-00-e-i.html +/sdcard/android/layout_tests/css2.1/t0804-c5508-ipadn-b-01-f-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-04-b-ag.html +/sdcard/android/layout_tests/css2.1/t040103-ident-01-c.html +/sdcard/android/layout_tests/css2.1/t040102-keywords-00-b.html +/sdcard/android/layout_tests/css2.1/t0505-c16-descendant-02-e.html +/sdcard/android/layout_tests/css2.1/t1507-c526-font-sz-03-f-a.html +/sdcard/android/layout_tests/css2.1/t1202-counters-09-b.html +/sdcard/android/layout_tests/css2.1/t1204-root-e.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-fit-01-d-g.html +/sdcard/android/layout_tests/css2.1/t1202-counter-01-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-12-d.html +/sdcard/android/layout_tests/css2.1/t090501-c5525-flt-r-00-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-32-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-01-b.html +/sdcard/android/layout_tests/css2.1/t1004-c43-rpl-bbx-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgre-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-ipadn-l-02-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-52-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5512-brdr-rw-02-b.html +/sdcard/android/layout_tests/css2.1/t040109-c17-comments-00-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-ipadn-r-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-72-d.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-01-c-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-92-d.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-01-d-g.html +/sdcard/android/layout_tests/css2.1/t100801-c548-ln-ht-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-03-b-g.html +/sdcard/android/layout_tests/css2.1/t1204-multiple-01-c.html +/sdcard/android/layout_tests/css2.1/t0803-c5505-mrgn-03-c-ag.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-04-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-00-b.html +/sdcard/android/layout_tests/css2.1/t1602-c43-center-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t140201-c532-bgcolor-00-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-mrgn-l-03-c.html +/sdcard/android/layout_tests/css2.1/t010403-shand-font-01-b.html +/sdcard/android/layout_tests/css2.1/t1005-c5524-width-01-b-g.html +/sdcard/android/layout_tests/css2.1/t1601-c547-indent-01-d.html +/sdcard/android/layout_tests/css2.1/t040103-ident-10-c.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-01-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5505-mrgn-02-c.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-04-c.html +/sdcard/android/layout_tests/css2.1/t051103-dom-hover-01-c-io.html +/sdcard/android/layout_tests/css2.1/t1604-c542-letter-sp-00-b-a.html +/sdcard/android/layout_tests/css2.1/t040103-ident-08-c.html +/sdcard/android/layout_tests/css2.1/t120401-scope-02-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-01-d.html +/sdcard/android/layout_tests/css2.1/t0402-c71-fwd-parsing-02-f.html +/sdcard/android/layout_tests/css2.1/t1204-reset-00-c-o.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-21-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-41-d.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltwidth-01-c-g.html +/sdcard/android/layout_tests/css2.1/t1507-c526-font-sz-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-61-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-08-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-81-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-19-d.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-04-b.html +/sdcard/android/layout_tests/css2.1/t1604-c541-word-sp-01-b-a.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-39-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-08-b.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-01-f.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-59-d.html +/sdcard/android/layout_tests/css2.1/t1205-c565-list-pos-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-79-d.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-10-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-99-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t040306-syntax-01-f.html +/sdcard/android/layout_tests/css2.1/t1507-c526-font-sz-01-b-a.html +/sdcard/android/layout_tests/css2.1/t040105-atrule-04-b.html +/sdcard/android/layout_tests/css2.1/t0505-c16-descendant-00-e.html +/sdcard/android/layout_tests/css2.1/t0805-c5513-brdr-bw-02-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5519-brdr-r-01-e.html +/sdcard/android/layout_tests/css2.1/t1202-counters-07-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5510-padn-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t0805-c5517-ibrdr-s-00-a.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-10-d.html +/sdcard/android/layout_tests/css2.1/t040105-import-00-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-ipadn-l-03-b-a.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-30-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5514-brdr-lw-02-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-50-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-ipadn-r-03-b-a.html +/sdcard/android/layout_tests/css2.1/t1008-c44-ln-box-02-d-ag.html +/sdcard/android/layout_tests/css2.1/t0805-c5512-brdr-rw-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-70-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-08-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-90-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-28-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-48-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5513-brdr-bw-01-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-68-d.html +/sdcard/android/layout_tests/css2.1/t1402-c45-bg-canvas-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-88-d.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-02-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5521-ibrdr-l-00-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5522-brdr-00-b.html +/sdcard/android/layout_tests/css2.1/t1606-c562-white-sp-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-02-c.html +/sdcard/android/layout_tests/css2.1/t100801-c544-valgn-00-a-ag.html +/sdcard/android/layout_tests/css2.1/t040103-ident-06-c.html +/sdcard/android/layout_tests/css2.1/t0402-c71-fwd-parsing-00-f.html +/sdcard/android/layout_tests/css2.1/t1204-reset-02-c-o.html +/sdcard/android/layout_tests/css2.1/t0602-c13-inheritance-00-e.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-ln-02-d.html +/sdcard/android/layout_tests/css2.1/t1205-c566-list-stl-01-c-g.html +/sdcard/android/layout_tests/css2.1/t1202-counter-06-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-17-d.html +/sdcard/android/layout_tests/css2.1/t100304-c43-rpl-bbx-00-d-g.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-mrgn-l-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t1205-c566-list-stl-00-e-ag.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltwidth-03-c-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-37-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-06-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-57-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5510-padn-02-f.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-77-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-97-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t090204-display-change-01-b-ao.html +/sdcard/android/layout_tests/css2.1/t040302-c61-ex-len-00-b-a.html +/sdcard/android/layout_tests/css2.1/t040105-atrule-02-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5513-brdr-bw-00-b.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-09-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-05-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-mrgn-r-02-c.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-06-b-ag.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-padn-r-01-c-a.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-00-d.html +/sdcard/android/layout_tests/css2.1/t1202-counters-17-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-mrgn-l-01-c-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5514-brdr-lw-00-b.html +/sdcard/android/layout_tests/css2.1/t140201-c536-bgpos-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t100303-c412-blockw-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t120401-scope-04-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5506-ipadn-t-01-b-a.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-06-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-15-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-26-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5521-brdr-l-00-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5511-brdr-tw-02-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-46-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-ipadn-r-02-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-66-d.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-03-c-ag.html +/sdcard/android/layout_tests/css2.1/t100801-c544-valgn-02-d-agi.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-ipadn-l-04-f-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-86-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5512-ibrdr-rw-00-a.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-00-b.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltblck-01-d.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-06-f.html +/sdcard/android/layout_tests/css2.1/t100801-c548-ln-ht-03-d-ag.html +/sdcard/android/layout_tests/css2.1/t0805-c5515-brdr-w-00-a.html +/sdcard/android/layout_tests/css2.1/t051202-c24-first-lttr-00-b.html +/sdcard/android/layout_tests/css2.1/t0511-c21-pseud-link-02-e.html +/sdcard/android/layout_tests/css2.1/t0804-c5510-padn-01-e-a.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltinln-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t1202-counters-14-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5518-brdr-t-01-e.html +/sdcard/android/layout_tests/css2.1/t040105-atkeyw-01-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5511-brdr-tw-01-b-g.html +/sdcard/android/layout_tests/css2.1/t040103-ident-04-c.html +/sdcard/android/layout_tests/css2.1/t1205-c563-list-type-01-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-18-f.html +/sdcard/android/layout_tests/css2.1/t0509-id-sel-syntax-01-f.html +/sdcard/android/layout_tests/css2.1/t100801-c544-valgn-01-d-ag.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-ln-00-d.html +/sdcard/android/layout_tests/css2.1/t1601-c547-indent-00-b-a.html +/sdcard/android/layout_tests/css2.1/t1202-counter-04-b.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-flthw-00-c-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-15-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5522-brdr-01-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-35-d.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-00-b.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-04-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-55-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-75-d.html +/sdcard/android/layout_tests/css2.1/t1205-c564-list-img-00-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-95-d.html +/sdcard/android/layout_tests/css2.1/t120403-visibility-00-c.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-02-b-a.html +/sdcard/android/layout_tests/css2.1/t051103-c21-activ-ln-00-e-i.html +/sdcard/android/layout_tests/css2.1/t0801-c412-hz-box-00-b-a.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-02-d-g.html +/sdcard/android/layout_tests/css2.1/t1605-c545-txttrans-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t040105-atrule-00-b.html +/sdcard/android/layout_tests/css2.1/t100801-c548-leadin-00-d-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-05-b-ag.html +/sdcard/android/layout_tests/css2.1/t0804-c5508-ipadn-b-03-b-a.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-07-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-03-b.html +/sdcard/android/layout_tests/css2.1/t040103-ident-13-c.html +/sdcard/android/layout_tests/css2.1/t1204-order-01-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5510-ipadn-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-04-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-13-b.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgre-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t1504-c523-font-style-00-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-ipadn-l-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-mrgn-r-01-c-a.html +/sdcard/android/layout_tests/css2.1/t1204-increment-01-c-o.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-24-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5511-brdr-tw-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-44-d.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-64-d.html +/sdcard/android/layout_tests/css2.1/t1204-implied-02-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-84-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5521-brdr-l-02-e.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-07-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-padn-r-02-f.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-04-f.html +/sdcard/android/layout_tests/css2.1/t1002-c5523-width-01-b-g.html +/sdcard/android/layout_tests/css2.1/t0805-c5519-brdr-r-00-a.html +/sdcard/android/layout_tests/css2.1/t0511-c21-pseud-link-00-e.html +/sdcard/android/layout_tests/css2.1/t1202-counters-12-b.html +/sdcard/android/layout_tests/css2.1/t040103-ident-02-c.html +/sdcard/android/layout_tests/css2.1/t040102-keywords-01-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5503-imrgn-b-00-b-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5513-ibrdr-bw-00-a.html +/sdcard/android/layout_tests/css2.1/css1_forward_compatible_parsing.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-padn-l-03-f-g.html +/sdcard/android/layout_tests/css2.1/t1202-counter-02-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-13-d.html +/sdcard/android/layout_tests/css2.1/t0905-c5526-flthw-00-c-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-33-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-02-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-53-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5512-brdr-rw-03-b.html +/sdcard/android/layout_tests/css2.1/t040109-c17-comments-01-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-73-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-93-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5511-ibrdr-tw-00-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-02-b-ag.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-05-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-01-b.html +/sdcard/android/layout_tests/css2.1/t1005-c5524-width-00-b-g.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-wrap-01-d-g.html +/sdcard/android/layout_tests/css2.1/t010403-shand-font-02-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5515-brdr-w-02-b.html +/sdcard/android/layout_tests/css2.1/t060401-c32-cascading-00-b.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltcont-00-d-g.html +/sdcard/android/layout_tests/css2.1/t040103-ident-11-c.html +/sdcard/android/layout_tests/css2.1/t1202-counters-16-c.html +/sdcard/android/layout_tests/css2.1/t0509-id-sel-syntax-02-b.html +/sdcard/android/layout_tests/css2.1/t090501-c5525-flt-l-00-b-g.html +/sdcard/android/layout_tests/css2.1/t040103-ident-09-c.html +/sdcard/android/layout_tests/css2.1/t050201-c12-grouping-00-b.html +/sdcard/android/layout_tests/css2.1/t120401-scope-03-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-02-d.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-wrap-00-e.html +/sdcard/android/layout_tests/css2.1/t0402-c71-fwd-parsing-03-f.html +/sdcard/android/layout_tests/css2.1/t1202-counter-11-b.html +/sdcard/android/layout_tests/css2.1/t1506-c525-font-wt-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-22-d.html +/sdcard/android/layout_tests/css2.1/t1008-c44-ln-box-01-d-ag.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltwidth-00-c-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-42-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-62-d.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltmult-00-d-g.html +/sdcard/android/layout_tests/css2.1/t1401-c531-color-00-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5514-ibrdr-lw-00-a.html +/sdcard/android/layout_tests/css2.1/t1202-counter-09-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-82-d.html +/sdcard/android/layout_tests/css2.1/t1604-c541-word-sp-00-b-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5515-brdr-w-01-b-g.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-ipadn-r-04-b-ag.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-09-b.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-05-c-ag.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-02-f.html +/sdcard/android/layout_tests/css2.1/t0803-c5503-mrgn-b-00-b-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5517-brdr-s-00-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5514-brdr-lw-01-b-g.html +/sdcard/android/layout_tests/css2.1/t100801-c42-ibx-ht-00-d-a.html +/sdcard/android/layout_tests/css2.1/t040103-ident-00-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5513-brdr-bw-03-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5506-padn-t-00-b-a.html +/sdcard/android/layout_tests/css2.1/t0505-c16-descendant-01-e.html +/sdcard/android/layout_tests/css2.1/t0805-c5519-brdr-r-02-e.html +/sdcard/android/layout_tests/css2.1/t1202-counters-08-b.html +/sdcard/android/layout_tests/css2.1/t051103-c21-hover-ln-00-e-i.html +/sdcard/android/layout_tests/css2.1/t120403-content-none-00-c.html +/sdcard/android/layout_tests/css2.1/t1202-counter-00-b.html +/sdcard/android/layout_tests/css2.1/t040105-import-01-b.html +/sdcard/android/layout_tests/css2.1/t040103-case-01-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-11-d.html +/sdcard/android/layout_tests/css2.1/t09-c5526c-display-00-e.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltblck-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-31-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5514-brdr-lw-03-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-51-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-71-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-09-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-91-d.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltwrap-00-b.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-fit-00-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-29-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5505-mrgn-01-e-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-mrgn-r-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t1004-c43-rpl-ibx-00-d-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-49-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-69-d.html +/sdcard/android/layout_tests/css2.1/t1204-multiple-00-c.html +/sdcard/android/layout_tests/css2.1/t140201-c533-bgimage-01-b-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-89-d.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-03-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-mrgn-l-02-c.html +/sdcard/android/layout_tests/css2.1/t0805-c5518-brdr-t-00-a.html +/sdcard/android/layout_tests/css2.1/t010403-shand-font-00-b.html +/sdcard/android/layout_tests/css2.1/t0510-c25-pseudo-elmnt-00-c.html +/sdcard/android/layout_tests/css2.1/t0803-c5505-imrgn-00-a-ag.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-imrgn-r-05-b-ag.html +/sdcard/android/layout_tests/css2.1/t060402-c31-important-00-b.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-00-d.html +/sdcard/android/layout_tests/css2.1/t0905-c414-flt-03-c.html +/sdcard/android/layout_tests/css2.1/t040103-ident-07-c.html +/sdcard/android/layout_tests/css2.1/t1204-order-00-c.html +/sdcard/android/layout_tests/css2.1/t120401-scope-01-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-00-d.html +/sdcard/android/layout_tests/css2.1/t0402-c71-fwd-parsing-01-f.html +/sdcard/android/layout_tests/css2.1/t1604-c542-letter-sp-01-b-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5520-brdr-b-01-e.html +/sdcard/android/layout_tests/css2.1/t140201-c536-bgpos-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-20-d.html +/sdcard/android/layout_tests/css2.1/t0509-c15-ids-01-e.html +/sdcard/android/layout_tests/css2.1/t1204-reset-01-c-o.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-40-d.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-ln-03-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-60-d.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltwidth-02-c-g.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltclr-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t1202-counter-07-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-80-d.html +/sdcard/android/layout_tests/css2.1/t1204-implied-01-c.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-18-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-ipadn-r-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-03-b.html +/sdcard/android/layout_tests/css2.1/t140201-c534-bgreps-02-c-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-38-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-07-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-58-d.html +/sdcard/android/layout_tests/css2.1/t0803-c5505-mrgn-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t100801-c544-valgn-04-d-agi.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-78-d.html +/sdcard/android/layout_tests/css2.1/t100801-c548-ln-ht-02-b-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-98-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5508-ipadn-b-00-b-a.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-padn-l-01-b-a.html +/sdcard/android/layout_tests/css2.1/t040105-atrule-03-b.html +/sdcard/android/layout_tests/css2.1/t1507-c526-font-sz-02-b-a.html +/sdcard/android/layout_tests/css2.1/t0805-c5522-ibrdr-00-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5502-mrgn-r-03-c.html +/sdcard/android/layout_tests/css2.1/t1202-counters-06-b.html +/sdcard/android/layout_tests/css2.1/t0905-c5525-fltmrgn-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t051103-dom-hover-02-c-io.html +/sdcard/android/layout_tests/css2.1/t0804-c5506-ipadn-t-00-b-a.html +/sdcard/android/layout_tests/css2.1/t040304-c64-uri-00-a-g.html +/sdcard/android/layout_tests/css2.1/t0803-c5501-mrgn-t-00-b-a.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-07-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-27-d.html +/sdcard/android/layout_tests/css2.1/t0805-c5511-brdr-tw-03-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-47-d.html +/sdcard/android/layout_tests/css2.1/t060403-c21-pseu-id-00-e-i.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-67-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-87-d.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-01-b.html +/sdcard/android/layout_tests/css2.1/t0603-c11-import-00-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-04-b-ag.html +/sdcard/android/layout_tests/css2.1/t1503-c522-font-family-00-b.html +/sdcard/android/layout_tests/css2.1/t090501-c414-flt-ln-01-d-g.html +/sdcard/android/layout_tests/css2.1/t0511-c21-pseud-link-03-e.html +/sdcard/android/layout_tests/css2.1/t1202-counters-15-b.html +/sdcard/android/layout_tests/css2.1/t040105-atkeyw-02-b.html +/sdcard/android/layout_tests/css2.1/t0805-c5519-ibrdr-r-00-a.html +/sdcard/android/layout_tests/css2.1/t040103-ident-05-c.html +/sdcard/android/layout_tests/css2.1/t0602-inherit-bdr-pad-b-00.html +/sdcard/android/layout_tests/css2.1/t040302-c61-rel-len-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-padn-r-00-c-ag.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-ipadn-l-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t0805-c5520-ibrdr-b-00-a.html +/sdcard/android/layout_tests/css2.1/t1202-counter-05-b.html +/sdcard/android/layout_tests/css2.1/t1008-c44-ln-box-03-d-ag.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-16-d.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-01-b.html +/sdcard/android/layout_tests/css2.1/t100304-c43-rpl-bbx-01-d-g.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-36-d.html +/sdcard/android/layout_tests/css2.1/t1001-abs-pos-cb-05-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-56-d.html +/sdcard/android/layout_tests/css2.1/t0804-c5509-padn-l-02-f.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-76-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-96-d.html +/sdcard/android/layout_tests/css2.1/t051202-c26-psudo-nest-00-c.html +/sdcard/android/layout_tests/css2.1/t040105-atrule-01-b.html +/sdcard/android/layout_tests/css2.1/t0804-c5508-ipadn-b-02-b-a.html +/sdcard/android/layout_tests/css2.1/t0803-c5501-imrgn-t-00-b-ag.html +/sdcard/android/layout_tests/css2.1/t140201-c532-bgcolor-01-b.html +/sdcard/android/layout_tests/css2.1/t1508-c527-font-08-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-04-b.html +/sdcard/android/layout_tests/css2.1/t040103-case-00-b.html +/sdcard/android/layout_tests/css2.1/t1504-c543-txt-decor-00-d-g.html +/sdcard/android/layout_tests/css2.1/t0805-c5516-brdr-c-00-a.html +/sdcard/android/layout_tests/css2.1/t0804-c5506-ipadn-t-02-b-a.html +/sdcard/android/layout_tests/css2.1/t1204-increment-00-c-o.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-05-d.html +/sdcard/android/layout_tests/css2.1/t1202-counter-14-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-25-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-45-d.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-65-d.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-02-d.html +/sdcard/android/layout_tests/css2.1/t1602-c546-txt-align-00-b.html +/sdcard/android/layout_tests/css2.1/t170602-bdr-conflct-w-85-d.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-05-c.html +/sdcard/android/layout_tests/css2.1/t040103-escapes-08-b.html +/sdcard/android/layout_tests/css2.1/t0803-c5504-imrgn-l-01-b-ag.html +/sdcard/android/layout_tests/css2.1/t1002-c5523-width-00-b-g.html +/sdcard/android/layout_tests/css2.1/t0804-c5507-padn-r-03-f.html +/sdcard/android/layout_tests/css2.1/t0402-syntax-05-f.html +/sdcard/android/layout_tests/css2.1/t1205-c561-list-displ-00-b.html +/sdcard/android/layout_tests/css2.1/t0511-c21-pseud-link-01-e.html +/sdcard/android/layout_tests/css2.1/t051201-c23-first-line-00-b.html +/sdcard/android/layout_tests/css2.1/t1202-counters-13-b.html +/sdcard/android/layout_tests/css2.1/t140201-c533-bgimage-00-a.html +/sdcard/android/layout_tests/css2.1/t040105-atkeyw-00-b.html +/sdcard/android/layout_tests/css1/color_and_background/background_color.html +/sdcard/android/layout_tests/css1/color_and_background/color.html +/sdcard/android/layout_tests/css1/color_and_background/background.html +/sdcard/android/layout_tests/css1/color_and_background/background_repeat.html +/sdcard/android/layout_tests/css1/color_and_background/background_image.html +/sdcard/android/layout_tests/css1/color_and_background/background_position.html +/sdcard/android/layout_tests/css1/color_and_background/background_attachment.html +/sdcard/android/layout_tests/css1/pseudo/firstline.html +/sdcard/android/layout_tests/css1/pseudo/pseudo_elements_in_selectors.html +/sdcard/android/layout_tests/css1/pseudo/multiple_pseudo_elements.html +/sdcard/android/layout_tests/css1/pseudo/firstletter.html +/sdcard/android/layout_tests/css1/pseudo/anchor.html +/sdcard/android/layout_tests/css1/text_properties/text_align.html +/sdcard/android/layout_tests/css1/text_properties/line_height.html +/sdcard/android/layout_tests/css1/text_properties/text_transform.html +/sdcard/android/layout_tests/css1/text_properties/word_spacing.html +/sdcard/android/layout_tests/css1/text_properties/letter_spacing.html +/sdcard/android/layout_tests/css1/text_properties/vertical_align.html +/sdcard/android/layout_tests/css1/text_properties/text_indent.html +/sdcard/android/layout_tests/css1/text_properties/text_decoration.html +/sdcard/android/layout_tests/css1/basic/containment.html +/sdcard/android/layout_tests/css1/basic/id_as_selector.html +/sdcard/android/layout_tests/css1/basic/comments.html +/sdcard/android/layout_tests/css1/basic/class_as_selector.html +/sdcard/android/layout_tests/css1/basic/contextual_selectors.html +/sdcard/android/layout_tests/css1/basic/inheritance.html +/sdcard/android/layout_tests/css1/basic/grouping.html +/sdcard/android/layout_tests/css1/font_properties/font_weight.html +/sdcard/android/layout_tests/css1/font_properties/font_size.html +/sdcard/android/layout_tests/css1/font_properties/font.html +/sdcard/android/layout_tests/css1/font_properties/font_style.html +/sdcard/android/layout_tests/css1/font_properties/font_family.html +/sdcard/android/layout_tests/css1/font_properties/font_variant.html +/sdcard/android/layout_tests/css1/units/percentage_units.html +/sdcard/android/layout_tests/css1/units/color_units.html +/sdcard/android/layout_tests/css1/units/length_units.html +/sdcard/android/layout_tests/css1/units/urls.html +/sdcard/android/layout_tests/css1/cascade/important.html +/sdcard/android/layout_tests/css1/cascade/cascade_order.html +/sdcard/android/layout_tests/css1/box_properties/border_width.html +/sdcard/android/layout_tests/css1/box_properties/margin.html +/sdcard/android/layout_tests/css1/box_properties/width.html +/sdcard/android/layout_tests/css1/box_properties/padding_left.html +/sdcard/android/layout_tests/css1/box_properties/margin_left_inline.html +/sdcard/android/layout_tests/css1/box_properties/clear.html +/sdcard/android/layout_tests/css1/box_properties/border_left_width.html +/sdcard/android/layout_tests/css1/box_properties/margin_left.html +/sdcard/android/layout_tests/css1/box_properties/padding_top.html +/sdcard/android/layout_tests/css1/box_properties/padding_bottom.html +/sdcard/android/layout_tests/css1/box_properties/border_style_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_top_width_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_top_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_style.html +/sdcard/android/layout_tests/css1/box_properties/border_top.html +/sdcard/android/layout_tests/css1/box_properties/margin_bottom_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_bottom_width.html +/sdcard/android/layout_tests/css1/box_properties/margin_bottom.html +/sdcard/android/layout_tests/css1/box_properties/float_elements_in_series.html +/sdcard/android/layout_tests/css1/box_properties/float_on_text_elements.html +/sdcard/android/layout_tests/css1/box_properties/padding.html +/sdcard/android/layout_tests/css1/box_properties/border_right_width_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_right_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_right_width.html +/sdcard/android/layout_tests/css1/box_properties/margin_right.html +/sdcard/android/layout_tests/css1/box_properties/border_width_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_inline.html +/sdcard/android/layout_tests/css1/box_properties/clear_float.html +/sdcard/android/layout_tests/css1/box_properties/border.html +/sdcard/android/layout_tests/css1/box_properties/padding_left_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_left_width_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_left_inline.html +/sdcard/android/layout_tests/css1/box_properties/padding_top_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_left.html +/sdcard/android/layout_tests/css1/box_properties/padding_bottom_inline.html +/sdcard/android/layout_tests/css1/box_properties/margin_top_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_top_width.html +/sdcard/android/layout_tests/css1/box_properties/border_bottom_width_inline.html +/sdcard/android/layout_tests/css1/box_properties/acid_test.html +/sdcard/android/layout_tests/css1/box_properties/border_bottom_inline.html +/sdcard/android/layout_tests/css1/box_properties/margin_top.html +/sdcard/android/layout_tests/css1/box_properties/border_bottom.html +/sdcard/android/layout_tests/css1/box_properties/padding_right_inline.html +/sdcard/android/layout_tests/css1/box_properties/float_margin.html +/sdcard/android/layout_tests/css1/box_properties/padding_right.html +/sdcard/android/layout_tests/css1/box_properties/padding_inline.html +/sdcard/android/layout_tests/css1/box_properties/float.html +/sdcard/android/layout_tests/css1/box_properties/height.html +/sdcard/android/layout_tests/css1/box_properties/margin_right_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_color_inline.html +/sdcard/android/layout_tests/css1/box_properties/border_right.html +/sdcard/android/layout_tests/css1/box_properties/border_color.html +/sdcard/android/layout_tests/css1/box_properties/margin_inline.html +/sdcard/android/layout_tests/css1/conformance/forward_compatible_parsing.html +/sdcard/android/layout_tests/css1/formatting_model/floating_elements.html +/sdcard/android/layout_tests/css1/formatting_model/horizontal_formatting.html +/sdcard/android/layout_tests/css1/formatting_model/vertical_formatting.html +/sdcard/android/layout_tests/css1/formatting_model/height_of_lines.html +/sdcard/android/layout_tests/css1/formatting_model/inline_elements.html +/sdcard/android/layout_tests/css1/formatting_model/canvas.html +/sdcard/android/layout_tests/css1/formatting_model/replaced_elements.html +/sdcard/android/layout_tests/css1/classification/list_style_type.html +/sdcard/android/layout_tests/css1/classification/list_style_image.html +/sdcard/android/layout_tests/css1/classification/list_style_position.html +/sdcard/android/layout_tests/css1/classification/display.html +/sdcard/android/layout_tests/css1/classification/list_style.html +/sdcard/android/layout_tests/css1/classification/white_space.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/insertTbodyRebuild1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/appendCellsRebuild1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/appendColGroup1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/appendCol1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/insertTbodyExpand1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/dom/appendCells1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug56024.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-14.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug11945.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-18.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug72393.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug23847.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug7121-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug27993-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug7243.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-3.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1647.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-7.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug101759.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug2479-5.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug14007-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-11.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1010.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-15.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug14159-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug25707.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug47163.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug91057.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug131020-3.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug19526.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug14489.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug73629.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1725.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-4.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug106336.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-8.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug22122.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug10216.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug14007-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug106966.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug32205-4.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug42043.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug178855.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-12.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug92868_1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug21518.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug45621.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-16.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug65372.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug59252.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug29058-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug17826.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug67915-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug46268-4.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1128.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1164.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/97619.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-5.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug10140.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-9.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug32205-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug61042-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug104898.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug8499.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug9879-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug128876.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-13.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug58402-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug24880-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug85016.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-17.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug80762-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug18770.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug33784.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3105.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1055-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug89315.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug92647-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug1262.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug7113.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3517.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug220653.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug4294.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-6.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug6933.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug51000.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug11331.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug61042-2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/bugs/bug3166-10.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/other/empty_cells.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/other/test4.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/col_span2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/columns.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/captions1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/cols1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/backgrounds.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/captions2.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/captions3.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/conflicts.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/core/standards1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/collapsing_borders/bug41262-5.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/collapsing_borders/bug41262-6.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/collapsing_borders/bug41262-1.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_below.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_position-table-cell.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_dirty_reflow_row.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_hidden_tr.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_position-table-column.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/tables_caption_align_right.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-column-group.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_position-table-row-group.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_hidden.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_row_sibling.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_lhs.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_tbody.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_rules_rows.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_rules_all.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_above.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_table.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_void.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_hsides.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_td_valign_top.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_right.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_hidden_tbody.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_layers-show.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_caption_align_right.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-cell.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_below.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_table_caption.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_hidden_table.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_rules_cols.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_cell.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_position-table-row.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_td_align_right.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-row.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_tbody_sibling.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_rules_cols.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_border.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_colgroup_width_px.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_td_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-quirks.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_box.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_rules_rows.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_hsides.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_left.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_caption_align_left.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_hidden_table.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-row-group.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_bottom.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_rhs.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_frame_vsides.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_cell_sibling.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_layers-hide.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_td_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/tables_cellspacing_pct.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_position-table-column-group.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_lhs.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_td_dynamic_deactivate.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_row.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_rhs.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_above.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/x_table_frame_vsides.xml +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_dirty_reflow.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_dirty_reflow_tbody.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/backgr_border-table-column.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_caption_top.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/table_overflow_dirty_reflow_table.html +/sdcard/android/layout_tests/tables/mozilla_expected_failures/marvin/tables_caption_align_left.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCellsRebuild1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteRowsShrink1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCellsRebuild1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCellsRebuild2.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCol1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCol2.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertColGroups1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCol3.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertColGroups2.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCellsShrink1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteTbodyExpand1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteCellsShrink2.html +/sdcard/android/layout_tests/tables/mozilla/dom/tableDom.html +/sdcard/android/layout_tests/tables/mozilla/dom/appendCol2.html +/sdcard/android/layout_tests/tables/mozilla/dom/appendTbodyExpand1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteTbodyRebuild1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteColGroup1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteColGroup2.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertRowsExpand1.html +/sdcard/android/layout_tests/tables/mozilla/dom/appendRowsExpand1.html +/sdcard/android/layout_tests/tables/mozilla/dom/deleteRowsRebuild1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCols1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCols2.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertRowsRebuild1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCellsExpand1.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCols3.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCellsExpand2.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCols4.html +/sdcard/android/layout_tests/tables/mozilla/dom/insertCols5.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30418.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug14159-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46623-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug24661.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30692.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug53690-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug88035-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug51727.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug81934.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug68912.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug60992.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug92647-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug120107.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1271.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug28928.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug103533.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4093.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2267.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug92868.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4429.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5538.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug106816.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10009.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug149275-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug32205-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13118.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1802s.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10296-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug647.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug48028-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug72359.xml +/sdcard/android/layout_tests/tables/mozilla/bugs/bug25367.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1430.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1224.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug67915-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug45486.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug113424.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug108340.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3454.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2479-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug139524-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4849-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug11321.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2886.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug219693-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug42443.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug54450.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug269566.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12709.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug80762-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug21918.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1302.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug25663.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug55527.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7112-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1055-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug69382-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17587.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug102145-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2516.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4803.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug19599.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1188.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3718.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug110566.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug106158-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5188.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug215629.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug154780.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug137388-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10039.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5798.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22246-3a.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug25074.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug27038-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/45621.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug25086.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug43854-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug34538.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug131020.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7121-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4501.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug53891.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8032-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46268-5.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug63785.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug113235-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug69187.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9024.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug120364.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug109043.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug220536.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-5.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2973.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug222467.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug6674.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug99948.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug277062.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug127267.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30332-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10036.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug16012.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2997.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17130-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug650.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug14323.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10565.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug52505.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29314.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13169.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30559.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29326.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug55545.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46268-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug18359.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3037-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug82946-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug55694.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug6304.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3263.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug101674.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug123862.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug221784-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug275625.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug106795.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22513.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug57300.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug51037.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug119786.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12908-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug15247.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46623-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug34176.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug53690-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug24880.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug41890.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug88035-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug50695-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1163-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8411.xml +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1802.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3260.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug97138.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9123-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3309-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3191.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1296.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug222336.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2773.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8381.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug194024.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2947.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5838.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug60013.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug138725.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug11026.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug175455-4.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug58402-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug43039.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug48028-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug24627.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug35662.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug21299.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug26178.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug18664.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug23299.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug88524.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug48827.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1318.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4427.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug6184.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug11384s.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5835.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4576.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2479-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug139524-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug133948.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9879-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug11944.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13196.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug20579.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29058-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug96334.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7112-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug60749.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug59354.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug69382-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug27993-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug57378.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug102145-4.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug98196.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9072.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2585.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug145572.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug137388-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5799.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug157890.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug196870.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22246-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug73321.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug18440.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug965.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug131020-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug43854-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug96343.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46368-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug102145-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8032-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1067-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2065.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug97383.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug113235-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3681-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9271-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1809.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2962.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-6.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2469.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8950.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug133756-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2886-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug159108.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4849.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug25004.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12910.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug43204.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug20804.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug23072.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug19061-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10269-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13526.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug52506.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug27038-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46480-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug42187.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2050.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3103.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug39209.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46268.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46268-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug38916.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug82946-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3037-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4523.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug67864.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8361.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2981-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2684.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4385.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug126742.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12910-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug8858.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug709.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug16252.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug33137.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug45350.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12908-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug92143.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug14159-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug50695-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29429.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1261.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4520.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46944.html +/sdcard/android/layout_tests/tables/mozilla/bugs/adforce_imgis_com.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug18955.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug227123.xml +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3309-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9123-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7342.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug83786.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4382.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug24200.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug641-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug625.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug32205-5.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug56201.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug32841.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug44505.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug45055-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug15544.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug40828.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1800.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug6404.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2509.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2479-4.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4739.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug139524-4.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13105.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug149275-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug32205-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12008.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10296-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug727.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug278385.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug13484.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug15933.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug60807.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug93363.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug14929.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug86708.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug57828.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug11384q.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2954.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2479-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug139524-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug219693-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug137388-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30273.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22246-3.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22246-2a.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12384.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug44523.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug60804.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug86220.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug32447.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17138.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug56405.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug26553.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1220.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29058-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug33855.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug29157.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2123.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug19356.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug28933.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46368-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug18558.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug102145-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1067-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1474.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3681-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4284.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug4527.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug9271-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2296.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug106158-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2757.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug128229.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug133756-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug5797.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug278266.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug23235.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug19061-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10269-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug963.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug27038-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug12268.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug45055.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7471.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug75250.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46480-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30985.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46924.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug56563.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug23994.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug113235-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug99923.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug55789.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2981-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-4.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug7714.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug222846.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug68998.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug30332-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17130-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug51140.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug23151.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug10633.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug24503.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug28341.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug47432.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug101201.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17168.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug46268-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug78162.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug17548.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug100334.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug57828-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1818-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug2763.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug1828.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug221784-1.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug3977.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug131020_iframe.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug641-2.html +/sdcard/android/layout_tests/tables/mozilla/bugs/bug22019.html +/sdcard/android/layout_tests/tables/mozilla/other/move_row.html +/sdcard/android/layout_tests/tables/mozilla/other/nestedTables.html +/sdcard/android/layout_tests/tables/mozilla/other/wa_table_tr_align.html +/sdcard/android/layout_tests/tables/mozilla/other/ms.html +/sdcard/android/layout_tests/tables/mozilla/other/cell_widths.html +/sdcard/android/layout_tests/tables/mozilla/other/test3.html +/sdcard/android/layout_tests/tables/mozilla/other/cellspacing.html +/sdcard/android/layout_tests/tables/mozilla/other/nested2.html +/sdcard/android/layout_tests/tables/mozilla/other/test6.html +/sdcard/android/layout_tests/tables/mozilla/other/padding.html +/sdcard/android/layout_tests/tables/mozilla/other/body_col.html +/sdcard/android/layout_tests/tables/mozilla/other/wa_table_thtd_rowspan.html +/sdcard/android/layout_tests/tables/mozilla/other/slashlogo.html +/sdcard/android/layout_tests/tables/mozilla/images/adforce_imgis_com.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_auto.html +/sdcard/android/layout_tests/tables/mozilla/core/captions.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_fixPer.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_autoFix.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_autoPer.html +/sdcard/android/layout_tests/tables/mozilla/core/row_span.html +/sdcard/android/layout_tests/tables/mozilla/core/cell_heights.html +/sdcard/android/layout_tests/tables/mozilla/core/misc.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_autoFixPer.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_fix.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_per.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_auto.html +/sdcard/android/layout_tests/tables/mozilla/core/col_span.html +/sdcard/android/layout_tests/tables/mozilla/core/margins.html +/sdcard/android/layout_tests/tables/mozilla/core/borders.html +/sdcard/android/layout_tests/tables/mozilla/core/table_frame.html +/sdcard/android/layout_tests/tables/mozilla/core/table_rules.html +/sdcard/android/layout_tests/tables/mozilla/core/table_heights.html +/sdcard/android/layout_tests/tables/mozilla/core/nested1.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_autoFix.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_auto_fixPer.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_autoPer.html +/sdcard/android/layout_tests/tables/mozilla/core/bloomberg.html +/sdcard/android/layout_tests/tables/mozilla/core/one_row.html +/sdcard/android/layout_tests/tables/mozilla/core/table_widths.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_autoFixPer.html +/sdcard/android/layout_tests/tables/mozilla/core/box_sizing.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_fix.html +/sdcard/android/layout_tests/tables/mozilla/core/col_widths_fix_per.html +/sdcard/android/layout_tests/tables/mozilla/collapsing_borders/bug41262-3.html +/sdcard/android/layout_tests/tables/mozilla/collapsing_borders/bug41262-4.html +/sdcard/android/layout_tests/tables/mozilla/collapsing_borders/bug127040.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_green.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_olive.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_teal_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_width_pct.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_olive_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_border_0.html +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table-row-group.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_maroon_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_width_rel.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_lime.html +/sdcard/android/layout_tests/tables/mozilla/marvin/td_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_navy.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table-column.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_nowrap.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_style.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_bgcolor_rgb.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_green_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_caption_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_align_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_span.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_bgcolor_rgb.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/th_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_silver_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_rules_groups.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_gray.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_bgcolor_rgb.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_cellspacing.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_width_px.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_navy_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/td_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_yellow.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_width.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_green.html +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table-cell.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_silver.html +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_border_px.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_colspan.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_cellspacing_pct.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_nowrap.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_blue.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_navy.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_lime_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_aqua_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_rowspan.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_bgcolor_name.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_rowspan.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_navy_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_red.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_gray.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_align_justify.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_fuchsia.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_white_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_maroon_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_rules_none.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_caption_align_bot.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_cellpadding_pct.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_yellow_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_colspan.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_align_justify.html +/sdcard/android/layout_tests/tables/mozilla/marvin/th_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_colspan.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_nowrap.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_lime_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_aqua_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_border_none.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_border_1.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_row_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/td_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_maroon.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_frame_border.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_caption_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_blue.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_width_px.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_silver_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_width_rel.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_index.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_row_th_nowrap.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_black_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_width.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_cellpadding_pct.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_gray_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_olive_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_id.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_align_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/body_col.html +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table-row.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_fuchsia.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_cellpadding.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_caption_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_frame_void.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_rowspan.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_purple.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_rules_groups.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_gray_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_class.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_width_pct.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/body_tfoot.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_span.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_width_pct.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_yellow_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_height.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_white.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/table_frame_box.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_yellow.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_width_px.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/table_row_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_border_2.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_aqua.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_fuchsia_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/td_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/th_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/col_span.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/table_row_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_purple_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_red_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_width_pct.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_green_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_border.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_white_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_valign_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_fuchsia_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_default.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_caption_align_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_silver.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_red.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_black.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_overflow_td_dynamic_deactivate.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_cellpadding.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_align_right.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_rowspan.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_cellspacing.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_rules_none.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_white.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_align_justify.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_rules_all.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_blue_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_black_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_align_left.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_align_right.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_caption_align_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_id.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_col_span.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_bgcolor_name.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_height.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_teal.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_height.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_olive.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_height.html +/sdcard/android/layout_tests/tables/mozilla/marvin/body_thead.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_maroon.html +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_simple-table-column-group.html +/sdcard/android/layout_tests/tables/mozilla/marvin/table_overflow_hidden_td.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_black.html +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_layers-opacity.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_bgcolor_name.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_red_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_blue_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_valign_top.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_lime.html +/sdcard/android/layout_tests/tables/mozilla/marvin/th_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_width_percent.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_width.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_teal_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_valign_middle.html +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_width_px.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_valign_baseline.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_align_char.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/backgr_position-table.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_align_char.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_th_width.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tfoot_align_center.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tbody_align_justify.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_bgcolor_rgb.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_purple_rgb.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/colgroup_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_td_colspan.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_caption_align_top.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_table_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_valign_middle.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_colgroup_align_center.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/thead_valign_baseline.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_thead_style.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tbody_class.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_border_3.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_align_left.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tr_valign_bottom.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/body_tbody.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_td_bgcolor_name.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_bgcolor_teal.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_tfoot_align_justify.xml +/sdcard/android/layout_tests/tables/mozilla/marvin/tables_bgcolor_purple.html +/sdcard/android/layout_tests/tables/mozilla/marvin/tr_valign_bottom.html +/sdcard/android/layout_tests/tables/mozilla/marvin/x_th_id.xml +/sdcard/android/layout_tests/css3/css3-modsel-33.html +/sdcard/android/layout_tests/css3/css3-modsel-35.html +/sdcard/android/layout_tests/css3/css3-modsel-36.html +/sdcard/android/layout_tests/css3/css3-modsel-37.html +/sdcard/android/layout_tests/transitions/transition-drt-api.html diff --git a/tests/DumpRenderTree/assets/results/layout_tests_passed.txt b/tests/DumpRenderTree/assets/results/layout_tests_passed.txt index fbceabd23048..37a4bbe9ca17 100644 --- a/tests/DumpRenderTree/assets/results/layout_tests_passed.txt +++ b/tests/DumpRenderTree/assets/results/layout_tests_passed.txt @@ -1,3 +1,1051 @@ +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrlastchild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementnormalize.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_noderemovechildnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetattributenodenull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapreturnfirstitem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrparentnodenull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapreturnattrnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementsetattributenodenull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementnormalize2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_noderemovechild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforeinvalidnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodechildnodesappendchild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrfirstchild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue05.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatagetlength.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrinsertdataoffsetnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementinvalidcharacterexception.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textsplittexttwo.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrsetvalue1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeclonegetparentnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreatetextnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentinvalidcharacterexceptioncreateelement.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistreturnlastitem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrchildnodes1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrdeletedataoffsetnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodetextnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementretrieveallattributes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentinvalidcharacterexceptioncreateelement1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_domimplementationfeaturenoversion.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrprevioussiblingnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetelementsbytagnamespecialvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchildnodeancestor.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetelementsbytagnamenomatch.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchildgetnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatadeletedataend.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatareplacedataexceedslengthofarg.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_notationssetnameditem1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildinvalidnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforenodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore7.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetprevioussiblingnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodehaschildnodesfalse.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapreturnlastitem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetfirstchildnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_notationsremovenameditem1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforedocfragment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataappenddata.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeattributenodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrspecifiedvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrgetvalue1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildnewchilddiffdocument.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_domimplementationfeaturenull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrreplacedataoffsetnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforenewchildexists.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue06.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistindexnotzero.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textwithnomarkup.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_entitiessetnameditem1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrsetvalue2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetelementsbytagnametotallength.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentinvalidcharacterexceptioncreateattribute1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistindexgetlengthofemptylist.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementchangeattributevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeattributenodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetnextsiblingnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapinuseattributeerr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrchildnodes2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrcreatetextnode2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodechildnodesempty.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataappenddatagetdata.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatareplacedataend.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetlastchildnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrcreatetextnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeclonefalsenocopytext.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrreplacedataoffsetgreater.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodetextnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeparentnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapnotfounderr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementretrieveattrvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildoldchildnonexistent.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbefore.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodehaschildnodes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodecommentnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/documentinvalidcharacterexceptioncreateentref1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrgetvalue2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementretrievetagname.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrspecifiedvaluechanged.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforenewchilddiffdocument.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrreplacedatacountnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatadeletedatabegining.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreateelement.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrinsertdataoffsetgreater.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textparseintolistofelements.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue07.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetownerdocumentnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatareplacedatamiddle.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchildinvalidnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementremoveattributenode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrsubstringoffsetgreater.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatadeletedatamiddle.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatasubstringexceedsvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetrootnode.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatainsertdataend.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeclonenodetrue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapchildnoderange.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapnumberofnodes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue01.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetimplementation.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchildchildexists.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild3.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetelementsbytagnamelength.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeelementnodeattributes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrsubstringnegativeoffset.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_noderemovechildgetnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrsubstringcountnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementnotfounderr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentfragmentnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrdeletedataoffsetgreater.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatasubstringvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetownerdocument.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementassociatedattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementinvalidcharacterexception1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textindexsizeerroffsetoutofbounds.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatadeletedatagetlengthanddata.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildnodeancestor.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue08.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textsplittextthree.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreateelementcasesensitive.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore3.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentnodeattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdataindexsizeerrdeletedatacountnegative.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeelementnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildnewchildexists.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_domimplementationfeaturexml.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementreplaceexistingattributegevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeelementnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementcreatenewattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodecommentnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrnextsiblingnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrremovechild1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapwrongdocumenterr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapreturnnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue02.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild4.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementinuseattributeerr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetlastchild.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetattributenode.html +/sdcard/android/layout_tests/dom/html/level1/core/documentgetdoctypenodtd.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapsetnameditemreturnvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatareplacedataexceedslengthofdata.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodetextnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeattributenodeattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetdoctype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapsetnameditemthatexists.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreatedocumentfragment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_commentgetcomment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attreffectivevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textsplittextfour.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforenodeancestor.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore4.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textindexsizeerrnegativeoffset.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodecommentnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodetextnodeattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatadeletedataexceedslength.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_textsplittextone.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentfragmentnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatagetdata.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatainsertdatabeginning.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetnextsibling.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistreturnfirstitem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrremovechild2.html +/sdcard/android/layout_tests/dom/html/level1/core/documentinvalidcharacterexceptioncreatepi.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue03.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild5.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrhaschildnodes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrreplacechild1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrnormalize.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodedocumentfragmentnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementreplaceexistingattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchildnewchilddiffdocument.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelisttraverselist.html +/sdcard/android/layout_tests/dom/html/level1/core/documentinvalidcharacterexceptioncreateentref.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatasetnodevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_entitiesremovenameditem1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore5.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetelementsbytagname.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrcreatedocumentfragment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrclonenode1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapremovenameditem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementaddnewattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistindexequalzero.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodechildnodes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetelementempty.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatainsertdatamiddle.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreatecomment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetprevioussibling.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforerefchildnonexistent.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementremoveattributeaftercreate.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_noderemovechildoldchildnonexistent.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementwrongdocumenterr.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_characterdatareplacedatabegining.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeattributenodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodecommentnodeattributes.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodevalue04.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentinvalidcharacterexceptioncreateattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementremoveattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrappendchild6.html +/sdcard/android/layout_tests/dom/html/level1/core/documentinvalidcharacterexceptioncreatepi1.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeelementnodetype.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrname.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapgetnameditem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgettagname.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapsetnameditem.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeinsertbeforerefchildnull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrreplacechild2.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeparentnodenull.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodecloneattributescopied.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeclonenodefalse.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodereplacechildnodename.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementgetelementsbytagnameaccessnodelist.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeclonetruecopytext.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_attrinsertbefore6.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodelistindexgetlength.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_elementreplaceattributewithself.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentcreateattribute.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodeappendchilddocfragment.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_namednodemapsetnameditemwithnewvalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_documentgetelementsbytagnamevalue.html +/sdcard/android/layout_tests/dom/html/level1/core/hc_nodegetfirstchild.html +/sdcard/android/layout_tests/dom/html/level2/events/EventTargetCast01.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent12.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent04.html +/sdcard/android/layout_tests/dom/html/level2/events/createEvent02.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent08.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent04.html +/sdcard/android/layout_tests/dom/html/level2/events/DocumentEventCast01.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent01.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent13.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent05.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent01.html +/sdcard/android/layout_tests/dom/html/level2/events/createEvent03.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent05.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent09.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent10.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent02.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent02.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent06.html +/sdcard/android/layout_tests/dom/html/level2/events/createEvent04.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent06.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent11.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent03.html +/sdcard/android/layout_tests/dom/html/level2/events/createEvent01.html +/sdcard/android/layout_tests/dom/html/level2/events/dispatchEvent07.html +/sdcard/android/layout_tests/dom/html/level2/events/initEvent03.html +/sdcard/android/layout_tests/dom/html/level2/events/createEvent05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement87.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement26.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement141.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement37.html +/sdcard/android/layout_tests/dom/html/level2/html/table12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIsIndexElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameSetElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLModElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement27.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement90.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/table45.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement124.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement40.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLParamElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement30.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptGroupElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement73.html +/sdcard/android/layout_tests/dom/html/level2/html/table28.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor02.html +/sdcard/android/layout_tests/dom/html/level2/html/object09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement27.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement107.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement23.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMetaElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument25.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement56.html +/sdcard/android/layout_tests/dom/html/level2/html/table31.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/object12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement30.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement89.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement110.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLabelElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement28.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement143.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBaseElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement39.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIsIndexElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLModElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement29.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement92.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/table47.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement126.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement42.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLParamElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement75.html +/sdcard/android/layout_tests/dom/html/level2/html/table50.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement29.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement109.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement25.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument27.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement58.html +/sdcard/android/layout_tests/dom/html/level2/html/table33.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/area01.html +/sdcard/android/layout_tests/dom/html/level2/html/object14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement32.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement112.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLabelElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDivElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLUListElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement145.html +/sdcard/android/layout_tests/dom/html/level2/html/button01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement61.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLegendElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCaptionElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement94.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFieldSetElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/table49.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLIElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement128.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMapElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement44.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement77.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/table52.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement131.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLParagraphElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement27.html +/sdcard/android/layout_tests/dom/html/level2/html/table02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement80.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/table35.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/area03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement34.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement114.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement30.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLStyleElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/button03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement63.html +/sdcard/android/layout_tests/dom/html/level2/html/table18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLegendElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement96.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement46.html +/sdcard/android/layout_tests/dom/html/level2/html/table21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/object02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLQuoteElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement79.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement100.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement133.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement29.html +/sdcard/android/layout_tests/dom/html/level2/html/table04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement82.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/table37.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement36.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement116.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement32.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/basefont01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLStyleElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement22.html +/sdcard/android/layout_tests/dom/html/level2/html/button05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement65.html +/sdcard/android/layout_tests/dom/html/level2/html/table40.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement98.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement48.html +/sdcard/android/layout_tests/dom/html/level2/html/table23.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/object04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLPreElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOListElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement102.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement135.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement51.html +/sdcard/android/layout_tests/dom/html/level2/html/table06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement84.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/table39.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement23.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement38.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement118.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement34.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBaseFontElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement24.html +/sdcard/android/layout_tests/dom/html/level2/html/button07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement67.html +/sdcard/android/layout_tests/dom/html/level2/html/table42.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement121.html +/sdcard/android/layout_tests/dom/html/level2/html/dlist01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement70.html +/sdcard/android/layout_tests/dom/html/level2/html/table25.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMenuElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFontElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement24.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement104.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMetaElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement137.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHRElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement53.html +/sdcard/android/layout_tests/dom/html/level2/html/table08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement86.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement25.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement140.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement36.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBaseFontElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameSetElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement26.html +/sdcard/android/layout_tests/dom/html/level2/html/button09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement69.html +/sdcard/android/layout_tests/dom/html/level2/html/table44.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement123.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLParamElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement72.html +/sdcard/android/layout_tests/dom/html/level2/html/table27.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor01.html +/sdcard/android/layout_tests/dom/html/level2/html/object08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement26.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement106.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMetaElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/body01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument24.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement139.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHRElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement55.html +/sdcard/android/layout_tests/dom/html/level2/html/table30.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/object11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement88.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHtmlElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLabelElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTitleElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement27.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement142.html +/sdcard/android/layout_tests/dom/html/level2/html/doc01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBaseElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/AppletsCollection.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement38.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIsIndexElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLModElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement28.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement91.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/table46.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement30.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement125.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement41.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLParamElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement31.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptGroupElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement74.html +/sdcard/android/layout_tests/dom/html/level2/html/table29.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement28.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement108.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement24.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument26.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement57.html +/sdcard/android/layout_tests/dom/html/level2/html/table32.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/object13.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement31.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement111.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLabelElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement29.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement144.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement60.html +/sdcard/android/layout_tests/dom/html/level2/html/table15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLegendElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLModElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement93.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFieldSetElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/table48.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLIElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement127.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement43.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement76.html +/sdcard/android/layout_tests/dom/html/level2/html/table51.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/anchor05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement130.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement26.html +/sdcard/android/layout_tests/dom/html/level2/html/table01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement59.html +/sdcard/android/layout_tests/dom/html/level2/html/table34.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/area02.html +/sdcard/android/layout_tests/dom/html/level2/html/object15.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement33.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement113.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLUListElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBRElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/button02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLButtonElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement62.html +/sdcard/android/layout_tests/dom/html/level2/html/table17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLegendElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement95.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionsCollection06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement129.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMapElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement45.html +/sdcard/android/layout_tests/dom/html/level2/html/table20.html +/sdcard/android/layout_tests/dom/html/level2/html/object01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement78.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/table53.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement17.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement132.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement28.html +/sdcard/android/layout_tests/dom/html/level2/html/table03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement81.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/table36.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/area04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement20.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement35.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement115.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDlistElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement31.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLStyleElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/button04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement64.html +/sdcard/android/layout_tests/dom/html/level2/html/table19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement97.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement47.html +/sdcard/android/layout_tests/dom/html/level2/html/table22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/object03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOListElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLQuoteElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement101.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement19.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement134.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLScriptElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement50.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement83.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/table38.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHeadingElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement22.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement37.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement117.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement33.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDirectoryElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement23.html +/sdcard/android/layout_tests/dom/html/level2/html/button06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement66.html +/sdcard/android/layout_tests/dom/html/level2/html/table41.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement40.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement99.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement120.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement16.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAppletElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement49.html +/sdcard/android/layout_tests/dom/html/level2/html/table24.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFontElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/object05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLCollection11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLImageElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement23.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOListElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement103.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement136.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHRElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement52.html +/sdcard/android/layout_tests/dom/html/level2/html/table07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLSelectElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableColElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement85.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement09.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAnchorElement14.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/hasFeature02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement24.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement39.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement119.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement35.html +/sdcard/android/layout_tests/dom/html/level2/html/table10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBaseFontElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement25.html +/sdcard/android/layout_tests/dom/html/level2/html/button08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement68.html +/sdcard/android/layout_tests/dom/html/level2/html/table43.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLInputElement12.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTextAreaElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableRowElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFrameElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement122.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement18.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFormElement04.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLAreaElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLObjectElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement71.html +/sdcard/android/layout_tests/dom/html/level2/html/table26.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLIFrameElement06.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLFontElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/object07.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableCellElement10.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableElement25.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement105.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLLinkElement08.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLOptionElement05.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement21.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLMetaElement02.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLBodyElement01.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLDocument23.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement138.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLHRElement03.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLTableSectionElement11.html +/sdcard/android/layout_tests/dom/html/level2/html/HTMLElement54.html +/sdcard/android/layout_tests/dom/html/level2/html/table09.html +/sdcard/android/layout_tests/dom/html/level2/html/object10.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_notationsremovenameditemns1.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_entitiessetnameditemns1.html +/sdcard/android/layout_tests/dom/html/level2/core/setAttributeNS10.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_nodedocumentfragmentnormalize2.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_namednodemapinvalidtype1.html +/sdcard/android/layout_tests/dom/html/level2/core/createAttributeNS06.html +/sdcard/android/layout_tests/dom/html/level2/core/createDocumentType04.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_notationssetnameditemns1.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_entitiesremovenameditemns1.html +/sdcard/android/layout_tests/dom/html/level2/core/hc_nodedocumentfragmentnormalize1.html +/sdcard/android/layout_tests/dom/html/level2/core/createDocument08.html +/sdcard/android/layout_tests/plugins/createScriptableObject-before-start.html +/sdcard/android/layout_tests/plugins/get-empty-url.html +/sdcard/android/layout_tests/plugins/return-error-from-new-stream-callback-in-full-frame-plugin.html +/sdcard/android/layout_tests/editing/style/temporary-span-crash.html +/sdcard/android/layout_tests/editing/style/4230923.html +/sdcard/android/layout_tests/editing/inserting/5549929-1.html +/sdcard/android/layout_tests/editing/inserting/6104369.html +/sdcard/android/layout_tests/editing/inserting/5803706-2.html +/sdcard/android/layout_tests/editing/inserting/5685601-2.html +/sdcard/android/layout_tests/editing/inserting/5607069-1.html +/sdcard/android/layout_tests/editing/inserting/5803706-1.html +/sdcard/android/layout_tests/editing/inserting/5685601-1.html +/sdcard/android/layout_tests/editing/inserting/5994480.html +/sdcard/android/layout_tests/editing/inserting/6104369-2.html +/sdcard/android/layout_tests/editing/inserting/insert-before-link-1.html +/sdcard/android/layout_tests/editing/inserting/5378847.html +/sdcard/android/layout_tests/editing/inserting/5685601-3.html +/sdcard/android/layout_tests/editing/execCommand/19403.html +/sdcard/android/layout_tests/editing/execCommand/default-parameters.html +/sdcard/android/layout_tests/editing/execCommand/19455.html +/sdcard/android/layout_tests/editing/execCommand/19087.html +/sdcard/android/layout_tests/editing/execCommand/empty-span-removal.html +/sdcard/android/layout_tests/editing/execCommand/5469868.html +/sdcard/android/layout_tests/editing/execCommand/5575101-1.html +/sdcard/android/layout_tests/editing/execCommand/findString-3.html +/sdcard/android/layout_tests/editing/execCommand/16049.html +/sdcard/android/layout_tests/editing/execCommand/19653-1.html +/sdcard/android/layout_tests/editing/execCommand/arguments-combinations.html +/sdcard/android/layout_tests/editing/execCommand/5575101-3.html +/sdcard/android/layout_tests/editing/execCommand/5770834-1.html +/sdcard/android/layout_tests/editing/execCommand/5483526.html +/sdcard/android/layout_tests/editing/execCommand/5658933-1.html +/sdcard/android/layout_tests/editing/execCommand/6355786.html +/sdcard/android/layout_tests/editing/execCommand/5604313.html +/sdcard/android/layout_tests/editing/execCommand/19653-3.html +/sdcard/android/layout_tests/editing/execCommand/5763082.html +/sdcard/android/layout_tests/editing/execCommand/6444148.html +/sdcard/android/layout_tests/editing/execCommand/4976800.html +/sdcard/android/layout_tests/editing/execCommand/4928635.html +/sdcard/android/layout_tests/editing/execCommand/4920742-2.html +/sdcard/android/layout_tests/editing/execCommand/4917055.html +/sdcard/android/layout_tests/editing/execCommand/5575101-2.html +/sdcard/android/layout_tests/editing/execCommand/12244.html +/sdcard/android/layout_tests/editing/execCommand/19653-2.html +/sdcard/android/layout_tests/editing/execCommand/4916235.html +/sdcard/android/layout_tests/editing/execCommand/5458246.html +/sdcard/android/layout_tests/editing/execCommand/toggle-styles.html +/sdcard/android/layout_tests/editing/pasteboard/5761530-2.html +/sdcard/android/layout_tests/editing/pasteboard/6018653.html +/sdcard/android/layout_tests/editing/pasteboard/4930986-1.html +/sdcard/android/layout_tests/editing/pasteboard/5780697-1.html +/sdcard/android/layout_tests/editing/pasteboard/4930986-3.html +/sdcard/android/layout_tests/editing/pasteboard/5245519.html +/sdcard/android/layout_tests/editing/pasteboard/5521237.html +/sdcard/android/layout_tests/editing/pasteboard/newlines-around-floating-or-positioned.html +/sdcard/android/layout_tests/editing/pasteboard/5078739.html +/sdcard/android/layout_tests/editing/pasteboard/createMarkup-assert.xml +/sdcard/android/layout_tests/editing/pasteboard/4930986-2.html +/sdcard/android/layout_tests/editing/pasteboard/5480736.html +/sdcard/android/layout_tests/editing/selection/5497643.html +/sdcard/android/layout_tests/editing/selection/5825350-1.html +/sdcard/android/layout_tests/editing/selection/setBaseAndExtent-revert-selection.html +/sdcard/android/layout_tests/editing/selection/selection-invalid-offset.html +/sdcard/android/layout_tests/editing/selection/rangeCount.html +/sdcard/android/layout_tests/editing/selection/5714333.html +/sdcard/android/layout_tests/editing/selection/select-line.html +/sdcard/android/layout_tests/editing/selection/doubleclick-whitespace-crash.html +/sdcard/android/layout_tests/editing/selection/containsNode.html +/sdcard/android/layout_tests/editing/selection/select-all-user-select-none.html +/sdcard/android/layout_tests/editing/selection/selectAllChildren.html +/sdcard/android/layout_tests/editing/selection/find-in-text-control.html +/sdcard/android/layout_tests/editing/selection/extend.html +/sdcard/android/layout_tests/editing/selection/5241148.html +/sdcard/android/layout_tests/editing/selection/cleared-by-relayout.html +/sdcard/android/layout_tests/editing/selection/5825350-2.html +/sdcard/android/layout_tests/editing/selection/5794920-1.html +/sdcard/android/layout_tests/editing/selection/5779984-1.html +/sdcard/android/layout_tests/editing/selection/deleteFromDocument.html +/sdcard/android/layout_tests/editing/undo/4059423-1.html +/sdcard/android/layout_tests/editing/undo/4059423-2.html +/sdcard/android/layout_tests/editing/undo/5658727.html +/sdcard/android/layout_tests/editing/undo/5738768.html +/sdcard/android/layout_tests/editing/deleting/2610675-2.html +/sdcard/android/layout_tests/editing/deleting/5847330-2.html +/sdcard/android/layout_tests/editing/deleting/6026335.html +/sdcard/android/layout_tests/editing/deleting/2610675-1.html +/sdcard/android/layout_tests/editing/deleting/5847330-1.html +/sdcard/android/layout_tests/editing/deleting/5433862-1.html +/sdcard/android/layout_tests/editing/deleting/5495723.html +/sdcard/android/layout_tests/editing/deleting/5290534.html +/sdcard/android/layout_tests/editing/deleting/2610675-3.html /sdcard/android/layout_tests/fast/replaced/object-param-no-name.html /sdcard/android/layout_tests/fast/dynamic/subtree-common-root.html /sdcard/android/layout_tests/fast/dynamic/hovered-detach.html @@ -15,6 +1063,7 @@ /sdcard/android/layout_tests/fast/text/find-backwards.html /sdcard/android/layout_tests/fast/text/large-text-composed-char-dos.html /sdcard/android/layout_tests/fast/text/find-case-folding.html +/sdcard/android/layout_tests/fast/text/text-shadow-extreme-value.html /sdcard/android/layout_tests/fast/text/line-breaks-after-ideographic-comma-or-full-stop.html /sdcard/android/layout_tests/fast/encoding/gbk/chinese.html /sdcard/android/layout_tests/fast/encoding/gbk/x-euc-cn.html @@ -68,12 +1117,10 @@ /sdcard/android/layout_tests/fast/encoding/pseudo-xml-3.html /sdcard/android/layout_tests/fast/encoding/pseudo-xml.html /sdcard/android/layout_tests/fast/encoding/tag-in-title.html -/sdcard/android/layout_tests/fast/encoding/yahoo-mail.html -/sdcard/android/layout_tests/fast/encoding/ahram-org-eg.html -/sdcard/android/layout_tests/fast/encoding/noscript-in-head.html /sdcard/android/layout_tests/fast/encoding/script-in-head.html /sdcard/android/layout_tests/fast/encoding/css-cached-bom.html /sdcard/android/layout_tests/fast/encoding/mispositioned-meta.html +/sdcard/android/layout_tests/fast/encoding/preload-encoding.html /sdcard/android/layout_tests/fast/multicol/gap-non-negative.html /sdcard/android/layout_tests/fast/multicol/content-height-zero-crash.html /sdcard/android/layout_tests/fast/doctypes/doctype-at-end.html @@ -106,6 +1153,7 @@ /sdcard/android/layout_tests/fast/events/no-blur-on-page-leave.html /sdcard/android/layout_tests/fast/events/onload-name-collision.html /sdcard/android/layout_tests/fast/events/div-focus.html +/sdcard/android/layout_tests/fast/events/related-target.html /sdcard/android/layout_tests/fast/events/overflow-events.html /sdcard/android/layout_tests/fast/events/create-document-crash-on-attach-event.html /sdcard/android/layout_tests/fast/events/no-blur-on-enter-button.html @@ -113,6 +1161,8 @@ /sdcard/android/layout_tests/fast/events/shadow-boundary-crossing.html /sdcard/android/layout_tests/fast/events/submit-reset-nested-bubble.html /sdcard/android/layout_tests/fast/events/nested-event-remove-node-crash.html +/sdcard/android/layout_tests/fast/events/onsubmit-bubbling.html +/sdcard/android/layout_tests/fast/events/onload-fires-twice.html /sdcard/android/layout_tests/fast/events/mousedown_in_scrollbar.html /sdcard/android/layout_tests/fast/events/window-load-capture.html /sdcard/android/layout_tests/fast/events/event-instanceof.html @@ -128,6 +1178,8 @@ /sdcard/android/layout_tests/fast/events/event-targets.html /sdcard/android/layout_tests/fast/events/space-scroll-event.html /sdcard/android/layout_tests/fast/events/onload-after-document-close-no-subresource.html +/sdcard/android/layout_tests/fast/events/nested-window-event.html +/sdcard/android/layout_tests/fast/events/selectstart-during-autoscroll.html /sdcard/android/layout_tests/fast/events/stopPropagation-checkbox.html /sdcard/android/layout_tests/fast/events/tab-crash-with-image-map.html /sdcard/android/layout_tests/fast/events/simulated-key-state.html @@ -166,10 +1218,59 @@ /sdcard/android/layout_tests/fast/regex/slow.html /sdcard/android/layout_tests/fast/regex/malformed-escapes.html /sdcard/android/layout_tests/fast/regex/early-acid3-86.html -/sdcard/android/layout_tests/fast/regex/test1.html /sdcard/android/layout_tests/fast/regex/alternative-length-miscalculation.html /sdcard/android/layout_tests/fast/regex/test4.html /sdcard/android/layout_tests/fast/regex/non-capturing-backtracking.html +/sdcard/android/layout_tests/fast/js/kde/Boolean.html +/sdcard/android/layout_tests/fast/js/kde/function.html +/sdcard/android/layout_tests/fast/js/kde/function_length.html +/sdcard/android/layout_tests/fast/js/kde/const.html +/sdcard/android/layout_tests/fast/js/kde/constructor_length.html +/sdcard/android/layout_tests/fast/js/kde/statements.html +/sdcard/android/layout_tests/fast/js/kde/math.html +/sdcard/android/layout_tests/fast/js/kde/assignments.html +/sdcard/android/layout_tests/fast/js/kde/crash-1.html +/sdcard/android/layout_tests/fast/js/kde/delete.html +/sdcard/android/layout_tests/fast/js/kde/var_decl_init.html +/sdcard/android/layout_tests/fast/js/kde/literals.html +/sdcard/android/layout_tests/fast/js/kde/eval.html +/sdcard/android/layout_tests/fast/js/kde/j-comment-3.html +/sdcard/android/layout_tests/fast/js/kde/StringObject.html +/sdcard/android/layout_tests/fast/js/kde/crash-2.html +/sdcard/android/layout_tests/fast/js/kde/exception_propagation.html +/sdcard/android/layout_tests/fast/js/kde/conditional.html +/sdcard/android/layout_tests/fast/js/kde/scope.html +/sdcard/android/layout_tests/fast/js/kde/parse.html +/sdcard/android/layout_tests/fast/js/kde/function_arguments.html +/sdcard/android/layout_tests/fast/js/kde/arguments-scope.html +/sdcard/android/layout_tests/fast/js/kde/lval-exceptions.html +/sdcard/android/layout_tests/fast/js/kde/operators.html +/sdcard/android/layout_tests/fast/js/kde/Array.html +/sdcard/android/layout_tests/fast/js/kde/md5-1.html +/sdcard/android/layout_tests/fast/js/kde/object_prototype_tostring.html +/sdcard/android/layout_tests/fast/js/kde/Date-setYear.html +/sdcard/android/layout_tests/fast/js/kde/GlobalObject.html +/sdcard/android/layout_tests/fast/js/kde/prototype_proto.html +/sdcard/android/layout_tests/fast/js/kde/evil-n.html +/sdcard/android/layout_tests/fast/js/kde/RegExp.html +/sdcard/android/layout_tests/fast/js/kde/cast.html +/sdcard/android/layout_tests/fast/js/kde/j-comment-4.html +/sdcard/android/layout_tests/fast/js/kde/iteration.html +/sdcard/android/layout_tests/fast/js/kde/comment-1.html +/sdcard/android/layout_tests/fast/js/kde/Prototype.html +/sdcard/android/layout_tests/fast/js/kde/completion.html +/sdcard/android/layout_tests/fast/js/kde/exceptions.html +/sdcard/android/layout_tests/fast/js/kde/md5-2.html +/sdcard/android/layout_tests/fast/js/kde/Error.html +/sdcard/android/layout_tests/fast/js/kde/function_constructor.html +/sdcard/android/layout_tests/fast/js/kde/object_prototype.html +/sdcard/android/layout_tests/fast/js/kde/empty.html +/sdcard/android/layout_tests/fast/js/kde/inbuilt_function_proto.html +/sdcard/android/layout_tests/fast/js/kde/func-decl.html +/sdcard/android/layout_tests/fast/js/kde/comment-2.html +/sdcard/android/layout_tests/fast/js/kde/inbuilt_function_tostring.html +/sdcard/android/layout_tests/fast/js/kde/Object.html +/sdcard/android/layout_tests/fast/js/kde/prototype_length.html /sdcard/android/layout_tests/fast/js/pic/cached-single-entry-transition.html /sdcard/android/layout_tests/fast/js/pic/cached-prototype-setter.html /sdcard/android/layout_tests/fast/js/pic/get-empty-string.html @@ -226,7 +1327,6 @@ /sdcard/android/layout_tests/fast/js/do-while-semicolon.html /sdcard/android/layout_tests/fast/js/regexp-divequal.html /sdcard/android/layout_tests/fast/js/named-function-expression.html -/sdcard/android/layout_tests/fast/js/array-iterate-backwards.html /sdcard/android/layout_tests/fast/js/constructor-attributes.html /sdcard/android/layout_tests/fast/js/array-some.html /sdcard/android/layout_tests/fast/js/missing-title-end-tag-js.html @@ -345,7 +1445,6 @@ /sdcard/android/layout_tests/fast/js/date-constructor.html /sdcard/android/layout_tests/fast/js/date-big-setdate.html /sdcard/android/layout_tests/fast/js/array-every.html -/sdcard/android/layout_tests/fast/js/function-toString-parentheses.html /sdcard/android/layout_tests/fast/js/array-functions-non-arrays.html /sdcard/android/layout_tests/fast/js/while-expression-value.html /sdcard/android/layout_tests/fast/js/string-replace-3.html @@ -393,6 +1492,7 @@ /sdcard/android/layout_tests/fast/js/throw-from-array-sort.html /sdcard/android/layout_tests/fast/js/slash-lineterminator-parse.html /sdcard/android/layout_tests/fast/js/dot-node-base-exception.html +/sdcard/android/layout_tests/fast/js/toString-stack-overflow.html /sdcard/android/layout_tests/fast/js/codegen-peephole-locals.html /sdcard/android/layout_tests/fast/js/constant-count.html /sdcard/android/layout_tests/fast/js/regexp-compile.html @@ -431,8 +1531,6 @@ /sdcard/android/layout_tests/fast/dom/HTMLTableElement/tBodies.html /sdcard/android/layout_tests/fast/dom/HTMLTableElement/rows.html /sdcard/android/layout_tests/fast/dom/HTMLDocument/write-multiple-calls.html -/sdcard/android/layout_tests/fast/dom/HTMLDocument/document-special-properties.html -/sdcard/android/layout_tests/fast/dom/HTMLDocument/url-getset.html /sdcard/android/layout_tests/fast/dom/HTMLDocument/writeln-call.html /sdcard/android/layout_tests/fast/dom/HTMLDocument/document-plugins.html /sdcard/android/layout_tests/fast/dom/HTMLDocument/title-get.html @@ -559,6 +1657,12 @@ /sdcard/android/layout_tests/fast/dom/Window/alert-undefined.html /sdcard/android/layout_tests/fast/dom/Window/window-open-top.html /sdcard/android/layout_tests/fast/dom/Window/window-appendages-cleared.html +/sdcard/android/layout_tests/fast/dom/Window/global-opener-function.html +/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing.html +/sdcard/android/layout_tests/fast/dom/Window/remove-timeout-crash.html +/sdcard/android/layout_tests/fast/dom/Window/window-custom-prototype.html +/sdcard/android/layout_tests/fast/dom/Window/timeout-callback-scope.html +/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing-name.html /sdcard/android/layout_tests/fast/dom/Window/window-open-parent-no-parent.html /sdcard/android/layout_tests/fast/dom/Window/closure-access-after-navigation-iframe.html /sdcard/android/layout_tests/fast/dom/HTMLTableRowElement/cells.html @@ -611,23 +1715,7 @@ /sdcard/android/layout_tests/fast/dom/css-dom-read.html /sdcard/android/layout_tests/fast/dom/image-object.html /sdcard/android/layout_tests/fast/dom/gc-5.html -/sdcard/android/layout_tests/fast/dom/node-filter-gc.html -/sdcard/android/layout_tests/fast/dom/noscript-canvas-in-created-html-document.html -/sdcard/android/layout_tests/fast/dom/DOMParser-assign-variable.html -/sdcard/android/layout_tests/fast/dom/timer-clear-interval-in-handler.html -/sdcard/android/layout_tests/fast/dom/implementation-createHTMLDocument.html -/sdcard/android/layout_tests/fast/dom/iframe-document.html -/sdcard/android/layout_tests/fast/dom/document-all-input.html -/sdcard/android/layout_tests/fast/dom/null-document-location-href-put-crash.html -/sdcard/android/layout_tests/fast/dom/getelementsbytagnamens-mixed-namespaces.html -/sdcard/android/layout_tests/fast/dom/object-plugin-hides-properties.html -/sdcard/android/layout_tests/fast/dom/gc-2.html -/sdcard/android/layout_tests/fast/dom/computed-style-set-property.html -/sdcard/android/layout_tests/fast/dom/inner-text-001.html -/sdcard/android/layout_tests/fast/dom/css-selectorText.html -/sdcard/android/layout_tests/fast/dom/replace-first-child.html -/sdcard/android/layout_tests/fast/dom/importNode-null.html -/sdcard/android/layout_tests/fast/dom/select-selectedIndex-multiple.html +/sdcard/android/layout_tests/fast/dom/cssTarget-crash.html /sdcard/android/layout_tests/fast/dom/xmlhttprequest-invalid-values.html /sdcard/android/layout_tests/fast/dom/space-to-text.html /sdcard/android/layout_tests/fast/dom/css-set-property-exception.html @@ -643,6 +1731,7 @@ /sdcard/android/layout_tests/fast/dom/getelementbyname-invalidation.html /sdcard/android/layout_tests/fast/dom/capturing-event-listeners.html /sdcard/android/layout_tests/fast/dom/title-text-property.html +/sdcard/android/layout_tests/fast/dom/null-page-show-modal-dialog-crash.html /sdcard/android/layout_tests/fast/dom/incompatible-operations.html /sdcard/android/layout_tests/fast/dom/xmlhttprequest-html-response-encoding.html /sdcard/android/layout_tests/fast/dom/inner-text-rtl.html @@ -671,7 +1760,6 @@ /sdcard/android/layout_tests/fast/dom/document-all-select.html /sdcard/android/layout_tests/fast/dom/anchor-backslash.html /sdcard/android/layout_tests/fast/dom/css-mediarule-functions.html -/sdcard/android/layout_tests/fast/dom/gc-acid3.html /sdcard/android/layout_tests/fast/dom/duplicate-ids-document-order.html /sdcard/android/layout_tests/fast/dom/exception-no-frame-inline-script-crash.html /sdcard/android/layout_tests/fast/dom/XMLSerializer-doctype2.html @@ -686,6 +1774,14 @@ /sdcard/android/layout_tests/fast/dom/createElement.html /sdcard/android/layout_tests/fast/dom/createElement-with-column.xml /sdcard/android/layout_tests/fast/dom/simultaneouslyRegsiteredTimerFireOrder.html +/sdcard/android/layout_tests/fast/dom/clone-node-form-elements-with-attr.html +/sdcard/android/layout_tests/fast/dom/setAttributeNS.html +/sdcard/android/layout_tests/fast/dom/anchor-toString.html +/sdcard/android/layout_tests/fast/dom/dom-add-optionelement.html +/sdcard/android/layout_tests/fast/dom/location-assign.html +/sdcard/android/layout_tests/fast/dom/documenturi-affects-relative-paths.html +/sdcard/android/layout_tests/fast/dom/javascript-backslash.html +/sdcard/android/layout_tests/fast/dom/setAttribute-using-initial-input-value.html /sdcard/android/layout_tests/fast/dom/generic-form-element-assert.html /sdcard/android/layout_tests/fast/dom/css-shortHands.html /sdcard/android/layout_tests/fast/dom/dom-instanceof.html @@ -713,7 +1809,6 @@ /sdcard/android/layout_tests/fast/dom/exception-no-frame-timeout-crash.html /sdcard/android/layout_tests/fast/dom/title-text-property-2.html /sdcard/android/layout_tests/fast/dom/no-elements.html -/sdcard/android/layout_tests/fast/dom/non-numeric-values-numeric-parameters.html /sdcard/android/layout_tests/fast/dom/gc-4.html /sdcard/android/layout_tests/fast/dom/inner-width-height.html /sdcard/android/layout_tests/fast/dom/XMLSerializer-doctype.html @@ -723,14 +1818,6 @@ /sdcard/android/layout_tests/fast/dom/gc-1.html /sdcard/android/layout_tests/fast/dom/select-selectedIndex-bug-12942.html /sdcard/android/layout_tests/fast/dom/frame-contentWindow-crash.html -/sdcard/android/layout_tests/fast/dom/namednodemap-namelookup.html -/sdcard/android/layout_tests/fast/dom/null-document-location-replace-crash.html -/sdcard/android/layout_tests/fast/dom/htmlcollection-detectability.html -/sdcard/android/layout_tests/fast/dom/documenturi-assigned-junk-implies-relative-urls-do-not-resolve.html -/sdcard/android/layout_tests/fast/dom/collection-namedItem-via-item.html -/sdcard/android/layout_tests/fast/dom/set-inner-text-newlines.html -/sdcard/android/layout_tests/fast/dom/document-dir-property.html -/sdcard/android/layout_tests/fast/dom/undetectable-style-filter.html /sdcard/android/layout_tests/fast/dom/domListEnumeration.html /sdcard/android/layout_tests/fast/dom/destroy-selected-radio-button-crash.html /sdcard/android/layout_tests/fast/dom/script-add.html @@ -742,6 +1829,12 @@ /sdcard/android/layout_tests/fast/dom/attribute-namespaces-get-set.html /sdcard/android/layout_tests/fast/dom/innerHTML-escaping-attribute.html /sdcard/android/layout_tests/fast/dom/null-document-xmlhttprequest-open.html +/sdcard/android/layout_tests/fast/dom/null-document-location-put-crash.html +/sdcard/android/layout_tests/fast/dom/ImageDocument-image-deletion.html +/sdcard/android/layout_tests/fast/dom/document-scripts.html +/sdcard/android/layout_tests/fast/gradients/crash-on-remove.html +/sdcard/android/layout_tests/fast/xpath/xpath-empty-string.html +/sdcard/android/layout_tests/fast/invalid/test-case-tr-th-td-should-not-close-dl-list.html /sdcard/android/layout_tests/fast/invalid/nestedh3s-rapidweaver.html /sdcard/android/layout_tests/fast/forms/element-by-name.html /sdcard/android/layout_tests/fast/forms/HTMLOptionElement_selected.html @@ -751,7 +1844,6 @@ /sdcard/android/layout_tests/fast/forms/textfield-focus-out.html /sdcard/android/layout_tests/fast/forms/willvalidate-000.html /sdcard/android/layout_tests/fast/forms/form-data-encoding-normalization-overrun.html -/sdcard/android/layout_tests/fast/forms/select-reset.html /sdcard/android/layout_tests/fast/forms/input-named-action-overrides-action-attribute.html /sdcard/android/layout_tests/fast/forms/empty-get.html /sdcard/android/layout_tests/fast/forms/display-none-in-onchange-keyboard.html @@ -762,18 +1854,25 @@ /sdcard/android/layout_tests/fast/forms/autofocus-opera-006.html /sdcard/android/layout_tests/fast/forms/activate-and-disabled-elements.html /sdcard/android/layout_tests/fast/forms/form-get-multipart2.html +/sdcard/android/layout_tests/fast/forms/tabs-with-modifiers.html +/sdcard/android/layout_tests/fast/forms/menulist-no-renderer-onmousedown.html +/sdcard/android/layout_tests/fast/forms/saved-state-adoptNode-crash.html /sdcard/android/layout_tests/fast/forms/select-replace-option.html /sdcard/android/layout_tests/fast/forms/textarea-setvalue-submit.html +/sdcard/android/layout_tests/fast/forms/11423.html /sdcard/android/layout_tests/fast/forms/cursor-position.html /sdcard/android/layout_tests/fast/forms/willvalidate-007.html /sdcard/android/layout_tests/fast/forms/input-changing-value.html /sdcard/android/layout_tests/fast/forms/double-focus.html /sdcard/android/layout_tests/fast/forms/form-data-encoding-2.html /sdcard/android/layout_tests/fast/forms/focus.html +/sdcard/android/layout_tests/fast/forms/willvalidate-004.html /sdcard/android/layout_tests/fast/forms/button-in-forms-collection.html +/sdcard/android/layout_tests/fast/forms/input-text-enter.html /sdcard/android/layout_tests/fast/forms/input-delete.html /sdcard/android/layout_tests/fast/forms/placeholder-non-textfield.html /sdcard/android/layout_tests/fast/forms/element-order.html +/sdcard/android/layout_tests/fast/forms/form-post-urlencoded.html /sdcard/android/layout_tests/fast/forms/willvalidate-001.html /sdcard/android/layout_tests/fast/forms/option-constructor-selected.html /sdcard/android/layout_tests/fast/forms/8250.html @@ -809,12 +1908,11 @@ /sdcard/android/layout_tests/fast/forms/select-index-setter.html /sdcard/android/layout_tests/fast/forms/option-change-single-selected.html /sdcard/android/layout_tests/fast/forms/listbox-scroll-after-options-removed.html +/sdcard/android/layout_tests/fast/forms/input-selection-hidden.html +/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-keyboard.html /sdcard/android/layout_tests/fast/forms/willvalidate-002.html +/sdcard/android/layout_tests/fast/forms/option-in-optgroup-removal.html /sdcard/android/layout_tests/fast/forms/form-data-encoding.html -/sdcard/android/layout_tests/fast/forms/input-appearance-elementFromPoint.html -/sdcard/android/layout_tests/fast/forms/select-set-inner.html -/sdcard/android/layout_tests/fast/forms/missing-action.html -/sdcard/android/layout_tests/fast/forms/textarea-scrollbar-height.html /sdcard/android/layout_tests/fast/forms/textarea-default-value-leading-newline.html /sdcard/android/layout_tests/fast/forms/autofocus-opera-008.html /sdcard/android/layout_tests/fast/forms/listbox-typeahead-empty.html @@ -828,9 +1926,9 @@ /sdcard/android/layout_tests/fast/forms/input-setvalue-selection.html /sdcard/android/layout_tests/fast/forms/autofocus-opera-002.html /sdcard/android/layout_tests/fast/forms/old-names.html -/sdcard/android/layout_tests/fast/forms/willvalidate-006.html /sdcard/android/layout_tests/fast/forms/focus-style-pending.html /sdcard/android/layout_tests/fast/forms/textarea-hard-linewrap-empty.html +/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-mouse.html /sdcard/android/layout_tests/fast/forms/document-write.html /sdcard/android/layout_tests/fast/forms/slow-click.html /sdcard/android/layout_tests/fast/forms/autofocus-attribute.html @@ -985,6 +2083,7 @@ /sdcard/android/layout_tests/fast/loader/data-url-encoding-html.html /sdcard/android/layout_tests/fast/loader/file-URL-with-port-number.html /sdcard/android/layout_tests/fast/loader/link-no-URL.html +/sdcard/android/layout_tests/fast/xsl/xslt-fragment-in-empty-doc.html /sdcard/android/layout_tests/fast/canvas/pattern-with-transform.html /sdcard/android/layout_tests/fast/canvas/gradient-addColorStop-with-invalid-color.html /sdcard/android/layout_tests/fast/canvas/canvas-gradient-without-path.html @@ -1041,6 +2140,63 @@ /sdcard/android/layout_tests/fast/frames/location-change.html /sdcard/android/layout_tests/fast/frames/frame-base-url.html /sdcard/android/layout_tests/fast/frames/iframe-display-none.html -/sdcard/android/layout_tests/fast/frames/frame-display-none-focus.html -/sdcard/android/layout_tests/fast/reflections/teardown-crash.html -/sdcard/android/layout_tests/fast/reflections/reflection-computed-style.html +/sdcard/android/layout_tests/animations/transition-and-animation-2.html +/sdcard/android/layout_tests/animations/import-crash.html +/sdcard/android/layout_tests/animations/empty-keyframes.html +/sdcard/android/layout_tests/animations/width-using-ems.html +/sdcard/android/layout_tests/animations/keyframes-out-of-order.html +/sdcard/android/layout_tests/geolocation/geolocation-not-implemented.html +/sdcard/android/layout_tests/traversal/node-iterator-003.html +/sdcard/android/layout_tests/traversal/node-iterator-005.html +/sdcard/android/layout_tests/traversal/node-iterator-007.html +/sdcard/android/layout_tests/traversal/tree-walker-001.html +/sdcard/android/layout_tests/traversal/node-iterator-009.html +/sdcard/android/layout_tests/traversal/tree-walker-003.html +/sdcard/android/layout_tests/traversal/tree-walker-005.html +/sdcard/android/layout_tests/traversal/exception-forwarding.html +/sdcard/android/layout_tests/traversal/stay-within-root.html +/sdcard/android/layout_tests/traversal/node-iterator-002.html +/sdcard/android/layout_tests/traversal/node-iterator-004.html +/sdcard/android/layout_tests/traversal/node-iterator-006.html +/sdcard/android/layout_tests/traversal/node-iterator-006a.html +/sdcard/android/layout_tests/traversal/tree-walker-002.html +/sdcard/android/layout_tests/traversal/node-iterator-008.html +/sdcard/android/layout_tests/traversal/tree-walker-004.html +/sdcard/android/layout_tests/traversal/tree-walker-006.html +/sdcard/android/layout_tests/traversal/size-zero-run.html +/sdcard/android/layout_tests/traversal/acid3-test-2.html +/sdcard/android/layout_tests/traversal/tree-walker-filter-1.html +/sdcard/android/layout_tests/traversal/node-iterator-001.html +/sdcard/android/layout_tests/css2.1/atrule_longest_match.html +/sdcard/android/layout_tests/css1/units/zero-duration-without-units.html +/sdcard/android/layout_tests/css3/khtml-background-size-0x0-bmp.html +/sdcard/android/layout_tests/transitions/transition-end-event-all-properties.html +/sdcard/android/layout_tests/transitions/transition-end-event-window.html +/sdcard/android/layout_tests/transitions/transition-end-event-multiple-03.html +/sdcard/android/layout_tests/transitions/interrupted-all-transition.html +/sdcard/android/layout_tests/transitions/transition-end-event-set-none.html +/sdcard/android/layout_tests/transitions/zero-duration-without-units.html +/sdcard/android/layout_tests/transitions/zero-duration-with-non-zero-delay-start.html +/sdcard/android/layout_tests/transitions/inherit-other-props.html +/sdcard/android/layout_tests/transitions/transition-end-event-multiple-04.html +/sdcard/android/layout_tests/transitions/transition-duration-cleared-in-transitionend-crash.html +/sdcard/android/layout_tests/transitions/interrupt-zero-duration.html +/sdcard/android/layout_tests/transitions/retargetted-transition.html +/sdcard/android/layout_tests/transitions/override-transition-crash.html +/sdcard/android/layout_tests/transitions/transition-end-event-left.html +/sdcard/android/layout_tests/transitions/matched-transform-functions.html +/sdcard/android/layout_tests/transitions/transition-end-event-multiple-01.html +/sdcard/android/layout_tests/transitions/transform-op-list-match.html +/sdcard/android/layout_tests/transitions/interrupt-transform-transition.html +/sdcard/android/layout_tests/transitions/transition-end-event-container.html +/sdcard/android/layout_tests/transitions/transition-end-event-nested.html +/sdcard/android/layout_tests/transitions/change-values-during-transition.html +/sdcard/android/layout_tests/transitions/transition-end-event-attributes.html +/sdcard/android/layout_tests/transitions/inherit.html +/sdcard/android/layout_tests/transitions/transition-end-event-create.html +/sdcard/android/layout_tests/transitions/transition-end-event-multiple-02.html +/sdcard/android/layout_tests/transitions/shadow.html +/sdcard/android/layout_tests/transitions/transition-end-event-transform.html +/sdcard/android/layout_tests/transitions/transition-timing-function.html +/sdcard/android/layout_tests/transitions/transform-op-list-no-match.html +/sdcard/android/layout_tests/transitions/transition-end-event-destroy-renderer.html diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py index 5409a0cdb0e3..49165d02cb40 100755 --- a/tests/DumpRenderTree/assets/run_layout_tests.py +++ b/tests/DumpRenderTree/assets/run_layout_tests.py @@ -61,7 +61,8 @@ def DumpRenderTreeFinished(adb_cmd): adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] return adb_output.strip() == "#DONE" -def DiffResults(marker, new_results, old_results, diff_results, strip_reason): +def DiffResults(marker, new_results, old_results, diff_results, strip_reason, + new_count_first=True): """ Given two result files, generate diff and write to diff_results file. All arguments are absolute paths to files. @@ -85,21 +86,25 @@ def DiffResults(marker, new_results, old_results, diff_results, strip_reason): for i in range(0, len(cdict)): cdict[i] = cdict[i].split(' ')[0] + "\n" - # Find results in new_results missing in old_results - new_count=0 - for line in ndict: - if line not in cdict: - diff_file.writelines("+ " + line) - new_count += 1 - - # Find results in old_results missing in new_results - missing_count=0 - for line in cdict: - if line not in ndict: - diff_file.writelines("- " + line) - missing_count += 1 - - logging.info(marker + " >>> " + str(new_count) + " new, " + str(missing_count) + " misses") + params = { + "new": [0, ndict, cdict, "+"], + "miss": [0, cdict, ndict, "-"] + } + if new_count_first: + order = ["new", "miss"] + else: + order = ["miss", "new"] + + for key in order: + for line in params[key][1]: + if line not in params[key][2]: + if line[-1] != "\n": + line += "\n"; + diff_file.writelines(params[key][3] + line) + params[key][0] += 1 + + logging.info(marker + " >>> " + str(params["new"][0]) + " new, " + + str(params["miss"][0]) + " misses") diff_file.writelines("\n\n") @@ -121,12 +126,12 @@ def CompareResults(ref_dir, results_dir): if os.path.exists(diff_result): os.remove(diff_result) - files=["passed", "failed", "nontext", "crashed"] + files=["crashed", "failed", "passed", "nontext"] for f in files: result_file_name = "layout_tests_" + f + ".txt" DiffResults(f, os.path.join(results_dir, result_file_name), os.path.join(ref_dir, result_file_name), diff_result, - f == "failed") + False, f != "passed") logging.info("Detailed diffs are in " + diff_result) def main(options, args): diff --git a/tests/DumpRenderTree/assets/run_reliability_tests.py b/tests/DumpRenderTree/assets/run_reliability_tests.py index a2422938d68d..23f93df8508e 100755 --- a/tests/DumpRenderTree/assets/run_reliability_tests.py +++ b/tests/DumpRenderTree/assets/run_reliability_tests.py @@ -10,14 +10,16 @@ import logging import optparse -import random +import os import subprocess import sys import time +from Numeric import * TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt" TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt" TEST_TIMEOUT_FILE = "/sdcard/android/reliability_timeout_test.txt" +TEST_LOAD_TIME_FILE = "/sdcard/android/reliability_load_time.txt" HTTP_URL_FILE = "urllist_http" HTTPS_URL_FILE = "urllist_https" NUM_URLS = 25 @@ -41,40 +43,67 @@ def DumpRenderTreeFinished(adb_cmd): return adb_output.strip() == "#DONE" -def RandomPick(file_name, approx_size, num_needed): - """Randomly pick lines from the text file specifed. +def RemoveDeviceFile(adb_cmd, file_name): + shell_cmd_str = adb_cmd + " shell rm " + file_name + subprocess.Popen(shell_cmd_str, + shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() - Args: - file_name: the text file where lines should be picked from - approx_size: an approximate size of the text file - num_needed: how many lines are needed from the file - Returns: - an array of string - """ - p = float(num_needed) / approx_size - num_picked = 0 - lines = [] - random.seed() - - while num_picked < num_needed: - file_handle = open(file_name, "r") - for line in file_handle: - line = line.strip() - if float(random.randint(0, approx_size)) / approx_size < p: - lines.append(line) - num_picked += 1 - if num_picked == num_needed: - break - file_handle.close() - return lines +def Bugreport(url, bugreport_dir, adb_cmd): + """Pull a bugreport from the device.""" + bugreport_filename = "%s/reliability_bugreport_%d.txt" % (bugreport_dir, + int(time.time())) + + # prepend the report with url + handle = open(bugreport_filename, "w") + handle.writelines("Bugreport for crash in url - %s\n\n" % url) + handle.close() + + cmd = "%s bugreport >> %s" % (adb_cmd, bugreport_filename) + os.system(cmd) + + +def ProcessPageLoadTime(raw_log): + """Processes the raw page load time logged by test app.""" + log_handle = open(raw_log, "r") + load_times = {} + + for line in log_handle: + line = line.strip() + pair = line.split("|") + if len(pair) != 2: + logging.info("Line has more than one '|': " + line) + continue + if pair[0] not in load_times: + load_times[pair[0]] = [] + try: + pair[1] = int(pair[1]) + except ValueError: + logging.info("Lins has non-numeric load time: " + line) + continue + load_times[pair[0]].append(pair[1]) + + log_handle.close() + + # rewrite the average time to file + log_handle = open(raw_log, "w") + for url, times in load_times.iteritems(): + # calculate std + arr = array(times) + avg = average(arr) + d = arr - avg + std = sqrt(sum(d * d) / len(arr)) + output = ("%-70s%-10d%-10d%-12.2f%-12.2f%s\n" % + (url, min(arr), max(arr), avg, std, + array2string(arr))) + log_handle.write(output) + log_handle.close() def main(options, args): """Send the url list to device and start testing, restart if crashed.""" - generate_url = False - # Set up logging format. log_level = logging.INFO if options.verbose: @@ -84,33 +113,37 @@ def main(options, args): # Include all tests if none are specified. if not args: - path = "/tmp/url_list_%d.txt" % time.time() - generate_url = True - logging.info("A URL list is not provided, will be automatically generated.") + print "Missing URL list file" + sys.exit(1) else: path = args[0] if not options.crash_file: - print "missing crash file name, use --crash-file to specify" + print "Missing crash file name, use --crash-file to specify" sys.exit(1) else: crashed_file = options.crash_file if not options.timeout_file: - print "missing timeout file, use --timeout-file to specify" + print "Missing timeout file, use --timeout-file to specify" sys.exit(1) else: timedout_file = options.timeout_file - http = RandomPick(HTTP_URL_FILE, 500000, NUM_URLS) - https = RandomPick(HTTPS_URL_FILE, 45000, NUM_URLS) + if not options.delay: + manual_delay = 0 + else: + manual_delay = options.delay - if generate_url: - file_handle = open(path, "w") - for i in range(0, NUM_URLS): - file_handle.write(http[i] + "\n") - file_handle.write(https[i] + "\n") - file_handle.close() + if not options.bugreport: + bugreport_dir = "." + else: + bugreport_dir = options.bugreport + if not os.path.exists(bugreport_dir): + os.makedirs(bugreport_dir) + if not os.path.isdir(bugreport_dir): + logging.error("Cannot create results dir: " + bugreport_dir) + sys.exit(1) adb_cmd = "adb " if options.adb_options: @@ -128,6 +161,11 @@ def main(options, args): logging.error(adb_error) sys.exit(1) + # clean up previous results + RemoveDeviceFile(adb_cmd, TEST_STATUS_FILE) + RemoveDeviceFile(adb_cmd, TEST_TIMEOUT_FILE) + RemoveDeviceFile(adb_cmd, TEST_LOAD_TIME_FILE) + logging.info("Running the test ...") # Count crashed tests. @@ -142,11 +180,15 @@ def main(options, args): # Call ReliabilityTestsAutoTest#startReliabilityTests test_cmd = (test_cmd_prefix + " -e class " - "com.android.dumprendertree.ReliabilityTestsAutoTest#" - "startReliabilityTests -e timeout " + timeout_ms - + test_cmd_postfix) + "com.android.dumprendertree.ReliabilityTest#" + "runReliabilityTest -e timeout %s -e delay %s" % + (str(timeout_ms), str(manual_delay))) + + if options.logtime: + test_cmd += " -e logtime true" + + test_cmd += test_cmd_postfix - time_start = time.time() adb_output = subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] @@ -157,19 +199,12 @@ def main(options, args): stdout=subprocess.PIPE).communicate()[0] logging.info(crashed_test + " CRASHED") crashed_tests.append(crashed_test) + Bugreport(crashed_test, bugreport_dir, adb_cmd) logging.info("Resuming reliability test runner...") - test_cmd = (test_cmd_prefix + " -e class " - "com.android.dumprendertree.ReliabilityTestsAutoTest#" - "resumeReliabilityTests -e timeout " + timeout_ms - + test_cmd_postfix) adb_output = subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] - time_end = time.time() - fp = open("time_stat", "a") - fp.writelines("%.2f\n" % ((time_end - time_start) / NUM_URLS / 2)) - fp.close() if (adb_output.find("INSTRUMENTATION_FAILED") != -1 or adb_output.find("Process crashed.") != -1): logging.error("Error happened : " + adb_output) @@ -186,29 +221,46 @@ def main(options, args): else: logging.info("No crash found.") + # get timeout file from sdcard test_cmd = (adb_cmd + "pull \"" + TEST_TIMEOUT_FILE + "\" \"" + timedout_file + "\"") - subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + if options.logtime: + # get logged page load times from sdcard + test_cmd = (adb_cmd + "pull \"" + TEST_LOAD_TIME_FILE + "\" \"" + + options.logtime + "\"") + subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + ProcessPageLoadTime(options.logtime) + if "__main__" == __name__: option_parser = optparse.OptionParser() - option_parser.add_option("", "--time-out-ms", + option_parser.add_option("-t", "--time-out-ms", default=60000, help="set the timeout for each test") - option_parser.add_option("", "--verbose", action="store_true", + option_parser.add_option("-v", "--verbose", action="store_true", default=False, help="include debug-level logging") - option_parser.add_option("", "--adb-options", + option_parser.add_option("-a", "--adb-options", default=None, help="pass options to adb, such as -d -e, etc") - option_parser.add_option("", "--crash-file", + option_parser.add_option("-c", "--crash-file", default="reliability_crashed_sites.txt", help="the list of sites that cause browser to crash") - option_parser.add_option("", "--timeout-file", + option_parser.add_option("-f", "--timeout-file", default="reliability_timedout_sites.txt", - help="the list of sites that timedout during test.") + help="the list of sites that timedout during test") + option_parser.add_option("-d", "--delay", + default=0, + help="add a manual delay between pages (in ms)") + option_parser.add_option("-b", "--bugreport", + default=".", + help="the directory to store bugreport for crashes") + option_parser.add_option("-l", "--logtime", + default=None, + help="Logs page load time for each url to the file") opts, arguments = option_parser.parse_args() main(opts, arguments) diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java index 0218317dbcc6..e741177a90d9 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java @@ -23,7 +23,9 @@ import java.util.List; import java.util.Map; import java.io.File; +import android.app.AlertDialog; import android.app.ListActivity; +import android.content.DialogInterface; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; @@ -31,7 +33,7 @@ import android.widget.SimpleAdapter; import android.os.Bundle; -public abstract class FileList extends ListActivity +public abstract class FileList extends ListActivity { public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) @@ -39,7 +41,7 @@ public abstract class FileList extends ListActivity case KeyEvent.KEYCODE_DPAD_LEFT: if (mPath.length() > mBaseLength) { File f = new File(mPath); - mFocusFile = f.getName(); + mFocusFile = f.getName(); mFocusIndex = 0; f = f.getParentFile(); mPath = f.getPath(); @@ -47,7 +49,7 @@ public abstract class FileList extends ListActivity return true; } break; - + case KeyEvent.KEYCODE_DPAD_RIGHT: { Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition()); @@ -61,24 +63,24 @@ public abstract class FileList extends ListActivity } return true; } - + default: break; } return super.onKeyDown(keyCode, event); } - public void onCreate(Bundle icicle) + public void onCreate(Bundle icicle) { super.onCreate(icicle); setupPath(); updateList(); } - + protected List getData() { List myData = new ArrayList<HashMap>(); - + File f = new File(mPath); if (!f.exists()) { addItem(myData, "!LayoutTests path missing!", ""); @@ -103,10 +105,10 @@ public abstract class FileList extends ListActivity addItem(myData, files[i], path); } } - + return myData; } - + protected void addItem(List<Map> data, String name, String path) { HashMap temp = new HashMap(); @@ -114,34 +116,58 @@ public abstract class FileList extends ListActivity temp.put("path", path); data.add(temp); } - + protected void onListItemClick(ListView l, View v, int position, long id) { - Map map = (Map) l.getItemAtPosition(position); - String path = (String)map.get("path"); + Map map = (Map) l.getItemAtPosition(position); + final String path = (String)map.get("path"); if ((new File(path)).isDirectory()) { - mPath = path; - mFocusFile = null; - updateList(); + final CharSequence[] items = {"Open", "Run"}; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Select an Action"); + builder.setSingleChoiceItems(items, -1, + new DialogInterface.OnClickListener(){ + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case OPEN_DIRECTORY: + dialog.dismiss(); + mPath = path; + mFocusFile = null; + updateList(); + break; + case RUN_TESTS: + dialog.dismiss(); + processDirectory(path, false); + break; + } + } + }); + builder.create().show(); } else { processFile(path, false); } } - + + /* + * This function is called when the user has selected a directory in the + * list and wants to perform an action on it instead of navigating into + * the directory. + */ + abstract void processDirectory(String path, boolean selection); /* * This function is called when the user has selected a file in the * file list. The selected file could be a file or a directory. * The flag indicates if this was from a selection or not. */ abstract void processFile(String filename, boolean selection); - + /* * This function is called when the file list is being built. Return * true if the file is to be added to the file list. */ abstract boolean fileFilter(File f); - + protected void updateList() { setListAdapter(new SimpleAdapter(this, getData(), @@ -152,16 +178,19 @@ public abstract class FileList extends ListActivity setTitle(title); getListView().setSelection(mFocusIndex); } - - protected void setupPath() + + protected void setupPath() { mPath = "/sdcard/android/layout_tests"; mBaseLength = mPath.length(); } - + protected String mPath; protected int mBaseLength; protected String mFocusFile; protected int mFocusIndex; - + + private final static int OPEN_DIRECTORY = 0; + private final static int RUN_TESTS = 1; + } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java new file mode 100644 index 000000000000..cc2f1f5936a7 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java @@ -0,0 +1,80 @@ +package com.android.dumprendertree; + +import android.util.Log; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; + +public class FsUtils { + + private static final String LOGTAG = "FsUtils"; + private FsUtils() { + //no creation of instances + } + + public static void findLayoutTestsRecursively(BufferedOutputStream bos, + String dir) throws IOException { + Log.v(LOGTAG, "Searching tests under " + dir); + + File d = new File(dir); + if (!d.isDirectory()) { + throw new AssertionError("A directory expected, but got " + dir); + } + + String[] files = d.list(); + for (int i = 0; i < files.length; i++) { + String s = dir + "/" + files[i]; + if (FileFilter.ignoreTest(s)) { + Log.v(LOGTAG, " Ignoring: " + s); + continue; + } + if (s.toLowerCase().endsWith(".html") + || s.toLowerCase().endsWith(".xml")) { + bos.write(s.getBytes()); + bos.write('\n'); + continue; + } + + File f = new File(s); + if (f.isDirectory()) { + findLayoutTestsRecursively(bos, s); + continue; + } + + Log.v(LOGTAG, "Skipping " + s); + } + } + + public static void updateTestStatus(String statusFile, String s) { + try { + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(statusFile)); + bos.write(s.getBytes()); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Cannot update file " + statusFile); + } + } + + public static String readTestStatus(String statusFile) { + // read out the test name it stopped last time. + String status = null; + File testStatusFile = new File(statusFile); + if(testStatusFile.exists()) { + try { + BufferedReader inReader = new BufferedReader( + new FileReader(testStatusFile)); + status = inReader.readLine(); + inReader.close(); + } catch (IOException e) { + Log.e(LOGTAG, "Error reading test status.", e); + } + } + return status; + } + +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java index ebdc9c72305c..e00d3ad290b1 100755 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java @@ -16,14 +16,11 @@ package com.android.dumprendertree; -import junit.framework.TestSuite; -import com.android.dumprendertree.LayoutTestsAutoTest; - +import android.os.Bundle; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; -import android.util.Log; -import android.content.Intent; -import android.os.Bundle; + +import junit.framework.TestSuite; /** @@ -61,13 +58,27 @@ public class LayoutTestsAutoRunner extends InstrumentationTestRunner { } } + String delay_str = (String) icicle.get("delay"); + if(delay_str != null) { + try { + this.mDelay = Integer.parseInt(delay_str); + } catch (Exception e) { + } + } + String r = (String)icicle.get("rebaseline"); this.mRebaseline = (r != null && r.toLowerCase().equals("true")); super.onCreate(icicle); + + String logtime = (String) icicle.get("logtime"); + this.mLogtime = (logtime != null + && logtime.toLowerCase().equals("true")); } public String mTestPath = null; public int mTimeoutInMillis = 0; + public int mDelay = 0; public boolean mRebaseline = false; + public boolean mLogtime = false; } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index caef86191c0d..a03490d8f041 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -178,15 +178,13 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh private void resumeTestList() { // read out the test name it stoped last time. try { - BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE)); - String line = inReader.readLine(); + String line = FsUtils.readTestStatus(TEST_STATUS_FILE); for (int i = 0; i < mTestList.size(); i++) { if (mTestList.elementAt(i).equals(line)) { mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size())); break; } } - inReader.close(); } catch (Exception e) { Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE); } @@ -204,18 +202,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage()); } } - - private void updateTestStatus(String s) { - // Write TEST_STATUS_FILE - try { - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE)); - bos.write(s.getBytes()); - bos.close(); - } catch (Exception e) { - Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE); - } - } - + private String getResultFile(String test) { String shortName = test.substring(0, test.lastIndexOf('.')); // Write actual results to result directory. @@ -223,7 +210,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } private String getExpectedResultFile(String test) { - String shortName = test.substring(0, test.lastIndexOf('.')); + int pos = test.lastIndexOf('.'); + if(pos == -1) + return null; + String shortName = test.substring(0, pos); return shortName + "-expected.txt"; } @@ -303,6 +293,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh }); String resultFile = getResultFile(test); + if(resultFile == null) { + //simply ignore this test + return; + } if (mRebaselineResults) { String expectedResultFile = getExpectedResultFile(test); File f = new File(expectedResultFile); @@ -385,12 +379,12 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh // Run tests. for (int i = 0; i < mTestList.size(); i++) { String s = mTestList.elementAt(i); - updateTestStatus(s); + FsUtils.updateTestStatus(TEST_STATUS_FILE, s); // Run tests runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis); } - updateTestStatus("#DONE"); + FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE"); activity.finish(); } @@ -417,7 +411,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh try { File tests_list = new File(LAYOUT_TESTS_LIST_FILE); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false)); - findTestsRecursively(bos, getTestPath()); + FsUtils.findLayoutTestsRecursively(bos, getTestPath()); bos.flush(); bos.close(); } catch (Exception e) { @@ -425,38 +419,6 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } } - private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException { - Log.v(LOGTAG, "Searching tests under " + dir); - - File d = new File(dir); - if (!d.isDirectory()) { - throw new AssertionError("A directory expected, but got " + dir); - } - - String[] files = d.list(); - for (int i = 0; i < files.length; i++) { - String s = dir + "/" + files[i]; - if (FileFilter.ignoreTest(s)) { - Log.v(LOGTAG, " Ignoring: " + s); - continue; - } - if (s.toLowerCase().endsWith(".html") - || s.toLowerCase().endsWith(".xml")) { - bos.write(s.getBytes()); - bos.write('\n'); - continue; - } - - File f = new File(s); - if (f.isDirectory()) { - findTestsRecursively(bos, s); - continue; - } - - Log.v(LOGTAG, "Skipping " + s); - } - } - // Running all the layout tests at once sometimes // causes the dumprendertree to run out of memory. // So, additional tests are added to run the tests diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java index 81cf3a8edf64..cbcac6caa9ea 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java @@ -16,18 +16,15 @@ package com.android.dumprendertree; +import dalvik.system.VMRuntime; + import android.app.Instrumentation; import android.content.Intent; - -import android.util.Log; - import android.os.Bundle; import android.os.Debug; -import android.os.Debug.MemoryInfo; +import android.os.Process; import android.test.ActivityInstrumentationTestCase2; - -import com.android.dumprendertree.TestShellActivity; -import com.android.dumprendertree.TestShellCallback; +import android.util.Log; import java.io.FileOutputStream; import java.io.IOException; @@ -69,52 +66,85 @@ public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShel TestShellActivity activity = (TestShellActivity) getActivity(); + Log.v(LOGTAG, "About to run tests, calling gc first..."); + freeMem(); + // Run tests runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis); - // TODO(fqian): let am instrumentation pass in the command line, currently - // am instrument does not allow spaces in the command. dumpMemoryInfo(); // Kill activity activity.finish(); } + private void freeMem() { + Log.v(LOGTAG, "freeMem: calling gc/finalization..."); + final VMRuntime runtime = VMRuntime.getRuntime(); + + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); + + } + + private void printRow(PrintStream ps, String format, Object...objs) { + ps.println(String.format(format, objs)); + } + private void dumpMemoryInfo() { try { + freeMem(); Log.v(LOGTAG, "Dumping memory information."); FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true); PrintStream ps = new PrintStream(out); - MemoryInfo mi = new MemoryInfo(); - Debug.getMemoryInfo(mi); - - //try to fake the dumpsys format - //this will eventually be changed to XML - String format = "%15s:%9d%9d%9d%9d"; - String pss = - String.format(format, "(Pss)", - mi.nativePss, mi.dalvikPss, mi.otherPss, - mi.nativePss + mi.dalvikPss + mi.otherPss); - String sd = - String.format(format, "(shared dirty)", - mi.nativeSharedDirty, mi.dalvikSharedDirty, mi.otherSharedDirty, - mi.nativeSharedDirty + mi.dalvikSharedDirty + mi.otherSharedDirty); - String pd = - String.format(format, "(priv dirty)", - mi.nativePrivateDirty, mi.dalvikPrivateDirty, mi.otherPrivateDirty, - mi.nativePrivateDirty + mi.dalvikPrivateDirty + mi.otherPrivateDirty); - ps.print("\n\n\n"); - ps.println("** MEMINFO in pid 0 [com.android.dumprendertree] **"); - ps.println(" native dalvik other total"); - ps.println(" size: 12060 5255 N/A 17315"); - ps.println(" allocated: 12060 5255 N/A 17315"); - ps.println(" free: 12060 5255 N/A 17315"); - ps.println(pss); - ps.println(sd); - ps.println(pd); + ps.println("** MEMINFO in pid " + Process.myPid() + + " [com.android.dumprendertree] **"); + String formatString = "%17s %8s %8s %8s %8s"; + + long nativeMax = Debug.getNativeHeapSize() / 1024; + long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; + long nativeFree = Debug.getNativeHeapFreeSize() / 1024; + Runtime runtime = Runtime.getRuntime(); + long dalvikMax = runtime.totalMemory() / 1024; + long dalvikFree = runtime.freeMemory() / 1024; + long dalvikAllocated = dalvikMax - dalvikFree; + + + Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); + Debug.getMemoryInfo(memInfo); + + final int nativeShared = memInfo.nativeSharedDirty; + final int dalvikShared = memInfo.dalvikSharedDirty; + final int otherShared = memInfo.otherSharedDirty; + + final int nativePrivate = memInfo.nativePrivateDirty; + final int dalvikPrivate = memInfo.dalvikPrivateDirty; + final int otherPrivate = memInfo.otherPrivateDirty; + + printRow(ps, formatString, "", "native", "dalvik", "other", "total"); + printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); + printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A", + nativeAllocated + dalvikAllocated); + printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A", + nativeFree + dalvikFree); + + printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss, + memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); + + printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared, + nativeShared + dalvikShared + otherShared); + printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate, + nativePrivate + dalvikPrivate + otherPrivate); ps.print("\n\n\n"); ps.flush(); ps.close(); @@ -134,7 +164,7 @@ public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShel LoadTestsAutoTest.this.notifyAll(); } } - + public void timedOut(String url) { } }); diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java index 00e0f8953757..e15ab6587fac 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java @@ -17,19 +17,23 @@ package com.android.dumprendertree; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.util.Log; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; public class Menu extends FileList { - - public void onCreate(Bundle icicle) - { + + private static final int MENU_START = 0x01; + private static String LOGTAG = "MenuActivity"; + static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt"; + + public void onCreate(Bundle icicle) { super.onCreate(icicle); } - + boolean fileFilter(File f) { if (f.getName().startsWith(".")) return false; @@ -41,14 +45,36 @@ public class Menu extends FileList { return true; return false; } - - void processFile(String filename, boolean selection) - { + + void processFile(String filename, boolean selection) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClass(this, TestShellActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename); startActivity(intent); } + + @Override + void processDirectory(String path, boolean selection) { + generateTestList(path); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setClass(this, TestShellActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE); + startActivity(intent); + } + + private void generateTestList(String path) { + try { + File tests_list = new File(LAYOUT_TESTS_LIST_FILE); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false)); + FsUtils.findLayoutTestsRecursively(bos, path); + bos.flush(); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Error when creating test list: " + e.getMessage()); + } + } + } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java new file mode 100644 index 000000000000..de39800149a3 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java @@ -0,0 +1,166 @@ +package com.android.dumprendertree; + +import android.os.Handler; +import android.os.Message; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ReliabilityTest extends ActivityInstrumentationTestCase2<ReliabilityTestActivity> { + + private static final String LOGTAG = "ReliabilityTest"; + private static final String PKG_NAME = "com.android.dumprendertree"; + private static final String TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt"; + private static final String TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt"; + private static final String TEST_TIMEOUT_FILE = "/sdcard/android/reliability_timeout_test.txt"; + private static final String TEST_LOAD_TIME_FILE = "/sdcard/android/reliability_load_time.txt"; + private static final String TEST_DONE = "#DONE"; + static final String RELIABILITY_TEST_RUNNER_FILES[] = { + "run_reliability_tests.py" + }; + + public ReliabilityTest() { + super(PKG_NAME, ReliabilityTestActivity.class); + } + + public void runReliabilityTest() throws Throwable { + ReliabilityTestActivity activity = getActivity(); + LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner)getInstrumentation(); + + File testListFile = new File(TEST_LIST_FILE); + if(!testListFile.exists()) + throw new FileNotFoundException("test list file not found."); + + BufferedReader listReader = new BufferedReader( + new FileReader(testListFile)); + + //always try to resume first, hence cleaning up status will be the + //responsibility of driver scripts + String lastUrl = FsUtils.readTestStatus(TEST_STATUS_FILE); + if(lastUrl != null && !TEST_DONE.equals(lastUrl)) + fastForward(listReader, lastUrl); + + String url = null; + Handler handler = null; + boolean timeoutFlag = false; + long start, elapsed; + + //read from BufferedReader instead of populating a list in advance, + //this will avoid excessive memory usage in case of a large list + while((url = listReader.readLine()) != null) { + url = url.trim(); + if(url.length() == 0) + continue; + start = System.currentTimeMillis(); + Log.v(LOGTAG, "Testing URL: " + url); + FsUtils.updateTestStatus(TEST_STATUS_FILE, url); + activity.reset(); + //use message to send new URL to avoid interacting with + //WebView in non-UI thread + handler = activity.getHandler(); + Message msg = handler.obtainMessage( + ReliabilityTestActivity.MSG_NAVIGATE, + runner.mTimeoutInMillis, runner.mDelay); + msg.getData().putString(ReliabilityTestActivity.MSG_NAV_URL, url); + msg.getData().putBoolean(ReliabilityTestActivity.MSG_NAV_LOGTIME, + runner.mLogtime); + handler.sendMessage(msg); + timeoutFlag = activity.waitUntilDone(); + elapsed = System.currentTimeMillis() - start; + if(elapsed < 1000) { + Log.w(LOGTAG, "Page load finished in " + elapsed + + "ms, too soon?"); + } else { + Log.v(LOGTAG, "Page load finished in " + elapsed + "ms"); + } + if(timeoutFlag) { + writeTimeoutFile(url); + } + if(runner.mLogtime) { + writeLoadTime(url, activity.getPageLoadTime()); + } + System.runFinalization(); + System.gc(); + System.gc(); + } + FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE); + activity.finish(); + listReader.close(); + } + + public void copyRunnerAssetsToCache() { + try { + String out_dir = getActivity().getApplicationContext() + .getCacheDir().getPath() + "/"; + + for( int i=0; i< RELIABILITY_TEST_RUNNER_FILES.length; i++) { + InputStream in = getActivity().getAssets().open( + RELIABILITY_TEST_RUNNER_FILES[i]); + OutputStream out = new FileOutputStream( + out_dir + RELIABILITY_TEST_RUNNER_FILES[i]); + + byte[] buf = new byte[2048]; + int len; + + while ((len = in.read(buf)) >= 0 ) { + out.write(buf, 0, len); + } + out.close(); + in.close(); + } + }catch (IOException e) { + Log.e(LOGTAG, "Cannot extract scripts for testing.", e); + } + } + + private void fastForward(BufferedReader testListReader, String lastUrl) { + //fastforward the BufferedReader to the position right after last url + if(lastUrl == null) + return; + + String line = null; + try { + while((line = testListReader.readLine()) != null) { + if(lastUrl.equals(line)) + return; + } + } catch (IOException ioe) { + Log.e(LOGTAG, "Error while reading test list.", ioe); + return; + } + } + + private void writeTimeoutFile(String s) { + //append to the file containing the list of timeout urls + try { + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(TEST_TIMEOUT_FILE, true)); + bos.write(s.getBytes()); + bos.write('\n'); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Cannot update file " + TEST_TIMEOUT_FILE, e); + } + } + + private void writeLoadTime(String s, long time) { + //append to the file containing the list of timeout urls + try { + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(TEST_LOAD_TIME_FILE, true)); + bos.write((s + '|' + time + '\n').getBytes()); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Cannot update file " + TEST_LOAD_TIME_FILE, e); + } + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java new file mode 100644 index 000000000000..5ddd0b379b81 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java @@ -0,0 +1,290 @@ +package com.android.dumprendertree; + +import android.app.Activity; +import android.app.ActivityThread; +import android.graphics.Bitmap; +import android.net.http.SslError; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.ViewGroup; +import android.webkit.HttpAuthHandler; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.webkit.WebSettings.LayoutAlgorithm; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +public class ReliabilityTestActivity extends Activity { + + public static final String TEST_URL_ACTION = "com.andrdoid.dumprendertree.TestUrlAction"; + public static final String PARAM_URL = "URL"; + public static final String PARAM_TIMEOUT = "Timeout"; + public static final int RESULT_TIMEOUT = 0xDEAD; + public static final int MSG_TIMEOUT = 0xC001; + public static final int MSG_NAVIGATE = 0xC002; + public static final String MSG_NAV_URL = "url"; + public static final String MSG_NAV_LOGTIME = "logtime"; + + private static final String LOGTAG = "ReliabilityTestActivity"; + + private WebView webView; + private SimpleWebViewClient webViewClient; + private SimpleChromeClient chromeClient; + private Handler handler; + private boolean timeoutFlag; + private boolean logTime; + private boolean pageDone; + private Object pageDoneLock; + private int pageStartCount; + private int manualDelay; + private long startTime; + private long pageLoadTime; + private PageDoneRunner pageDoneRunner = new PageDoneRunner(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Log.v(LOGTAG, "onCreate, inst=" + Integer.toHexString(hashCode())); + + LinearLayout contentView = new LinearLayout(this); + contentView.setOrientation(LinearLayout.VERTICAL); + setContentView(contentView); + setTitle("Idle"); + + webView = new WebView(this); + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false); + webView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NORMAL); + + webViewClient = new SimpleWebViewClient(); + chromeClient = new SimpleChromeClient(); + webView.setWebViewClient(webViewClient); + webView.setWebChromeClient(chromeClient); + + contentView.addView(webView, new LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT, 0.0f)); + + handler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TIMEOUT: + handleTimeout(); + return; + case MSG_NAVIGATE: + manualDelay = msg.arg2; + navigate(msg.getData().getString(MSG_NAV_URL), msg.arg1); + logTime = msg.getData().getBoolean(MSG_NAV_LOGTIME); + return; + } + } + }; + + pageDoneLock = new Object(); + } + + public void reset() { + synchronized (pageDoneLock) { + pageDone = false; + } + timeoutFlag = false; + pageStartCount = 0; + chromeClient.resetJsTimeout(); + } + + private void navigate(String url, int timeout) { + if(url == null) { + Log.v(LOGTAG, "URL is null, cancelling..."); + finish(); + } + webView.stopLoading(); + if(logTime) { + webView.clearCache(true); + } + startTime = System.currentTimeMillis(); + Log.v(LOGTAG, "Navigating to URL: " + url); + webView.loadUrl(url); + + if(timeout != 0) { + //set a timer with specified timeout (in ms) + handler.sendMessageDelayed(handler.obtainMessage(MSG_TIMEOUT), + timeout); + } + } + + @Override + protected void onDestroy() { + Log.v(LOGTAG, "onDestroy, inst=" + Integer.toHexString(hashCode())); + super.onDestroy(); + } + + private boolean isPageDone() { + synchronized (pageDoneLock) { + return pageDone; + } + } + + private void setPageDone(boolean pageDone) { + synchronized (pageDoneLock) { + this.pageDone = pageDone; + pageDoneLock.notifyAll(); + } + } + + private void handleTimeout() { + int progress = webView.getProgress(); + webView.stopLoading(); + Log.v(LOGTAG, "Page timeout triggered, progress = " + progress); + timeoutFlag = true; + handler.postDelayed(pageDoneRunner, manualDelay); + } + + public boolean waitUntilDone() { + validateNotAppThread(); + synchronized (pageDoneLock) { + while(!isPageDone()) { + try { + pageDoneLock.wait(); + } catch (InterruptedException ie) { + //no-op + } + } + } + return timeoutFlag; + } + + public Handler getHandler() { + return handler; + } + + private final void validateNotAppThread() { + if (ActivityThread.currentActivityThread() != null) { + throw new RuntimeException( + "This method can not be called from the main application thread"); + } + } + + public long getPageLoadTime() { + return pageLoadTime; + } + + class SimpleWebViewClient extends WebViewClient { + + @Override + public void onReceivedError(WebView view, int errorCode, String description, + String failingUrl) { + Log.v(LOGTAG, "Received WebCore error: code=" + errorCode + + ", description=" + description + + ", url=" + failingUrl); + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + //ignore certificate error + Log.v(LOGTAG, "Received SSL error: " + error.toString()); + handler.proceed(); + } + + @Override + public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, + String realm) { + // cancel http auth request + handler.cancel(); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + pageStartCount++; + Log.v(LOGTAG, "onPageStarted: " + url); + } + + @Override + public void onPageFinished(WebView view, String url) { + Log.v(LOGTAG, "onPageFinished: " + url); + // let handleTimeout take care of finishing the page + if(!timeoutFlag) + handler.postDelayed(new WebViewStatusChecker(), 500); + } + } + + class SimpleChromeClient extends WebChromeClient { + + private int timeoutCounter = 0; + + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + result.confirm(); + return true; + } + + @Override + public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { + result.confirm(); + return true; + } + + @Override + public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { + result.confirm(); + return true; + } + + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, + JsPromptResult result) { + result.confirm(); + return true; + } + + @Override + public boolean onJsTimeout() { + timeoutCounter++; + Log.v(LOGTAG, "JavaScript timeout, count=" + timeoutCounter); + return timeoutCounter > 2; + } + + public void resetJsTimeout() { + timeoutCounter = 0; + } + + @Override + public void onReceivedTitle(WebView view, String title) { + ReliabilityTestActivity.this.setTitle(title); + } + } + + class WebViewStatusChecker implements Runnable { + + private int initialStartCount; + + public WebViewStatusChecker() { + initialStartCount = pageStartCount; + } + + public void run() { + if (initialStartCount == pageStartCount) { + //perform cleanup + handler.removeMessages(MSG_TIMEOUT); + webView.stopLoading(); + handler.postDelayed(pageDoneRunner, manualDelay); + } + } + } + + class PageDoneRunner implements Runnable { + + public void run() { + Log.v(LOGTAG, "Finishing URL: " + webView.getUrl()); + pageLoadTime = System.currentTimeMillis() - startTime; + setPageDone(true); + } + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestsAutoTest.java deleted file mode 100644 index 347efde3f07f..000000000000 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestsAutoTest.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.android.dumprendertree; - -import com.android.dumprendertree.TestShellActivity.DumpDataType; - -import android.content.Intent; -import android.test.ActivityInstrumentationTestCase2; -import android.util.Log; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Vector; - -public class ReliabilityTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> { - - private static final String LOGTAG = "ReliabilityTests"; - private static final String TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt"; - private static final String TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt"; - private static final String TEST_TIMEOUT_FILE = "/sdcard/android/reliability_timeout_test.txt"; - static final String RELIABILITY_TEST_RUNNER_FILES[] = { - "run_reliability_tests.py" - }; - - private boolean finished; - private List<String> testList; - - public ReliabilityTestsAutoTest() { - super("com.android.dumprendertree", TestShellActivity.class); - } - - private void getTestList() { - // Read test list. - testList = new Vector<String>(); - try { - BufferedReader inReader = new BufferedReader(new FileReader(TEST_LIST_FILE)); - String line; - while ((line = inReader.readLine()) != null) { - testList.add(line); - } - inReader.close(); - Log.v(LOGTAG, "Test list has " + testList.size() + " test(s)."); - } catch (Exception e) { - Log.e(LOGTAG, "Error while reading test list : " + e.getMessage()); - } - } - - private void resumeTestList() { - // read out the test name it stopped last time. - try { - BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE)); - String line = inReader.readLine(); - for (int i = 0; i < testList.size(); i++) { - if (testList.get(i).equals(line)) { - testList = new Vector<String>(testList.subList(i+1, testList.size())); - break; - } - } - inReader.close(); - } catch (Exception e) { - Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE); - } - } - - private void clearTestStatus() { - // Delete TEST_STATUS_FILE - try { - File f = new File(TEST_STATUS_FILE); - if (f.delete()) - Log.v(LOGTAG, "Deleted " + TEST_STATUS_FILE); - else - Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE); - } catch (Exception e) { - Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage()); - } - } - - private void clearTestTimeout() { - // Delete TEST_TIMEOUT_FILE - try { - File f = new File(TEST_TIMEOUT_FILE); - if (f.delete()) - Log.v(LOGTAG, "Deleted " + TEST_TIMEOUT_FILE); - else - Log.e(LOGTAG, "Fail to delete " + TEST_TIMEOUT_FILE); - } catch (Exception e) { - Log.e(LOGTAG, "Fail to delete " + TEST_TIMEOUT_FILE + " : " + e.getMessage()); - } - } - - private void updateTestStatus(String s) { - // Write TEST_STATUS_FILE - try { - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE)); - bos.write(s.getBytes()); - bos.close(); - } catch (Exception e) { - Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE); - } - } - - private void writeTimeoutFile(String s) { - // Write TEST_TIMEOUT_FILE - try { - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_TIMEOUT_FILE, true)); - bos.write(s.getBytes()); - bos.write('\n'); - bos.close(); - } catch (Exception e) { - Log.e(LOGTAG, "Cannot update file " + TEST_TIMEOUT_FILE); - } - } - - private void runReliabilityTest(boolean resume) { - LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); - - getTestList(); - if(!resume) - clearTestStatus(); - else - resumeTestList(); - - TestShellActivity activity = getActivity(); - activity.setDefaultDumpDataType(DumpDataType.NO_OP); - // Run tests. - for (int i = 0; i < testList.size(); i++) { - String s = testList.get(i); - updateTestStatus(s); - // Run tests - runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis); - } - - updateTestStatus("#DONE"); - - activity.finish(); - } - - private void runTestAndWaitUntilDone(TestShellActivity activity, String url, int timeout) { - activity.setCallback(new TestShellCallback() { - public void finished() { - synchronized (ReliabilityTestsAutoTest.this) { - finished = true; - ReliabilityTestsAutoTest.this.notifyAll(); - } - } - - public void timedOut(String url) { - writeTimeoutFile(url); - } - }); - - finished = false; - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClass(activity, TestShellActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(TestShellActivity.TEST_URL, url); - intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout); - activity.startActivity(intent); - - // Wait until done. - synchronized (this) { - while(!finished){ - try { - this.wait(); - } catch (InterruptedException e) { } - } - } - } - - public void startReliabilityTests() { - clearTestTimeout(); - runReliabilityTest(false); - } - - public void resumeReliabilityTests() { - runReliabilityTest(true); - } - - public void copyRunnerAssetsToCache() { - try { - String out_dir = getActivity().getApplicationContext() - .getCacheDir().getPath() + "/"; - - for( int i=0; i< RELIABILITY_TEST_RUNNER_FILES.length; i++) { - InputStream in = getActivity().getAssets().open( - RELIABILITY_TEST_RUNNER_FILES[i]); - OutputStream out = new FileOutputStream( - out_dir + RELIABILITY_TEST_RUNNER_FILES[i]); - - byte[] buf = new byte[2048]; - int len; - - while ((len = in.read(buf)) >= 0 ) { - out.write(buf, 0, len); - } - out.close(); - in.close(); - } - }catch (IOException e) { - e.printStackTrace(); - } - - } -} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 1ba291c9c134..0d22eca99ecd 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -17,7 +17,10 @@ package com.android.dumprendertree; import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; +import android.content.DialogInterface.OnClickListener; import android.graphics.Bitmap; import android.net.http.SslError; import android.os.Bundle; @@ -35,21 +38,24 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.util.Vector; public class TestShellActivity extends Activity implements LayoutTestController { - + static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP} - + public class AsyncHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == MSG_TIMEOUT) { mTimedOut = true; - mCallback.timedOut(mWebView.getUrl()); + if(mCallback != null) + mCallback.timedOut(mWebView.getUrl()); requestWebKitData(); return; } else if (msg.what == MSG_WEBKIT_DATA) { @@ -63,10 +69,10 @@ public class TestShellActivity extends Activity implements LayoutTestController public void requestWebKitData() { Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); - + if (mRequestedWebKitData) throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); - + mRequestedWebKitData = true; switch (mDumpDataType) { case DUMP_AS_TEXT: @@ -79,12 +85,12 @@ public class TestShellActivity extends Activity implements LayoutTestController finished(); break; } - } + } @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - + LinearLayout contentView = new LinearLayout(this); contentView.setOrientation(LinearLayout.VERTICAL); setContentView(contentView); @@ -133,59 +139,122 @@ public class TestShellActivity extends Activity implements LayoutTestController mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f)); - + mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); - + mHandler = new AsyncHandler(); - + Intent intent = getIntent(); if (intent != null) { executeIntent(intent); } } - + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); executeIntent(intent); } - + private void executeIntent(Intent intent) { resetTestStatus(); if (!Intent.ACTION_VIEW.equals(intent.getAction())) { return; } - + mTestUrl = intent.getStringExtra(TEST_URL); - if (mTestUrl == null) + if (mTestUrl == null) { + mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST); + if(mUiAutoTestPath != null) { + beginUiAutoTest(); + } return; - + } + mResultFile = intent.getStringExtra(RESULT_FILE); mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0); Log.v(LOGTAG, " Loading " + mTestUrl); mWebView.loadUrl(mTestUrl); - + if (mTimeoutInMillis > 0) { // Create a timeout timer Message m = mHandler.obtainMessage(MSG_TIMEOUT); mHandler.sendMessageDelayed(m, mTimeoutInMillis); } } - + + private void beginUiAutoTest() { + try { + mTestListReader = new BufferedReader( + new FileReader(mUiAutoTestPath)); + } catch (IOException ioe) { + Log.e(LOGTAG, "Failed to open test list for read.", ioe); + finishUiAutoTest(); + return; + } + moveToNextTest(); + } + + private void finishUiAutoTest() { + try { + if(mTestListReader != null) + mTestListReader.close(); + } catch (IOException ioe) { + Log.w(LOGTAG, "Failed to close test list file.", ioe); + } + finished(); + } + + private void moveToNextTest() { + String url = null; + try { + url = mTestListReader.readLine(); + } catch (IOException ioe) { + Log.e(LOGTAG, "Failed to read next test.", ioe); + finishUiAutoTest(); + return; + } + if (url == null) { + mUiAutoTestPath = null; + finishUiAutoTest(); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("All tests finished. Exit?") + .setCancelable(false) + .setPositiveButton("Yes", new OnClickListener(){ + public void onClick(DialogInterface dialog, int which) { + TestShellActivity.this.finish(); + } + }) + .setNegativeButton("No", new OnClickListener(){ + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + builder.create().show(); + return; + } + url = "file://" + url; + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(TestShellActivity.TEST_URL, url); + intent.putExtra(TIMEOUT_IN_MILLIS, 10000); + executeIntent(intent); + } + @Override protected void onStop() { super.onStop(); mWebView.stopLoading(); } - + @Override protected void onDestroy() { super.onDestroy(); mWebView.destroy(); mWebView = null; } - + @Override public void onLowMemory() { super.onLowMemory(); @@ -199,13 +268,13 @@ public class TestShellActivity extends Activity implements LayoutTestController finished(); return; } - + try { File parentDir = new File(mResultFile).getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } - + FileOutputStream os = new FileOutputStream(mResultFile); if (timeout) { Log.w("Layout test: Timeout", mResultFile); @@ -222,22 +291,27 @@ public class TestShellActivity extends Activity implements LayoutTestController os.flush(); os.close(); } catch (IOException ex) { - Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); + Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); } finished(); } - + public void setCallback(TestShellCallback callback) { mCallback = callback; } - + public void finished() { - if (mCallback != null) { - mCallback.finished(); + if (mUiAutoTestPath != null) { + //don't really finish here + moveToNextTest(); + } else { + if (mCallback != null) { + mCallback.finished(); + } } } - + public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { mDefaultDumpDataType = defaultDumpDataType; } @@ -257,7 +331,7 @@ public class TestShellActivity extends Activity implements LayoutTestController String url = mWebView.getUrl(); Log.v(LOGTAG, "waitUntilDone called: " + url); } - + public void notifyDone() { String url = mWebView.getUrl(); Log.v(LOGTAG, "notifyDone called: " + url); @@ -266,7 +340,7 @@ public class TestShellActivity extends Activity implements LayoutTestController mChromeClient.onProgressChanged(mWebView, 100); } } - + public void display() { mWebView.invalidate(); } @@ -332,7 +406,7 @@ public class TestShellActivity extends Activity implements LayoutTestController } public void queueScript(String scriptToRunInCurrentContext) { - mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); + mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); } public void repaintSweepHorizontally() { @@ -359,7 +433,7 @@ public class TestShellActivity extends Activity implements LayoutTestController public void testRepaint() { mWebView.invalidate(); } - + private final WebChromeClient mChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { @@ -406,7 +480,7 @@ public class TestShellActivity extends Activity implements LayoutTestController result.confirm(); return true; } - + @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { @@ -419,7 +493,7 @@ public class TestShellActivity extends Activity implements LayoutTestController result.confirm(); return true; } - + @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { @@ -435,7 +509,7 @@ public class TestShellActivity extends Activity implements LayoutTestController return true; } }; - + private void resetTestStatus() { mWaitUntilDone = false; mDumpDataType = mDefaultDumpDataType; @@ -444,17 +518,19 @@ public class TestShellActivity extends Activity implements LayoutTestController mRequestedWebKitData = false; mEventSender.resetMouse(); } - + private WebView mWebView; private WebViewEventSender mEventSender; private AsyncHandler mHandler; private TestShellCallback mCallback; private CallbackProxy mCallbackProxy; - + private String mTestUrl; private String mResultFile; private int mTimeoutInMillis; + private String mUiAutoTestPath; + private BufferedReader mTestListReader; // States private boolean mTimedOut; @@ -472,13 +548,14 @@ public class TestShellActivity extends Activity implements LayoutTestController private Vector mWebHistory; static final String TIMEOUT_STR = "**Test timeout"; - + static final int MSG_TIMEOUT = 0; static final int MSG_WEBKIT_DATA = 1; static final String LOGTAG="TestShell"; - + static final String TEST_URL = "TestUrl"; static final String RESULT_FILE = "ResultFile"; static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; + static final String UI_AUTO_TEST = "UiAutoTest"; } diff --git a/tests/FrameworkTest/AndroidManifest.xml b/tests/FrameworkTest/AndroidManifest.xml index c70302be798f..4e4ebffc7d37 100644 --- a/tests/FrameworkTest/AndroidManifest.xml +++ b/tests/FrameworkTest/AndroidManifest.xml @@ -20,6 +20,9 @@ <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.HARDWARE_TEST" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_VIEW_TYPES" /> + <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" /> + <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" /> <application android:theme="@style/Theme"> <uses-library android:name="android.test.runner" /> @@ -939,7 +942,7 @@ </intent-filter> </activity> - <activity android:name="android.widget.AutoCompleteTextViewSimple" + <activity android:name="android.widget.AutoCompleteTextViewSimple" android:label="AutoCompleteTextViewSimple"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -947,6 +950,12 @@ </intent-filter> </activity> + <service android:name=".accessibility.AccessibilityTestService"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService" /> + </intent-filter> + </service> + </application> </manifest> diff --git a/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java b/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java new file mode 100644 index 000000000000..83d6056df9d6 --- /dev/null +++ b/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java @@ -0,0 +1,167 @@ +/* + * 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.frameworktest.accessibility; + +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Notification; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * This class text the accessibility framework end to end. + * <p> + * Note: Since accessibility is provided by {@link AccessibilityService}s we create one, + * and it generates an event and an interruption dispatching them through the + * {@link AccessibilityManager}. We verify the received result. To trigger the test + * go to Settings->Accessibility and select the enable accessibility check and then + * select the check for this service (same name as the class). + */ +public class AccessibilityTestService extends AccessibilityService { + + private static final String LOG_TAG = "AccessibilityTestService"; + + private static final String CLASS_NAME = "foo.bar.baz.Test"; + private static final String PACKAGE_NAME = "foo.bar.baz"; + private static final String TEXT = "Some stuff"; + private static final String BEFORE_TEXT = "Some other stuff"; + + private static final String CONTENT_DESCRIPTION = "Content description"; + + private static final int ITEM_COUNT = 10; + private static final int CURRENT_ITEM_INDEX = 1; + private static final int INTERRUPT_INVOCATION_TYPE = 0x00000200; + + private static final int FROM_INDEX = 1; + private static final int ADDED_COUNT = 2; + private static final int REMOVED_COUNT = 1; + + private static final int NOTIFICATION_TIMEOUT_MILLIS = 80; + + private int mReceivedResult; + + private Timer mTimer = new Timer(); + + @Override + public void onServiceConnected() { + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + info.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE; + info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS; + info.flags &= AccessibilityServiceInfo.DEFAULT; + setServiceInfo(info); + + // we need to wait until the system picks our configuration + // otherwise it will not notify us + mTimer.schedule(new TimerTask() { + @Override + public void run() { + try { + testAccessibilityEventDispatching(); + testInterrupt(); + } catch (Exception e) { + Log.e(LOG_TAG, "Error in testing Accessibility feature", e); + } + } + }, 1000); + } + + /** + * Check here if the event we received is actually the one we sent. + */ + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + assert(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType()); + assert(event != null); + assert(event.getEventTime() > 0); + assert(CLASS_NAME.equals(event.getClassName())); + assert(PACKAGE_NAME.equals(event.getPackageName())); + assert(1 == event.getText().size()); + assert(TEXT.equals(event.getText().get(0))); + assert(BEFORE_TEXT.equals(event.getBeforeText())); + assert(event.isChecked()); + assert(CONTENT_DESCRIPTION.equals(event.getContentDescription())); + assert(ITEM_COUNT == event.getItemCount()); + assert(CURRENT_ITEM_INDEX == event.getCurrentItemIndex()); + assert(event.isEnabled()); + assert(event.isPassword()); + assert(FROM_INDEX == event.getFromIndex()); + assert(ADDED_COUNT == event.getAddedCount()); + assert(REMOVED_COUNT == event.getRemovedCount()); + assert(event.getParcelableData() != null); + assert(1 == ((Notification) event.getParcelableData()).icon); + + // set the type of the receved request + mReceivedResult = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED; + } + + /** + * Set a flag that we received the interrupt request. + */ + @Override + public void onInterrupt() { + + // set the type of the receved request + mReceivedResult = INTERRUPT_INVOCATION_TYPE; + } + + /** + * If an {@link AccessibilityEvent} is sent and received correctly. + */ + public void testAccessibilityEventDispatching() throws Exception { + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + + assert(event != null); + event.setClassName(CLASS_NAME); + event.setPackageName(PACKAGE_NAME); + event.getText().add(TEXT); + event.setBeforeText(BEFORE_TEXT); + event.setChecked(true); + event.setContentDescription(CONTENT_DESCRIPTION); + event.setItemCount(ITEM_COUNT); + event.setCurrentItemIndex(CURRENT_ITEM_INDEX); + event.setEnabled(true); + event.setPassword(true); + event.setFromIndex(FROM_INDEX); + event.setAddedCount(ADDED_COUNT); + event.setRemovedCount(REMOVED_COUNT); + event.setParcelableData(new Notification(1, "Foo", 1234)); + + AccessibilityManager.getInstance(this).sendAccessibilityEvent(event); + + assert(mReceivedResult == event.getEventType()); + + Log.i(LOG_TAG, "AccessibilityTestService#testAccessibilityEventDispatching: Success"); + } + + /** + * If accessibility feedback interruption is triggered and received correctly. + */ + public void testInterrupt() throws Exception { + AccessibilityManager.getInstance(this).interrupt(); + + assert(INTERRUPT_INVOCATION_TYPE == mReceivedResult); + + Log.i(LOG_TAG, "AccessibilityTestService#testInterrupt: Success"); + } +} + diff --git a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java index 6f89fce71dd8..5ae960a2d231 100644 --- a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java +++ b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java @@ -18,6 +18,7 @@ package android.widget; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase2; +import android.test.FlakyTest; import android.test.suitebuilder.annotation.MediumTest; /** @@ -149,6 +150,7 @@ public class AutoCompleteTextViewPopup } /** Test the show/hide behavior of the drop-down. */ + @FlakyTest(tolerance=5) @MediumTest public void testPopupShow() throws Throwable { AutoCompleteTextViewSimple theActivity = getActivity(); diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java new file mode 100644 index 000000000000..d6380f90f89c --- /dev/null +++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java @@ -0,0 +1,81 @@ +/** + * 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.frameworktest.accessibility; + +import android.test.suitebuilder.annotation.MediumTest; +import android.view.accessibility.AccessibilityEvent; + +import junit.framework.TestCase; + +/** + * This class exercises the caching and recycling of {@link AccessibilityEvent}s. + */ +public class RecycleAccessibilityEventTest extends TestCase { + + private static final String CLASS_NAME = "foo.bar.baz.Test"; + private static final String PACKAGE_NAME = "foo.bar.baz"; + private static final String TEXT = "Some stuff"; + + private static final String CONTENT_DESCRIPTION = "Content description"; + private static final int ITEM_COUNT = 10; + private static final int CURRENT_ITEM_INDEX = 1; + + private static final int FROM_INDEX = 1; + private static final int ADDED_COUNT = 2; + private static final int REMOVED_COUNT = 1; + + /** + * If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly + */ + @MediumTest + public void testAccessibilityEventViewTextChangedType() { + AccessibilityEvent first = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + assertNotNull(first); + + first.setClassName(CLASS_NAME); + first.setPackageName(PACKAGE_NAME); + first.getText().add(TEXT); + first.setFromIndex(FROM_INDEX); + first.setAddedCount(ADDED_COUNT); + first.setRemovedCount(REMOVED_COUNT); + first.setChecked(true); + first.setContentDescription(CONTENT_DESCRIPTION); + first.setItemCount(ITEM_COUNT); + first.setCurrentItemIndex(CURRENT_ITEM_INDEX); + first.setEnabled(true); + first.setPassword(true); + + first.recycle(); + + assertNotNull(first); + assertNull(first.getClassName()); + assertNull(first.getPackageName()); + assertEquals(0, first.getText().size()); + assertFalse(first.isChecked()); + assertNull(first.getContentDescription()); + assertEquals(0, first.getItemCount()); + assertEquals(AccessibilityEvent.INVALID_POSITION, first.getCurrentItemIndex()); + assertFalse(first.isEnabled()); + assertFalse(first.isPassword()); + assertEquals(0, first.getFromIndex()); + assertEquals(0, first.getAddedCount()); + assertEquals(0, first.getRemovedCount()); + + // get another event from the pool (this must be the recycled first) + AccessibilityEvent second = AccessibilityEvent.obtain(); + assertEquals(first, second); + } +} diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk index 2e3385f8b569..0813c35efed5 100644 --- a/tests/backup/Android.mk +++ b/tests/backup/Android.mk @@ -21,7 +21,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ backup_helper_test.cpp -LOCAL_MODULE_TAGS := user +LOCAL_MODULE_TAGS := optional LOCAL_MODULE := backup_helper_test LOCAL_SHARED_LIBRARIES := libutils @@ -31,7 +31,7 @@ include $(BUILD_EXECUTABLE) # ======================================== include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := user +LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml index eaeb5b72da94..3778742e105e 100644 --- a/tests/backup/AndroidManifest.xml +++ b/tests/backup/AndroidManifest.xml @@ -1,6 +1,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.backuptest"> - <application> + <application android:backupAgent="BackupTestAgent"> <activity android:name="BackupTestActivity" android:label="_BackupTest"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -8,10 +8,5 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <service android:name="BackupTestService"> - <intent-filter> - <action android:name="android.backup.BackupService.SERVICE" /> - </intent-filter> - </service> </application> </manifest> diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp index 6da16b4de4c9..04358ad46984 100644 --- a/tests/backup/backup_helper_test.cpp +++ b/tests/backup/backup_helper_test.cpp @@ -1,8 +1,26 @@ -#include <utils/backup_helpers.h> +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/BackupHelpers.h> #include <stdio.h> #include <string.h> +using namespace android; + #if TEST_BACKUP_HELPERS // ============================================================ @@ -20,6 +38,10 @@ Test TESTS[] = { { "backup_helper_test_empty", backup_helper_test_empty, 0, false }, { "backup_helper_test_four", backup_helper_test_four, 0, false }, { "backup_helper_test_files", backup_helper_test_files, 0, false }, + { "backup_helper_test_null_base", backup_helper_test_null_base, 0, false }, + { "backup_helper_test_missing_file", backup_helper_test_missing_file, 0, false }, + { "backup_helper_test_data_writer", backup_helper_test_data_writer, 0, false }, + { "backup_helper_test_data_reader", backup_helper_test_data_reader, 0, false }, { 0, NULL, 0, false} }; diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java index af7dfd4c1915..afbc70304ea4 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java +++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java @@ -17,14 +17,16 @@ package com.android.backuptest; import android.app.ListActivity; +import android.backup.BackupHelperDispatcher; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; import android.backup.BackupManager; +import android.backup.FileBackupHelper; import android.content.Intent; import android.content.SharedPreferences; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.PowerManager; -import android.os.SystemClock; +import android.os.ParcelFileDescriptor; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; @@ -32,6 +34,10 @@ import android.widget.ListView; import android.widget.Toast; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; @@ -46,6 +52,8 @@ public class BackupTestActivity extends ListActivity static final String PREF_KEY = "pref"; static final String FILE_NAME = "file.txt"; + BackupManager sBm = new BackupManager(this); + Test[] mTests = new Test[] { new Test("Show File") { void run() { @@ -79,8 +87,7 @@ public class BackupTestActivity extends ListActivity output.close(); } } - BackupManager bm = new BackupManager(BackupTestActivity.this); - bm.dataChanged(); + sBm.dataChanged(); } }, new Test("Clear File") { @@ -94,14 +101,12 @@ public class BackupTestActivity extends ListActivity output.close(); } } - BackupManager bm = new BackupManager(BackupTestActivity.this); - bm.dataChanged(); + sBm.dataChanged(); } }, new Test("Poke") { void run() { - BackupManager bm = new BackupManager(BackupTestActivity.this); - bm.dataChanged(); + sBm.dataChanged(); } }, new Test("Show Shared Pref") { @@ -120,8 +125,50 @@ public class BackupTestActivity extends ListActivity SharedPreferences.Editor editor = prefs.edit(); editor.putInt(PREF_KEY, val+1); editor.commit(); - BackupManager bm = new BackupManager(BackupTestActivity.this); - bm.dataChanged(); + sBm.dataChanged(); + } + }, + new Test("Backup Helpers") { + void run() { + try { + writeFile("a", "a\naa", MODE_PRIVATE); + writeFile("empty", "", MODE_PRIVATE); + + ParcelFileDescriptor state = ParcelFileDescriptor.open( + new File(getFilesDir(), "state"), + ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE| + ParcelFileDescriptor.MODE_TRUNCATE); + FileBackupHelper h = new FileBackupHelper(BackupTestActivity.this, + new String[] { "a", "empty" }); + FileOutputStream dataFile = openFileOutput("backup_test", MODE_WORLD_READABLE); + BackupDataOutput data = new BackupDataOutput(dataFile.getFD()); + h.performBackup(null, data, state); + dataFile.close(); + state.close(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }, + new Test("Restore Helpers") { + void run() { + try { + BackupHelperDispatcher dispatch = new BackupHelperDispatcher(); + dispatch.addHelper("", new FileBackupHelper(BackupTestActivity.this, + new String[] { "a", "empty" })); + FileInputStream dataFile = openFileInput("backup_test"); + BackupDataInput data = new BackupDataInput(dataFile.getFD()); + ParcelFileDescriptor state = ParcelFileDescriptor.open( + new File(getFilesDir(), "restore_state"), + ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE| + ParcelFileDescriptor.MODE_TRUNCATE); + // TODO: a more plausable synthetic stored-data version number + dispatch.performRestore(data, 0, state); + dataFile.close(); + state.close(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } } } }; @@ -154,5 +201,14 @@ public class BackupTestActivity extends ListActivity t.run(); } + void writeFile(String name, String contents, int mode) { + try { + PrintStream out = new PrintStream(openFileOutput(name, mode)); + out.print(contents); + out.close(); + } catch (FileNotFoundException ex) { + throw new RuntimeException(ex); + } + } } diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java new file mode 100644 index 000000000000..8e4fd39aa7cf --- /dev/null +++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +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/src/com/android/backuptest/BackupTestService.java b/tests/backup/src/com/android/backuptest/BackupTestService.java deleted file mode 100644 index 00eb86e47a74..000000000000 --- a/tests/backup/src/com/android/backuptest/BackupTestService.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -package com.android.backuptest; - -import android.backup.BackupService; -import android.backup.BackupDataOutput; -import android.backup.FileBackupHelper; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -public class BackupTestService extends BackupService -{ - static final String TAG = "BackupTestService"; - - @Override - public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { - Log.d(TAG, "onBackup"); - FileBackupHelper.performBackup(this, oldState, data, newState, new String[] { - BackupTestActivity.FILE_NAME - }); - } - - @Override - public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) { - Log.d(TAG, "onRestore"); - } -} - diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh new file mode 100755 index 000000000000..dbf9ed213ebc --- /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 000000000000..ccf29cf29731 --- /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/Android.mk b/tests/permission/Android.mk new file mode 100644 index 000000000000..a6df98e37576 --- /dev/null +++ b/tests/permission/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := FrameworkPermissionTests + +include $(BUILD_PACKAGE) + diff --git a/tests/permission/AndroidManifest.xml b/tests/permission/AndroidManifest.xml new file mode 100644 index 000000000000..b19bf006cfeb --- /dev/null +++ b/tests/permission/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2009 Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.permission.tests"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <!-- + The test declared in this instrumentation can be run via this command + "adb shell am instrument -w com.android.framework.permission.tests/android.test.InstrumentationTestRunner" + --> + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.framework.permission.tests" + android:label="Tests for private API framework permissions"/> + +</manifest> diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java new file mode 100644 index 000000000000..14d3d731b5bb --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java @@ -0,0 +1,182 @@ +package com.android.framework.permission.tests; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.content.res.Configuration; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * TODO: Remove this. This is only a placeholder, need to implement this. + */ +public class ActivityManagerPermissionTests extends TestCase { + IActivityManager mAm; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mAm = ActivityManagerNative.getDefault(); + } + + @SmallTest + public void testREORDER_TASKS() { + try { + mAm.moveTaskToFront(-1); + fail("IActivityManager.moveTaskToFront did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mAm.moveTaskToBack(-1); + fail("IActivityManager.moveTaskToBack did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mAm.moveTaskBackwards(-1); + fail("IActivityManager.moveTaskToFront did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testCHANGE_CONFIGURATION() { + try { + mAm.updateConfiguration(new Configuration()); + fail("IActivityManager.updateConfiguration did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSET_DEBUG_APP() { + try { + mAm.setDebugApp(null, false, false); + fail("IActivityManager.setDebugApp did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSET_PROCESS_LIMIT() { + try { + mAm.setProcessLimit(10); + fail("IActivityManager.setProcessLimit did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testALWAYS_FINISH() { + try { + mAm.setAlwaysFinish(false); + fail("IActivityManager.setAlwaysFinish did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSIGNAL_PERSISTENT_PROCESSES() { + try { + mAm.signalPersistentProcesses(-1); + fail("IActivityManager.signalPersistentProcesses did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testFORCE_BACK() { + try { + mAm.unhandledBack(); + fail("IActivityManager.unhandledBack did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSET_ACTIVITY_WATCHER() { + try { + mAm.setActivityWatcher(null); + fail("IActivityManager.setActivityWatcher did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSHUTDOWN() { + try { + mAm.shutdown(0); + fail("IActivityManager.shutdown did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSTOP_APP_SWITCHES() { + try { + mAm.stopAppSwitches(); + fail("IActivityManager.stopAppSwitches did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mAm.resumeAppSwitches(); + fail("IActivityManager.resumeAppSwitches did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } +} diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java new file mode 100644 index 000000000000..719e758e96a8 --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java @@ -0,0 +1,136 @@ +/* + * 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.framework.permission.tests; + +import junit.framework.TestCase; + +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; + + @Override + protected void setUp() throws Exception { + mHardwareService = IHardwareService.Stub.asInterface( + ServiceManager.getService("hardware")); + } + + /** + * Test that calling {@link android.os.IHardwareService#vibrate(long)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibrate() throws RemoteException { + try { + mHardwareService.vibrate(2000); + fail("vibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#vibratePattern(long[], + * int, android.os.IBinder)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibratePattern() throws RemoteException { + try { + mHardwareService.vibratePattern(new long[] {0}, 0, new Binder()); + fail("vibratePattern did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#cancelVibrate()} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testCancelVibrate() throws RemoteException { + try { + mHardwareService.cancelVibrate(); + fail("cancelVibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setFlashlightEnabled(boolean)} + * requires permissions. + * <p>Tests permissions: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#FLASHLIGHT} + * @throws RemoteException + */ + public void testSetFlashlightEnabled() throws RemoteException { + try { + mHardwareService.setFlashlightEnabled(true); + fail("setFlashlightEnabled did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#enableCameraFlash(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#CAMERA} + * @throws RemoteException + */ + public void testEnableCameraFlash() throws RemoteException { + try { + mHardwareService.enableCameraFlash(100); + fail("enableCameraFlash did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setBacklights(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * @throws RemoteException + */ + public void testSetBacklights() throws RemoteException { + try { + mHardwareService.setBacklights(0); + fail("setBacklights did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +} diff --git a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java new file mode 100644 index 000000000000..b690c459874a --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.framework.permission.tests; + +import junit.framework.TestCase; +import android.content.pm.PackageManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Verify PackageManager api's that require specific permissions. + */ +public class PmPermissionsTests extends AndroidTestCase { + private PackageManager mPm; + private String mPkgName = "com.android.framework.permission.tests"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPm = getContext().getPackageManager(); + } + + /* + * This test verifies that PackageManger.getPackageSizeInfo enforces permission + * android.permission.GET_PACKAGE_SIZE + */ + @SmallTest + public void testGetPackageSize() { + try { + mPm.getPackageSizeInfo(mPkgName, null); + fail("PackageManager.getPackageSizeInfo" + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.DeleteApplicationCacheFiles enforces permission + * android.permission.DELETE_CACHE_FILES + */ + @SmallTest + public void testDeleteApplicationCacheFiles() { + try { + mPm.deleteApplicationCacheFiles(mPkgName, null); + fail("PackageManager.deleteApplicationCacheFiles" + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.installPackage enforces permission + * android.permission.INSTALL_PACKAGES + */ + @SmallTest + public void testInstallPackage() { + try { + mPm.installPackage(null, null, 0, null); + fail("PackageManager.installPackage" + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.freeStorage + * enforces permission android.permission.CLEAR_APP_CACHE + */ + @SmallTest + public void testFreeStorage1() { + try { + mPm.freeStorage(100000, null); + fail("PackageManager.freeStorage " + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.freeStorageAndNotify + * enforces permission android.permission.CLEAR_APP_CACHE + */ + @SmallTest + public void testFreeStorage2() { + try { + mPm.freeStorageAndNotify(100000, null); + fail("PackageManager.freeStorageAndNotify" + + " did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.clearApplicationUserData + * enforces permission android.permission.CLEAR_APP_USER_DATA + */ + @SmallTest + public void testClearApplicationUserData() { + try { + mPm.clearApplicationUserData(mPkgName, null); + fail("PackageManager.clearApplicationUserData" + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /* + * This test verifies that PackageManger.deletePackage + * enforces permission android.permission.DELETE_PACKAGES + */ + @SmallTest + public void testDeletePackage() { + try { + mPm.deletePackage(mPkgName, null, 0); + fail("PackageManager.deletePackage" + + "did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +}
\ No newline at end of file diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java new file mode 100644 index 000000000000..3f1e27e0c766 --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -0,0 +1,50 @@ +package com.android.framework.permission.tests; + +import com.android.internal.os.BinderInternal; + +import android.os.Binder; +import android.os.IPermissionController; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManagerNative; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * TODO: Remove this. This is only a placeholder, need to implement this. + */ +public class ServiceManagerPermissionTests extends TestCase { + @SmallTest + public void testAddService() { + try { + // The security in the service manager is that you can't replace + // a service that is already published. + Binder binder = new Binder(); + ServiceManager.addService("activity", binder); + fail("ServiceManager.addService did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } + } + + @SmallTest + public void testSetPermissionController() { + try { + IPermissionController pc = new IPermissionController.Stub() { + public boolean checkPermission(java.lang.String permission, int pid, int uid) { + return true; + } + }; + ServiceManagerNative.asInterface(BinderInternal.getContextObject()) + .setPermissionController(pc); + fail("IServiceManager.setPermissionController did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } +} diff --git a/tests/permission/src/com/android/framework/permission/tests/SettingsPermissionsTests.java b/tests/permission/src/com/android/framework/permission/tests/SettingsPermissionsTests.java new file mode 100644 index 000000000000..f55998f9a0f2 --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/SettingsPermissionsTests.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 com.android.framework.permission.tests; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.provider.Settings; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; + +/** + * Verify that accessing private-API protected Settings require specific permissions. + */ +public class SettingsPermissionsTests extends AndroidTestCase { + + private ContentResolver mContentResolver; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContentResolver = getContext().getContentResolver(); + } + + /** + * Verify that writing to the GServices table in Settings provider requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#WRITE_GSERVICES} + */ + @MediumTest + public void testWriteGServices() { + try { + ContentValues values = new ContentValues(); + values.put("url", "android"); + mContentResolver.insert(Settings.Gservices.CONTENT_URI, values); + fail("Write into Gservices provider did not throw SecurityException as expected."); + } catch (SecurityException e) { + // expected + } + } +}
\ No newline at end of file diff --git a/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java new file mode 100644 index 000000000000..273943fe2d2e --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java @@ -0,0 +1,85 @@ +/* + * 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.framework.permission.tests; + +import java.util.ArrayList; + +import android.telephony.SmsManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Verify that SmsManager apis cannot be called without required permissions. + */ +public class SmsManagerPermissionTest extends AndroidTestCase { + + private static final String MSG_CONTENTS = "hi"; + private static final short DEST_PORT = (short)1004; + private static final String DEST_NUMBER = "4567"; + private static final String SRC_NUMBER = "1234"; + + /** + * Verify that SmsManager.sendTextMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_SMS}. + */ + @SmallTest + public void testSendTextMessage() { + try { + SmsManager.getDefault().sendTextMessage(SRC_NUMBER, DEST_NUMBER, MSG_CONTENTS, null, + null); + fail("SmsManager.sendTextMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Verify that SmsManager.sendDataMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_SMS}. + */ + @SmallTest + public void testSendDataMessage() { + try { + SmsManager.getDefault().sendDataMessage(SRC_NUMBER, DEST_NUMBER, DEST_PORT, + MSG_CONTENTS.getBytes(), null, null); + fail("SmsManager.sendDataMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Verify that SmsManager.sendMultipartMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_MMS}. + */ + @SmallTest + public void testSendMultipartMessage() { + try { + ArrayList<String> msgParts = new ArrayList<String>(2); + msgParts.add(MSG_CONTENTS); + msgParts.add("foo"); + SmsManager.getDefault().sendMultipartTextMessage(SRC_NUMBER, DEST_NUMBER, msgParts, + null, null); + fail("SmsManager.sendMultipartTextMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +} diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java new file mode 100644 index 000000000000..8ab2a10d2272 --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -0,0 +1,409 @@ +package com.android.framework.permission.tests; + +import android.content.res.Configuration; +import android.os.Binder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.IWindowManager; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import junit.framework.TestCase; + +/** + * TODO: Remove this. This is only a placeholder, need to implement this. + */ +public class WindowManagerPermissionTests extends TestCase { + IWindowManager mWm; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mWm = IWindowManager.Stub.asInterface( + ServiceManager.getService("window")); + } + + @SmallTest + public void testMANAGE_APP_TOKENS() { + try { + mWm.pauseKeyDispatching(null); + fail("IWindowManager.pauseKeyDispatching did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.resumeKeyDispatching(null); + fail("IWindowManager.resumeKeyDispatching did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setEventDispatching(true); + fail("IWindowManager.setEventDispatching did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.addWindowToken(null, 0); + fail("IWindowManager.addWindowToken did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.removeWindowToken(null); + fail("IWindowManager.removeWindowToken did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.addAppToken(0, null, 0, 0, false); + fail("IWindowManager.addAppToken did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAppGroupId(null, 0); + fail("IWindowManager.setAppGroupId did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.updateOrientationFromAppTokens(new Configuration(), null); + fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAppOrientation(null, 0); + mWm.addWindowToken(null, 0); + fail("IWindowManager.setAppOrientation did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setFocusedApp(null, false); + fail("IWindowManager.setFocusedApp did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.prepareAppTransition(0); + fail("IWindowManager.prepareAppTransition did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.executeAppTransition(); + fail("IWindowManager.executeAppTransition did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAppStartingWindow(null, "foo", 0, null, 0, 0, null, false); + fail("IWindowManager.setAppStartingWindow did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAppWillBeHidden(null); + fail("IWindowManager.setAppWillBeHidden did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAppVisibility(null, false); + fail("IWindowManager.setAppVisibility did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.startAppFreezingScreen(null, 0); + fail("IWindowManager.startAppFreezingScreen did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.stopAppFreezingScreen(null, false); + fail("IWindowManager.stopAppFreezingScreen did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.removeAppToken(null); + fail("IWindowManager.removeAppToken did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.moveAppToken(0, null); + fail("IWindowManager.moveAppToken did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.moveAppTokensToTop(null); + fail("IWindowManager.moveAppTokensToTop did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.moveAppTokensToBottom(null); + fail("IWindowManager.moveAppTokensToBottom did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testINJECT_EVENTS() { + try { + mWm.injectKeyEvent(new KeyEvent(0, 0), false); + fail("IWindowManager.injectKeyEvent did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.injectPointerEvent(MotionEvent.obtain(0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0), false); + fail("IWindowManager.injectPointerEvent did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.injectTrackballEvent(MotionEvent.obtain(0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0), false); + fail("IWindowManager.injectTrackballEvent did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testDISABLE_KEYGUARD() { + Binder token = new Binder(); + try { + mWm.disableKeyguard(token, "foo"); + fail("IWindowManager.disableKeyguard did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.reenableKeyguard(token); + fail("IWindowManager.reenableKeyguard did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.exitKeyguardSecurely(null); + fail("IWindowManager.exitKeyguardSecurely did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSET_ANIMATION_SCALE() { + try { + mWm.setAnimationScale(0, 1); + fail("IWindowManager.setAnimationScale did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.setAnimationScales(new float[1]); + fail("IWindowManager.setAnimationScales did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testREAD_INPUT_STATE() { + try { + mWm.getSwitchState(0); + fail("IWindowManager.getSwitchState did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.getSwitchStateForDevice(0, 0); + fail("IWindowManager.getSwitchStateForDevice did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.getScancodeState(0); + fail("IWindowManager.getScancodeState did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.getScancodeStateForDevice(0, 0); + fail("IWindowManager.getScancodeStateForDevice did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.getKeycodeState(0); + fail("IWindowManager.getKeycodeState did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + try { + mWm.getKeycodeStateForDevice(0, 0); + fail("IWindowManager.getKeycodeStateForDevice did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } + + @SmallTest + public void testSET_ORIENTATION() { + try { + mWm.setRotation(0, true, 0); + mWm.getSwitchState(0); + fail("IWindowManager.setRotation did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } +} diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 6bc1ee6cbfbc..67af116d372d 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 01c8140b25ce..3b96412c7c54 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/Bundle.h b/tools/aapt/Bundle.h index 2d8471bb5cc6..a6fedf3d34bd 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -34,10 +34,13 @@ public: mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), mRequireLocalization(false), mPseudolocalize(false), + mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mAssetSourceDir(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), + mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), + mVersionCode(NULL), mVersionName(NULL), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -70,6 +73,8 @@ public: void setRequireLocalization(bool val) { mRequireLocalization = val; } bool getPseudolocalize(void) const { return mPseudolocalize; } void setPseudolocalize(bool val) { mPseudolocalize = val; } + bool getValues(void) const { return mValues; } + void setValues(bool val) { mValues = val; } int getCompressionMethod(void) const { return mCompressionMethod; } void setCompressionMethod(int val) { mCompressionMethod = val; } const char* getOutputAPKFile() const { return mOutputAPKFile; } @@ -99,6 +104,17 @@ public: const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; } void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); } + const char* getMinSdkVersion() const { return mMinSdkVersion; } + void setMinSdkVersion(const char* val) { mMinSdkVersion = val; } + const char* getTargetSdkVersion() const { return mTargetSdkVersion; } + void setTargetSdkVersion(const char* val) { mTargetSdkVersion = val; } + const char* getMaxSdkVersion() const { return mMaxSdkVersion; } + void setMaxSdkVersion(const char* val) { mMaxSdkVersion = val; } + const char* getVersionCode() const { return mVersionCode; } + void setVersionCode(const char* val) { mVersionCode = val; } + const char* getVersionName() const { return mVersionName; } + void setVersionName(const char* val) { mVersionName = val; } + /* * Set and get the file specification. * @@ -138,6 +154,7 @@ private: bool mExtending; bool mRequireLocalization; bool mPseudolocalize; + bool mValues; int mCompressionMethod; const char* mOutputAPKFile; const char* mAssetSourceDir; @@ -151,6 +168,12 @@ private: android::Vector<const char*> mNoCompressExtensions; android::Vector<const char*> mResourceSourceDirs; + const char* mMinSdkVersion; + const char* mTargetSdkVersion; + const char* mMaxSdkVersion; + const char* mVersionCode; + const char* mVersionName; + /* file specification */ int mArgc; char* const* mArgv; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 6f3461df15eb..e04491d93b86 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -196,7 +196,7 @@ int doList(Bundle* bundle) printf("\nNo resource table found.\n"); } else { printf("\nResource table:\n"); - res.print(); + res.print(false); } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -268,17 +268,19 @@ static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* o return str ? String8(str, len) : String8(); } -static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) +static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, + String8* outError, int32_t defValue = -1) { ssize_t idx = indexOfAttribute(tree, attrRes); if (idx < 0) { - return -1; + return defValue; } Res_value value; if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType != Res_value::TYPE_INT_DEC) { + if (value.dataType < Res_value::TYPE_FIRST_INT + || value.dataType > Res_value::TYPE_LAST_INT) { if (outError != NULL) *outError = "attribute is not an integer value"; - return -1; + return defValue; } } return value.data; @@ -318,7 +320,18 @@ enum { VERSION_NAME_ATTR = 0x0101021c, LABEL_ATTR = 0x01010001, ICON_ATTR = 0x01010002, - MIN_SDK_VERSION_ATTR = 0x0101020c + MIN_SDK_VERSION_ATTR = 0x0101020c, + REQ_TOUCH_SCREEN_ATTR = 0x01010227, + REQ_KEYBOARD_TYPE_ATTR = 0x01010228, + REQ_HARD_KEYBOARD_ATTR = 0x01010229, + REQ_NAVIGATION_ATTR = 0x0101022a, + REQ_FIVE_WAY_NAV_ATTR = 0x01010232, + 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) { @@ -357,7 +370,8 @@ int doDump(Bundle* bundle) const char* filename = bundle->getFileSpecEntry(1); AssetManager assets; - if (!assets.addAssetPath(String8(filename), NULL)) { + void* assetsCookie; + if (!assets.addAssetPath(String8(filename), &assetsCookie)) { fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); return 1; } @@ -369,7 +383,7 @@ int doDump(Bundle* bundle) } if (strcmp("resources", option) == 0) { - res.print(); + res.print(bundle->getValues()); } else if (strcmp("xmltree", option) == 0) { if (bundle->getFileSpecCount() < 3) { @@ -488,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; @@ -543,15 +561,90 @@ int doDump(Bundle* bundle) goto bail; } printf("icon='%s'\n", icon.string()); - } else if (tag == "uses-sdk") { - int32_t sdkVersion = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); if (error != "") { - fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); goto bail; } - if (sdkVersion != -1) { - printf("sdkVersion:'%d'\n", sdkVersion); + if (testOnly != 0) { + printf("testOnly='%d'\n", testOnly); + } + } else if (tag == "uses-sdk") { + int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + if (error != "") { + error = ""; + String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", + 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); + if (error != "") { + error = ""; + String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", + 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") { + int32_t reqTouchScreen = getIntegerAttribute(tree, + REQ_TOUCH_SCREEN_ATTR, NULL, 0); + int32_t reqKeyboardType = getIntegerAttribute(tree, + REQ_KEYBOARD_TYPE_ATTR, NULL, 0); + int32_t reqHardKeyboard = getIntegerAttribute(tree, + REQ_HARD_KEYBOARD_ATTR, NULL, 0); + int32_t reqNavigation = getIntegerAttribute(tree, + REQ_NAVIGATION_ATTR, NULL, 0); + int32_t reqFiveWayNav = getIntegerAttribute(tree, + REQ_FIVE_WAY_NAV_ATTR, NULL, 0); + printf("uses-configuation:"); + if (reqTouchScreen != 0) { + printf(" reqTouchScreen='%d'", reqTouchScreen); + } + if (reqKeyboardType != 0) { + printf(" reqKeyboardType='%d'", reqKeyboardType); + } + if (reqHardKeyboard != 0) { + printf(" reqHardKeyboard='%d'", reqHardKeyboard); + } + if (reqNavigation != 0) { + printf(" reqNavigation='%d'", reqNavigation); + } + if (reqFiveWayNav != 0) { + printf(" reqFiveWayNav='%d'", reqFiveWayNav); + } + printf("\n"); + } else if (tag == "supports-density") { + int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n", + error.string()); + 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; @@ -592,18 +685,18 @@ int doDump(Bundle* bundle) } } } else if (depth == 5) { - if (withinActivity) { - if (tag == "action") { - //printf("LOG: action tag\n"); - String8 action = getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); - goto bail; - } - if (action == "android.intent.action.MAIN") { - isMainActivity = true; - //printf("LOG: isMainActivity==true\n"); - } + if (withinActivity) { + if (tag == "action") { + //printf("LOG: action tag\n"); + String8 action = getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); + goto bail; + } + if (action == "android.intent.action.MAIN") { + isMainActivity = true; + //printf("LOG: isMainActivity==true\n"); + } } else if (tag == "category") { String8 category = getAttribute(tree, NAME_ATTR, &error); if (error != "") { @@ -659,11 +752,31 @@ int doDump(Bundle* bundle) activityIcon.string()); } } + + // 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); - const size_t N = locales.size(); - for (size_t i=0; i<N; i++) { + const size_t NL = locales.size(); + for (size_t i=0; i<NL; i++) { const char* localeStr = locales[i].string(); if (localeStr == NULL || strlen(localeStr) == 0) { localeStr = "--_--"; @@ -671,6 +784,35 @@ int doDump(Bundle* bundle) printf(" '%s'", localeStr); } printf("\n"); + + Vector<ResTable_config> configs; + res.getConfigurations(&configs); + SortedVector<int> densities; + const size_t NC = configs.size(); + for (size_t i=0; i<NC; i++) { + int dens = configs[i].density; + if (dens == 0) dens = 160; + densities.add(dens); + } + + printf("densities:"); + const size_t ND = densities.size(); + for (size_t i=0; i<ND; i++) { + printf(" '%d'", densities[i]); + } + printf("\n"); + + AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); + if (dir != NULL) { + if (dir->getFileCount() > 0) { + printf("native-code:"); + for (size_t i=0; i<dir->getFileCount(); i++) { + printf(" '%s'", dir->getFileName(i).string()); + } + printf("\n"); + } + delete dir; + } } else if (strcmp("configurations", option) == 0) { Vector<ResTable_config> configs; res.getConfigurations(&configs); diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 71b1a3c86317..12a04454cb54 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -45,7 +45,7 @@ void usage(void) " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n" " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, - " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n" + " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" @@ -54,9 +54,10 @@ void usage(void) " xmlstrings Print the strings of the given compiled xml assets.\n\n", gProgName); fprintf(stderr, " %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n" - " [-0 extension [-0 extension ...]] \\\n" - " [-g tolerance] \\\n" - " [-j jarfile] \\\n" + " [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n" + " [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n" + " [--max-sdk-version VAL] [--app-version VAL] \\\n" + " [--app-version-name TEXT] \\\n" " [-I base-package [-I base-package ...]] \\\n" " [-A asset-source-dir] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " @@ -115,7 +116,19 @@ void usage(void) " and the first match found (left to right) will take precedence." " -0 specifies an additional extension for which such files will not\n" " be stored compressed in the .apk. An empty string means to not\n" - " compress any files at all.\n"); + " compress any files at all.\n" + " --min-sdk-version\n" + " inserts android:minSdkVersion in to manifest.\n" + " --target-sdk-version\n" + " inserts android:targetSdkVersion in to manifest.\n" + " --max-sdk-version\n" + " inserts android:maxSdkVersion in to manifest.\n" + " --values\n" + " when used with \"dump resources\" also includes resource values.\n" + " --version-code\n" + " inserts android:versionCode in to manifest.\n" + " --version-name\n" + " inserts android:versionName in to manifest.\n"); } /* @@ -339,6 +352,61 @@ int main(int argc, char* const argv[]) bundle.setCompressionMethod(ZipEntry::kCompressStored); } break; + case '-': + if (strcmp(cp, "-min-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--min-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setMinSdkVersion(argv[0]); + } else if (strcmp(cp, "-target-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--target-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setTargetSdkVersion(argv[0]); + } else if (strcmp(cp, "-max-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--max-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setMaxSdkVersion(argv[0]); + } else if (strcmp(cp, "-version-code") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--version-code' option\n"); + wantUsage = true; + goto bail; + } + bundle.setVersionCode(argv[0]); + } else if (strcmp(cp, "-version-name") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--version-name' option\n"); + wantUsage = true; + goto bail; + } + bundle.setVersionName(argv[0]); + } else if (strcmp(cp, "-values") == 0) { + bundle.setValues(true); + } else { + fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); + wantUsage = true; + goto bail; + } + cp += strlen(cp) - 1; + break; default: fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp); wantUsage = true; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index b2bd9ffc35ec..027e3ab3883d 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -433,7 +433,7 @@ static void checkForIds(const String8& path, ResXMLParser& parser) } } -static void applyFileOverlay(const sp<AaptAssets>& assets, +static bool applyFileOverlay(const sp<AaptAssets>& assets, const sp<ResourceTypeSet>& baseSet, const char *resType) { @@ -441,7 +441,7 @@ static void applyFileOverlay(const sp<AaptAssets>& assets, // Also add any found only in the overlay. sp<AaptAssets> overlay = assets->getOverlay(); String8 resTypeString(resType); - + // work through the linked list of overlays while (overlay.get()) { KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources(); @@ -456,7 +456,7 @@ static void applyFileOverlay(const sp<AaptAssets>& assets, size_t overlayCount = overlaySet->size(); for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) { size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex)); - if (baseIndex != UNKNOWN_ERROR) { + if (baseIndex < UNKNOWN_ERROR) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -482,9 +482,10 @@ static void applyFileOverlay(const sp<AaptAssets>& assets, } } else { // this group doesn't exist (a file that's only in the overlay) - // add it - baseSet->add(overlaySet->keyAt(overlayIndex), - overlaySet->valueAt(overlayIndex)); + fprintf(stderr, "aapt: error: " + "*** Resource file '%s' exists only in an overlay\n", + overlaySet->keyAt(overlayIndex).string()); + return false; } } // this overlay didn't have resources for this type @@ -492,7 +493,59 @@ static void applyFileOverlay(const sp<AaptAssets>& assets, // try next overlay overlay = overlay->getOverlay(); } - return; + return true; +} + +void addTagAttribute(const sp<XMLNode>& node, const char* ns8, + const char* attr8, const char* value) +{ + if (value == NULL) { + return; + } + + const String16 ns(ns8); + const String16 attr(attr8); + + if (node->getAttribute(ns, attr) != NULL) { + fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n", + String8(attr).string(), String8(ns).string()); + return; + } + + node->addAttribute(ns, attr, String16(value)); +} + +status_t massageManifest(Bundle* bundle, sp<XMLNode> root) +{ + root = root->searchElement(String16(), String16("manifest")); + if (root == NULL) { + fprintf(stderr, "No <manifest> tag.\n"); + return UNKNOWN_ERROR; + } + + addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", + bundle->getVersionCode()); + addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", + bundle->getVersionName()); + + if (bundle->getMinSdkVersion() != NULL + || bundle->getTargetSdkVersion() != NULL + || bundle->getMaxSdkVersion() != NULL) { + sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); + if (vers == NULL) { + vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); + root->insertChildAt(vers, 0); + } + + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", + bundle->getMinSdkVersion()); + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", + bundle->getTargetSdkVersion()); + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", + bundle->getMaxSdkVersion()); + } + + return NO_ERROR; } #define ASSIGN_IT(n) \ @@ -566,13 +619,15 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) current = current->getOverlay(); } // apply the overlay files to the base set - applyFileOverlay(assets, drawables, "drawable"); - applyFileOverlay(assets, layouts, "layout"); - applyFileOverlay(assets, anims, "anim"); - applyFileOverlay(assets, xmls, "xml"); - applyFileOverlay(assets, raws, "raw"); - applyFileOverlay(assets, colors, "color"); - applyFileOverlay(assets, menus, "menu"); + if (!applyFileOverlay(assets, drawables, "drawable") || + !applyFileOverlay(assets, layouts, "layout") || + !applyFileOverlay(assets, anims, "anim") || + !applyFileOverlay(assets, xmls, "xml") || + !applyFileOverlay(assets, raws, "raw") || + !applyFileOverlay(assets, colors, "color") || + !applyFileOverlay(assets, menus, "menu")) { + return UNKNOWN_ERROR; + } bool hasErrors = false; @@ -1013,7 +1068,15 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // Generate final compiled manifest file. manifestFile->clearData(); - err = compileXmlFile(assets, manifestFile, &table); + sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); + if (manifestTree == NULL) { + return UNKNOWN_ERROR; + } + err = massageManifest(bundle, manifestTree); + if (err < NO_ERROR) { + return err; + } + err = compileXmlFile(assets, manifestTree, manifestFile, &table); if (err < NO_ERROR) { return err; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index ef11a83c8387..b004664af5ce 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -23,6 +23,16 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, if (root == NULL) { return UNKNOWN_ERROR; } + + return compileXmlFile(assets, root, target, table, options); +} + +status_t compileXmlFile(const sp<AaptAssets>& assets, + const sp<XMLNode>& root, + const sp<AaptFile>& target, + ResourceTable* table, + int options) +{ if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) { root->removeWhitespace(true, NULL); } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) { @@ -651,6 +661,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 string_array16("string-array"); const String16 integer_array16("integer-array"); const String16 public16("public"); + const String16 public_padding16("public-padding"); const String16 private_symbols16("private-symbols"); const String16 skip16("skip"); const String16 eat_comment16("eat-comment"); @@ -685,7 +696,7 @@ status_t compileResourceFile(Bundle* bundle, bool hasErrors = false; - uint32_t nextPublicId = 0; + DefaultKeyedVector<String16, uint32_t> nextPublicId(0); ResXMLTree::event_code_t code; do { @@ -718,6 +729,7 @@ status_t compileResourceFile(Bundle* bundle, String16 curType; int32_t curFormat = ResTable_map::TYPE_ANY; bool curIsBag = false; + bool curIsBagReplaceOnOverwrite = false; bool curIsStyled = false; bool curIsPseudolocalizable = false; bool localHasErrors = false; @@ -774,15 +786,15 @@ status_t compileResourceFile(Bundle* bundle, hasErrors = localHasErrors = true; } else { ident = identValue.data; - nextPublicId = ident+1; + nextPublicId.replaceValueFor(type, ident+1); } - } else if (nextPublicId == 0) { + } else if (nextPublicId.indexOfKey(type) < 0) { srcPos.error("No 'id' attribute supplied <public>," " and no previous id defined in this file.\n"); hasErrors = localHasErrors = true; } else if (!localHasErrors) { - ident = nextPublicId; - nextPublicId++; + ident = nextPublicId.valueFor(type); + nextPublicId.replaceValueFor(type, ident+1); } if (!localHasErrors) { @@ -816,6 +828,116 @@ status_t compileResourceFile(Bundle* bundle, } continue; + } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + + String16 type; + ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); + if (typeIdx < 0) { + srcPos.error("A 'type' attribute is required for <public-padding>\n"); + hasErrors = localHasErrors = true; + } + type = String16(block.getAttributeStringValue(typeIdx, &len)); + + String16 name; + ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); + if (nameIdx < 0) { + srcPos.error("A 'name' attribute is required for <public-padding>\n"); + hasErrors = localHasErrors = true; + } + name = String16(block.getAttributeStringValue(nameIdx, &len)); + + uint32_t start = 0; + ssize_t startIdx = block.indexOfAttribute(NULL, "start"); + if (startIdx >= 0) { + const char16_t* startStr = block.getAttributeStringValue(startIdx, &len); + Res_value startValue; + if (!ResTable::stringToInt(startStr, len, &startValue)) { + srcPos.error("Given 'start' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(startIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + start = startValue.data; + } + } else if (nextPublicId.indexOfKey(type) < 0) { + srcPos.error("No 'start' attribute supplied <public-padding>," + " and no previous id defined in this file.\n"); + hasErrors = localHasErrors = true; + } else if (!localHasErrors) { + start = nextPublicId.valueFor(type); + } + + uint32_t end = 0; + ssize_t endIdx = block.indexOfAttribute(NULL, "end"); + if (endIdx >= 0) { + const char16_t* endStr = block.getAttributeStringValue(endIdx, &len); + Res_value endValue; + if (!ResTable::stringToInt(endStr, len, &endValue)) { + srcPos.error("Given 'end' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(endIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + end = endValue.data; + } + } else { + srcPos.error("No 'end' attribute supplied <public-padding>\n"); + hasErrors = localHasErrors = true; + } + + if (end >= start) { + nextPublicId.replaceValueFor(type, end+1); + } else { + srcPos.error("Padding start '%ul' is after end '%ul'\n", + start, end); + hasErrors = localHasErrors = true; + } + + String16 comment( + block.getComment(&len) ? block.getComment(&len) : nulStr); + for (uint32_t curIdent=start; curIdent<=end; curIdent++) { + if (localHasErrors) { + break; + } + String16 curName(name); + char buf[64]; + sprintf(buf, "%d", (int)(end-curIdent+1)); + curName.append(String16(buf)); + + err = outTable->addEntry(srcPos, myPackage, type, curName, + String16("padding"), NULL, &curParams, false, + ResTable_map::TYPE_STRING, overwrite); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + err = outTable->addPublic(srcPos, myPackage, type, + curName, curIdent); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); + if (symbols != NULL) { + symbols = symbols->addNestedSymbol(String8(type), srcPos); + } + if (symbols != NULL) { + symbols->makeSymbolPublic(String8(curName), srcPos); + symbols->appendComment(String8(curName), comment, srcPos); + } else { + srcPos.error("Unable to create symbols!\n"); + hasErrors = localHasErrors = true; + } + } + + while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + break; + } + } + } + continue; + } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) { String16 pkg; ssize_t pkgIdx = block.indexOfAttribute(NULL, "package"); @@ -1050,6 +1172,7 @@ status_t compileResourceFile(Bundle* bundle, curTag = &array16; curType = array16; curIsBag = true; + curIsBagReplaceOnOverwrite = true; ssize_t formatIdx = block.indexOfAttribute(NULL, "format"); if (formatIdx >= 0) { String16 formatStr = String16(block.getAttributeStringValue( @@ -1068,12 +1191,14 @@ status_t compileResourceFile(Bundle* bundle, curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsBag = true; + curIsBagReplaceOnOverwrite = true; curIsPseudolocalizable = true; } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { curTag = &integer_array16; curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER; curIsBag = true; + curIsBagReplaceOnOverwrite = true; } else { SourcePos(in->getPrintableSource(), block.getLineNumber()).error( "Found tag %s where item is expected\n", @@ -1108,9 +1233,10 @@ status_t compileResourceFile(Bundle* bundle, } if (!localHasErrors) { - err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), - myPackage, curType, ident, parentIdent, &curParams, - overwrite); + err = outTable->startBag(SourcePos(in->getPrintableSource(), + block.getLineNumber()), myPackage, curType, ident, + parentIdent, &curParams, + overwrite, curIsBagReplaceOnOverwrite); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1307,7 +1433,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets } else if (id != 0) { if (id == 127) { if (mHaveAppPackage) { - fprintf(stderr, "Included resource have two application packages!\n"); + fprintf(stderr, "Included resources have two application packages!\n"); return UNKNOWN_ERROR; } mHaveAppPackage = true; @@ -1390,8 +1516,9 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos, String8(value).string()); } #endif - - sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex); + + sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite, + params, doSetIndex); if (e == NULL) { return UNKNOWN_ERROR; } @@ -1408,6 +1535,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, const String16& name, const String16& bagParent, const ResTable_config* params, + bool overlay, bool replace, bool isId) { status_t result = NO_ERROR; @@ -1428,8 +1556,12 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp<Entry> e = getEntry(package, type, name, sourcePos, params); + if (overlay && !hasBagOrEntry(package, type, name)) { + sourcePos.error("Can't add new bags in an overlay. See '%s'\n", + String8(name).string()); + return UNKNOWN_ERROR; + } + sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params); if (e == NULL) { return UNKNOWN_ERROR; } @@ -1450,7 +1582,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, return result; } - if (replace) { + if (overlay && replace) { return e->emptyBag(sourcePos); } return result; @@ -1483,8 +1615,7 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp<Entry> e = getEntry(package, type, name, sourcePos, params); + sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params); if (e == NULL) { return UNKNOWN_ERROR; } @@ -2767,7 +2898,7 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos, mItem.sourcePos.file.string(), mItem.sourcePos.line); return UNKNOWN_ERROR; } - + mType = TYPE_ITEM; mItem = item; mItemFormat = format; @@ -3087,11 +3218,17 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos, sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, const SourcePos& sourcePos, const ResTable_config* config, - bool doSetIndex) + bool doSetIndex, + bool overlay) { int pos = -1; sp<ConfigList> c = mConfigs.valueFor(entry); if (c == NULL) { + if (overlay == true) { + sourcePos.error("Resource %s appears in overlay but not" + " in the base package.\n", String8(entry).string()); + return NULL; + } c = new ConfigList(entry, sourcePos); mConfigs.add(entry, c); pos = (int)mOrderedConfigs.size(); @@ -3390,6 +3527,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package, const String16& type, const String16& name, const SourcePos& sourcePos, + bool overlay, const ResTable_config* config, bool doSetIndex) { @@ -3397,7 +3535,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package, if (t == NULL) { return NULL; } - return t->getEntry(name, sourcePos, config, doSetIndex); + return t->getEntry(name, sourcePos, config, doSetIndex, overlay); } sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID, diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 74ba326c2bf3..ec4331aa3557 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -15,6 +15,7 @@ using namespace std; +class XMLNode; class ResourceTable; enum { @@ -34,6 +35,12 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, ResourceTable* table, int options = XML_COMPILE_STANDARD_RESOURCE); +status_t compileXmlFile(const sp<AaptAssets>& assets, + const sp<XMLNode>& xmlTree, + const sp<AaptFile>& target, + ResourceTable* table, + int options = XML_COMPILE_STANDARD_RESOURCE); + status_t compileResourceFile(Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptFile>& in, @@ -89,6 +96,7 @@ public: const String16& name, const String16& bagParent, const ResTable_config* params = NULL, + bool overlay = false, bool replace = false, bool isId = false); @@ -410,7 +418,8 @@ public: sp<Entry> getEntry(const String16& entry, const SourcePos& pos, const ResTable_config* config = NULL, - bool doSetIndex = false); + bool doSetIndex = false, + bool overlay = false); const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; } @@ -494,6 +503,7 @@ private: const String16& type, const String16& name, const SourcePos& pos, + bool overlay, const ResTable_config* config = NULL, bool doSetIndex = false); sp<const Entry> getEntry(uint32_t resID, diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index d4765672274e..2a85bc7e8b6d 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -524,12 +524,30 @@ const Vector<sp<XMLNode> >& XMLNode::getChildren() const return mChildren; } +const String8& XMLNode::getFilename() const +{ + return mFilename; +} + const Vector<XMLNode::attribute_entry>& XMLNode::getAttributes() const { return mAttributes; } +const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns, + const String16& name) const +{ + for (size_t i=0; i<mAttributes.size(); i++) { + const attribute_entry& ae(mAttributes.itemAt(i)); + if (ae.ns == ns && ae.name == name) { + return &ae; + } + } + + return NULL; +} + const String16& XMLNode::getCData() const { return mChars; @@ -550,6 +568,38 @@ int32_t XMLNode::getEndLineNumber() const return mEndLineNumber; } +sp<XMLNode> XMLNode::searchElement(const String16& tagNamespace, const String16& tagName) +{ + if (getType() == XMLNode::TYPE_ELEMENT + && mNamespaceUri == tagNamespace + && mElementName == tagName) { + return this; + } + + for (size_t i=0; i<mChildren.size(); i++) { + sp<XMLNode> found = mChildren.itemAt(i)->searchElement(tagNamespace, tagName); + if (found != NULL) { + return found; + } + } + + return NULL; +} + +sp<XMLNode> XMLNode::getChildElement(const String16& tagNamespace, const String16& tagName) +{ + for (size_t i=0; i<mChildren.size(); i++) { + sp<XMLNode> child = mChildren.itemAt(i); + if (child->getType() == XMLNode::TYPE_ELEMENT + && child->mNamespaceUri == tagNamespace + && child->mElementName == tagName) { + return child; + } + } + + return NULL; +} + status_t XMLNode::addChild(const sp<XMLNode>& child) { if (getType() == TYPE_CDATA) { @@ -561,6 +611,17 @@ status_t XMLNode::addChild(const sp<XMLNode>& child) return NO_ERROR; } +status_t XMLNode::insertChildAt(const sp<XMLNode>& child, size_t index) +{ + if (getType() == TYPE_CDATA) { + SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node."); + return UNKNOWN_ERROR; + } + //printf("Adding child %p to parent %p\n", child.get(), this); + mChildren.insertAt(child, index); + return NO_ERROR; +} + status_t XMLNode::addAttribute(const String16& ns, const String16& name, const String16& value) { diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index 86548a22c858..a9bea4312ecc 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -68,6 +68,8 @@ public: const String16& getElementName() const; const Vector<sp<XMLNode> >& getChildren() const; + const String8& getFilename() const; + struct attribute_entry { attribute_entry() : index(~(uint32_t)0), nameResId(0) { @@ -91,6 +93,8 @@ public: const Vector<attribute_entry>& getAttributes() const; + const attribute_entry* getAttribute(const String16& ns, const String16& name) const; + const String16& getCData() const; const String16& getComment() const; @@ -98,8 +102,14 @@ public: int32_t getStartLineNumber() const; int32_t getEndLineNumber() const; + sp<XMLNode> searchElement(const String16& tagNamespace, const String16& tagName); + + sp<XMLNode> getChildElement(const String16& tagNamespace, const String16& tagName); + status_t addChild(const sp<XMLNode>& child); + status_t insertChildAt(const sp<XMLNode>& child, size_t index); + status_t addAttribute(const String16& ns, const String16& name, const String16& value); diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h index 1dedd048bd14..aec21644bf6b 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/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java index 3f9a99355a4b..18c0e178b3b7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java @@ -24,8 +24,8 @@ import java.awt.geom.AffineTransform; */ public class Matrix extends _Original_Matrix { - float mValues[] = new float[9]; - + float mValues[] = new float[9]; + /** * Create an identity matrix */ @@ -40,7 +40,7 @@ public class Matrix extends _Original_Matrix { public Matrix(Matrix src) { set(src); } - + /** * Creates a Matrix object from the float array. The array becomes the internal storage * of the object. @@ -50,14 +50,14 @@ public class Matrix extends _Original_Matrix { assert data.length != 9; mValues = data; } - + @Override public void finalize() throws Throwable { // pass } - + //---------- Custom Methods - + /** * Adds the given transformation to the current Matrix * <p/>This in effect does this = this*matrix @@ -65,17 +65,17 @@ public class Matrix extends _Original_Matrix { */ private void addTransform(float[] matrix) { float[] tmp = new float[9]; - - // first row + + // first row tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6]; tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7]; tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8]; - + // 2nd row tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6]; tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7]; tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8]; - + // 3rd row tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6]; tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7]; @@ -84,16 +84,16 @@ public class Matrix extends _Original_Matrix { // copy the result over to mValues mValues = tmp; } - + public AffineTransform getTransform() { return new AffineTransform(mValues[0], mValues[1], mValues[2], mValues[3], mValues[4], mValues[5]); } - + public boolean hasPerspective() { return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1); } - + //---------- /** @@ -109,7 +109,7 @@ public class Matrix extends _Original_Matrix { } } } - + return true; } @@ -122,7 +122,7 @@ public class Matrix extends _Original_Matrix { public boolean rectStaysRect() { return (computeTypeMask() & kRectStaysRect_Mask) != 0; } - + /** * (deep) copy the src matrix into this matrix. If src is null, reset this * matrix to the identity matrix. @@ -151,10 +151,10 @@ public class Matrix extends _Original_Matrix { return false; } } - + return true; } - + return false; } @@ -179,7 +179,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = dy; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; } /** @@ -200,7 +200,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = -py; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; // scale addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }); @@ -219,7 +219,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = 0; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; } /** @@ -240,13 +240,13 @@ public class Matrix extends _Original_Matrix { mValues[5] = -py; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; // scale double rad = Math.toRadians(degrees); float cos = (float)Math.cos(rad); float sin = (float)Math.sin(rad); - addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); + addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); // translate back the pivot addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); } @@ -268,7 +268,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = 0; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; } /** @@ -289,10 +289,10 @@ public class Matrix extends _Original_Matrix { mValues[5] = -py; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; // scale - addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 }); + addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 }); // translate back the pivot addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); } @@ -308,7 +308,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = 0; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; } /** @@ -329,7 +329,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = -py; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; // scale addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); @@ -348,7 +348,7 @@ public class Matrix extends _Original_Matrix { mValues[5] = 0; mValues[6] = 0; mValues[7] = 0; - mValues[7] = 1; + mValues[8] = 1; } /** @@ -366,10 +366,10 @@ public class Matrix extends _Original_Matrix { tmp.addTransform(a.mValues); set(tmp); } - + return true; } - + @Override public boolean setConcat(_Original_Matrix a, _Original_Matrix b) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); @@ -384,7 +384,7 @@ public class Matrix extends _Original_Matrix { // create a matrix that will be multiply by this Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 }); m.addTransform(this.mValues); - + System.arraycopy(m.mValues, 0, mValues, 0, 9); return true; } @@ -399,7 +399,7 @@ public class Matrix extends _Original_Matrix { m.setScale(sx, sy, px, py); m.addTransform(mValues); set(m); - + return true; } @@ -413,7 +413,7 @@ public class Matrix extends _Original_Matrix { m.setScale(sx, sy); m.addTransform(mValues); set(m); - + return true; } @@ -427,7 +427,7 @@ public class Matrix extends _Original_Matrix { m.setRotate(degrees, px, py); m.addTransform(mValues); set(m); - + return true; } @@ -441,7 +441,7 @@ public class Matrix extends _Original_Matrix { m.setRotate(degrees); m.addTransform(mValues); set(m); - + return true; } @@ -455,7 +455,7 @@ public class Matrix extends _Original_Matrix { m.setSkew(kx, ky, px, py); m.addTransform(mValues); set(m); - + return true; } @@ -469,7 +469,7 @@ public class Matrix extends _Original_Matrix { m.setSkew(kx, ky); m.addTransform(mValues); set(m); - + return true; } @@ -481,10 +481,10 @@ public class Matrix extends _Original_Matrix { Matrix m = new Matrix(other); other.addTransform(mValues); set(m); - + return true; } - + @Override public boolean preConcat(_Original_Matrix other) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); @@ -513,7 +513,7 @@ public class Matrix extends _Original_Matrix { addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }); // translate back the pivot addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); - + return true; } @@ -540,10 +540,10 @@ public class Matrix extends _Original_Matrix { double rad = Math.toRadians(degrees); float cos = (float)Math.cos(rad); float sin = (float)Math.sin(rad); - addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); + addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); // translate back the pivot addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); - + return true; } @@ -557,7 +557,7 @@ public class Matrix extends _Original_Matrix { float cos = (float)Math.cos(rad); float sin = (float)Math.sin(rad); addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); - + return true; } @@ -574,7 +574,7 @@ public class Matrix extends _Original_Matrix { addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); // translate back the pivot addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); - + return true; } @@ -585,7 +585,7 @@ public class Matrix extends _Original_Matrix { @Override public boolean postSkew(float kx, float ky) { addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); - + return true; } @@ -595,7 +595,7 @@ public class Matrix extends _Original_Matrix { */ public boolean postConcat(Matrix other) { addTransform(other.mValues); - + return true; } @@ -603,7 +603,7 @@ public class Matrix extends _Original_Matrix { public boolean postConcat(_Original_Matrix other) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } - + /** Controlls how the src rect should align into the dst rect for setRectToRect(). */ @@ -634,7 +634,7 @@ public class Matrix extends _Original_Matrix { */ END (3); - // the native values must match those in SkMatrix.h + // the native values must match those in SkMatrix.h ScaleToFit(int nativeInt) { this.nativeInt = nativeInt; } @@ -655,7 +655,7 @@ public class Matrix extends _Original_Matrix { if (dst == null || src == null) { throw new NullPointerException(); } - + if (src.isEmpty()) { reset(); return false; @@ -689,7 +689,7 @@ public class Matrix extends _Original_Matrix { } else { diff = dst.height() - src.height() * sy; } - + if (stf == ScaleToFit.CENTER) { diff = diff / 2; } @@ -712,12 +712,12 @@ public class Matrix extends _Original_Matrix { mValues[8] = 1; return true; } - + @Override public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } - + /** * Set the matrix such that the specified src points would map to the * specified dst points. The "points" are represented as an array of floats, @@ -749,7 +749,7 @@ public class Matrix extends _Original_Matrix { public boolean invert(Matrix inverse) { throw new UnsupportedOperationException("STUB NEEDED"); } - + @Override public boolean invert(_Original_Matrix inverse) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); @@ -772,7 +772,7 @@ public class Matrix extends _Original_Matrix { checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); throw new UnsupportedOperationException("STUB NEEDED"); } - + /** * Apply this matrix to the array of 2D vectors specified by src, and write * the transformed vectors into the array of vectors specified by dst. The @@ -790,7 +790,7 @@ public class Matrix extends _Original_Matrix { checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount); throw new UnsupportedOperationException("STUB NEEDED"); } - + /** * Apply this matrix to the array of 2D points specified by src, and write * the transformed points into the array of points specified by dst. The @@ -883,7 +883,7 @@ public class Matrix extends _Original_Matrix { public float mapRadius(float radius) { throw new UnsupportedOperationException("STUB NEEDED"); } - + /** Copy 9 values from the matrix into the array. */ @Override @@ -907,7 +907,7 @@ public class Matrix extends _Original_Matrix { } System.arraycopy(values, 0, mValues, 0, mValues.length); } - + @SuppressWarnings("unused") private final static int kIdentity_Mask = 0; private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation @@ -917,7 +917,7 @@ public class Matrix extends _Original_Matrix { private final static int kRectStaysRect_Mask = 0x10; @SuppressWarnings("unused") private final static int kUnknown_Mask = 0x80; - + @SuppressWarnings("unused") private final static int kAllMasks = kTranslate_Mask | kScale_Mask | @@ -942,43 +942,43 @@ public class Matrix extends _Original_Matrix { if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) { mask |= kPerspective_Mask; } - + if (mValues[2] != 0. || mValues[5] != 0.) { mask |= kTranslate_Mask; } - + float m00 = mValues[0]; float m01 = mValues[1]; float m10 = mValues[3]; float m11 = mValues[4]; - + if (m01 != 0. || m10 != 0.) { mask |= kAffine_Mask; } - + if (m00 != 1. || m11 != 1.) { mask |= kScale_Mask; } - + if ((mask & kPerspective_Mask) == 0) { // map non-zero to 1 int im00 = m00 != 0 ? 1 : 0; int im01 = m01 != 0 ? 1 : 0; int im10 = m10 != 0 ? 1 : 0; int im11 = m11 != 0 ? 1 : 0; - + // record if the (p)rimary and (s)econdary diagonals are all 0 or // all non-zero (answer is 0 or 1) int dp0 = (im00 | im11) ^ 1; // true if both are 0 int dp1 = im00 & im11; // true if both are 1 int ds0 = (im01 | im10) ^ 1; // true if both are 0 int ds1 = im01 & im10; // true if both are 1 - + // return 1 if primary is 1 and secondary is 0 or // primary is 0 and secondary is 1 mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; } - + return mask; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 47a7ec035ea1..d0a1c46bf644 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -55,6 +55,8 @@ import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.view.WindowManager.LayoutParams; import android.widget.FrameLayout; +import android.widget.TabHost; +import android.widget.TabWidget; import java.lang.ref.SoftReference; import java.lang.reflect.Field; @@ -69,10 +71,10 @@ import java.util.Map; * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. */ public final class Bridge implements ILayoutBridge { - + private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; - + public static class StaticMethodNotImplementedException extends RuntimeException { private static final long serialVersionUID = 1L; @@ -82,19 +84,20 @@ public final class Bridge implements ILayoutBridge { } /** - * Maps from id to resource name/type. + * Maps from id to resource name/type. This is for android.R only. */ private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>(); /** - * Same as sRMap except for int[] instead of int resources. + * Same as sRMap except for int[] instead of int resources. This is for android.R only. */ private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>(); /** - * Reverse map compared to sRMap, resource type -> (resource name -> id) + * Reverse map compared to sRMap, resource type -> (resource name -> id). + * This is for android.R only. */ private final static Map<String, Map<String, Integer>> sRFullMap = new HashMap<String, Map<String,Integer>>(); - + private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache = new HashMap<Object, Map<String, SoftReference<Bitmap>>>(); private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache = @@ -104,7 +107,7 @@ public final class Bridge implements ILayoutBridge { new HashMap<String, SoftReference<Bitmap>>(); private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache = new HashMap<String, SoftReference<NinePatch>>(); - + private static Map<String, Map<String, Integer>> sEnumValueMap; /** @@ -156,14 +159,14 @@ public final class Bridge implements ILayoutBridge { return sinit(fontOsLocation, enumValueMap); } - + private static synchronized boolean sinit(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener // on static (native) methods which prints the signature on the console and // throws an exception. - // This is useful when testing the rendering in ADT to identify static native + // This is useful when testing the rendering in ADT to identify static native // methods that are ignored -- layoutlib_create makes them returns 0/false/null // which is generally OK yet might be a problem, so this is how you'd find out. // @@ -214,7 +217,7 @@ public final class Bridge implements ILayoutBridge { } else { return false; } - + sEnumValueMap = enumValueMap; // now parse com.android.internal.R (and only this one as android.R is a subset of @@ -226,13 +229,13 @@ public final class Bridge implements ILayoutBridge { // int[] does not implement equals/hashCode, and if the parsing used a different class // loader for the R class, this would NOT work. Class<?> r = com.android.internal.R.class; - + for (Class<?> inner : r.getDeclaredClasses()) { String resType = inner.getSimpleName(); Map<String, Integer> fullMap = new HashMap<String, Integer>(); sRFullMap.put(resType, fullMap); - + for (Field f : inner.getDeclaredFields()) { // only process static final fields. Since the final attribute may have // been altered by layoutlib_create, we only check static @@ -243,7 +246,7 @@ public final class Bridge implements ILayoutBridge { // if the object is an int[] we put it in sRArrayMap sRArrayMap.put((int[]) f.get(null), f.getName()); } else if (type == int.class) { - Integer value = (Integer) f.get(null); + Integer value = (Integer) f.get(null); sRMap.put(value, new String[] { f.getName(), resType }); fullMap.put(f.getName(), value); } else { @@ -281,7 +284,7 @@ public final class Bridge implements ILayoutBridge { themeName = themeName.substring(1); isProjectTheme = true; } - + return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, @@ -294,6 +297,7 @@ public final class Bridge implements ILayoutBridge { * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ + @Deprecated public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, Map<String, Map<String, IResourceValue>> projectResources, @@ -319,7 +323,7 @@ public final class Bridge implements ILayoutBridge { if (logger == null) { logger = sDefaultLogger; } - + synchronized (sDefaultLogger) { sLogger = logger; } @@ -327,12 +331,12 @@ public final class Bridge implements ILayoutBridge { // find the current theme and compute the style inheritance map Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = new HashMap<IStyleResourceValue, IStyleResourceValue>(); - + IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme, projectResources.get(BridgeConstants.RES_STYLE), frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap); - - BridgeContext context = null; + + BridgeContext context = null; try { // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); @@ -347,29 +351,32 @@ public final class Bridge implements ILayoutBridge { frameworkResources, styleParentMap, customViewLoader, logger); BridgeInflater inflater = new BridgeInflater(context, customViewLoader); context.setBridgeInflater(inflater); - + IResourceValue windowBackground = null; int screenOffset = 0; if (currentTheme != null) { windowBackground = context.findItemInStyle(currentTheme, "windowBackground"); windowBackground = context.resolveResValue(windowBackground); - + screenOffset = getScreenOffset(currentTheme, context); } - + // we need to make sure the Looper has been initialized for this thread. // this is required for View that creates Handler objects. if (Looper.myLooper() == null) { Looper.prepare(); } - + BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription, context, false /* platformResourceFlag */); - + ViewGroup root = new FrameLayout(context); - + View view = inflater.inflate(parser, root); - + + // post-inflate process. For now this supports TabHost/TabWidget + postInflateProcess(view, customViewLoader); + // set the AttachInfo on the root view. AttachInfo info = new AttachInfo(new WindowSession(), new Window(), new Handler(), null); @@ -392,16 +399,19 @@ public final class Bridge implements ILayoutBridge { // measure the views view.measure(w_spec, h_spec); view.layout(0, screenOffset, screenWidth, screenHeight); - + // draw them BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset, logger); - + root.draw(canvas); canvas.dispose(); - + return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context), canvas.getImage()); + } catch (PostInflateException e) { + return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n" + + e.getMessage()); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -419,7 +429,7 @@ public final class Bridge implements ILayoutBridge { // Make sure to remove static references, otherwise we could not unload the lib BridgeResources.clearSystem(); BridgeAssetManager.clearSystem(); - + // Remove the global logger synchronized (sDefaultLogger) { sLogger = sDefaultLogger; @@ -437,18 +447,18 @@ public final class Bridge implements ILayoutBridge { sProject9PatchCache.remove(projectKey); } } - + /** * Returns details of a framework resource from its integer value. * @param value the integer value * @return an array of 2 strings containing the resource name and type, or null if the id - * does not match any resource. + * does not match any resource. */ public static String[] resolveResourceValue(int value) { return sRMap.get(value); - + } - + /** * Returns the name of a framework resource whose value is an int array. * @param array @@ -456,7 +466,7 @@ public final class Bridge implements ILayoutBridge { public static String resolveResourceValue(int[] array) { return sRArrayMap.get(array); } - + /** * Returns the integer id of a framework resource, from a given resource type and resource name. * @param type the type of the resource @@ -468,15 +478,15 @@ public final class Bridge implements ILayoutBridge { if (map != null) { return map.get(name); } - + return null; } - + static Map<String, Integer> getEnumValues(String attributeName) { if (sEnumValueMap != null) { return sEnumValueMap.get(attributeName); } - + return null; } @@ -507,13 +517,13 @@ public final class Bridge implements ILayoutBridge { return result; } - + /** * Compute style information from the given list of style for the project and framework. * @param themeName the name of the current theme. In order to differentiate project and * platform themes sharing the same name, all project themes must be prepended with * a '*' character. - * @param isProjectTheme Is this a project theme + * @param isProjectTheme Is this a project theme * @param inProjectStyleMap the project style map * @param inFrameworkStyleMap the framework style map * @param outInheritanceMap the map of style inheritance. This is filled by the method @@ -523,23 +533,23 @@ public final class Bridge implements ILayoutBridge { String themeName, boolean isProjectTheme, Map<String, IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { - + if (inProjectStyleMap != null && inFrameworkStyleMap != null) { // first, get the theme IResourceValue theme = null; - + // project theme names have been prepended with a * if (isProjectTheme) { theme = inProjectStyleMap.get(themeName); } else { theme = inFrameworkStyleMap.get(themeName); } - + if (theme instanceof IStyleResourceValue) { // compute the inheritance map for both the project and framework styles computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap, inFrameworkStyleMap, outInheritanceMap); - + // Compute the style inheritance for the framework styles/themes. // Since, for those, the style parent values do not contain 'android:' // we want to force looking in the framework style only to avoid using @@ -547,11 +557,11 @@ public final class Bridge implements ILayoutBridge { // To do this, we pass null in lieu of the project style map. computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */, inFrameworkStyleMap, outInheritanceMap); - + return (IStyleResourceValue)theme; } } - + return null; } @@ -573,7 +583,7 @@ public final class Bridge implements ILayoutBridge { // first look for a specified parent. String parentName = style.getParentStyle(); - + // no specified parent? try to infer it from the name of the style. if (parentName == null) { parentName = getParentName(value.getName()); @@ -581,7 +591,7 @@ public final class Bridge implements ILayoutBridge { if (parentName != null) { parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); - + if (parentStyle != null) { outInheritanceMap.put(style, parentStyle); } @@ -589,7 +599,7 @@ public final class Bridge implements ILayoutBridge { } } } - + /** * Searches for and returns the {@link IStyleResourceValue} from a given name. * <p/>The format of the name can be: @@ -607,27 +617,27 @@ public final class Bridge implements ILayoutBridge { Map<String, IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap) { boolean frameworkOnly = false; - + String name = parentName; - + // remove the useless @ if it's there if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); } - + // check for framework identifier. if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) { frameworkOnly = true; name = name.substring(BridgeConstants.PREFIX_ANDROID.length()); } - + // at this point we could have the format style/<name>. we want only the name if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) { name = name.substring(BridgeConstants.REFERENCE_STYLE.length()); } IResourceValue parent = null; - + // if allowed, search in the project resources. if (frameworkOnly == false && inProjectStyleMap != null) { parent = inProjectStyleMap.get(name); @@ -637,17 +647,17 @@ public final class Bridge implements ILayoutBridge { if (parent == null) { parent = inFrameworkStyleMap.get(name); } - + // make sure the result is the proper class type and return it. if (parent instanceof IStyleResourceValue) { return (IStyleResourceValue)parent; } - + sLogger.error(String.format("Unable to resolve parent style name: ", parentName)); - + return null; } - + /** * Computes the name of the parent style, or <code>null</code> if the style is a root style. */ @@ -656,10 +666,10 @@ public final class Bridge implements ILayoutBridge { if (index != -1) { return styleName.substring(0, index); } - + return null; } - + /** * Returns the top screen offset. This depends on whether the current theme defines the user * of the title and status bars. @@ -670,7 +680,7 @@ public final class Bridge implements ILayoutBridge { // get the title bar flag from the current theme. IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle"); - + // because it may reference something else, we resolve it. value = context.resolveResValue(value); @@ -679,10 +689,10 @@ public final class Bridge implements ILayoutBridge { XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { // get value from the theme. value = context.findItemInStyle(currentTheme, "windowTitleSize"); - + // resolve it value = context.resolveResValue(value); - + // default value offset = DEFAULT_TITLE_BAR_HEIGHT; @@ -690,17 +700,17 @@ public final class Bridge implements ILayoutBridge { if (value != null) { TypedValue typedValue = ResourceHelper.getValue(value.getValue()); if (typedValue != null) { - offset = (int)typedValue.getDimension(context.getResources().mMetrics); + offset = (int)typedValue.getDimension(context.getResources().mMetrics); } } } - + // get the fullscreen flag from the current theme. value = context.findItemInStyle(currentTheme, "windowFullscreen"); - + // because it may reference something else, we resolve it. value = context.resolveResValue(value); - + if (value == null || value.getValue() == null || XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it. @@ -711,6 +721,94 @@ public final class Bridge implements ILayoutBridge { } /** + * Post process on a view hierachy that was just inflated. + * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the + * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically + * based on the content of the {@link FrameLayout}. + * @param view the root view to process. + * @param projectCallback callback to the project. + */ + private void postInflateProcess(View view, IProjectCallback projectCallback) + throws PostInflateException { + if (view instanceof TabHost) { + setupTabHost((TabHost)view, projectCallback); + } else if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup)view; + final int count = group.getChildCount(); + for (int c = 0 ; c < count ; c++) { + View child = group.getChildAt(c); + postInflateProcess(child, projectCallback); + } + } + } + + /** + * Sets up a {@link TabHost} object. + * @param tabHost the TabHost to setup. + * @param projectCallback The project callback object to access the project R class. + * @throws PostInflateException + */ + private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback) + throws PostInflateException { + // look for the TabWidget, and the FrameLayout. They have their own specific names + View v = tabHost.findViewById(android.R.id.tabs); + + if (v == null) { + throw new PostInflateException( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n"); + } + + if ((v instanceof TabWidget) == false) { + throw new PostInflateException(String.format( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n" + + "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName())); + } + + v = tabHost.findViewById(android.R.id.tabcontent); + + if (v == null) { + // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty) + throw new PostInflateException( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\"."); + } + + if ((v instanceof FrameLayout) == false) { + throw new PostInflateException(String.format( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" + + "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName())); + } + + FrameLayout content = (FrameLayout)v; + + // now process the content of the framelayout and dynamically create tabs for it. + final int count = content.getChildCount(); + + if (count == 0) { + throw new PostInflateException( + "The FrameLayout for the TabHost has no content. Rendering failed.\n"); + } + + // this must be called before addTab() so that the TabHost searches its TabWidget + // and FrameLayout. + tabHost.setup(); + + // for each child of the framelayout, add a new TabSpec + for (int i = 0 ; i < count ; i++) { + View child = content.getChildAt(i); + String tabSpec = String.format("tab_spec%d", i+1); + int id = child.getId(); + String[] resource = projectCallback.resolveResourceValue(id); + String name; + if (resource != null) { + name = resource[0]; // 0 is resource name, 1 is resource type. + } else { + name = String.format("Tab %d", i+1); // default name if id is unresolved. + } + tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id)); + } + } + + /** * Returns the bitmap for a specific path, from a specific project cache, or from the * framework cache. * @param value the path of the bitmap @@ -750,7 +848,7 @@ public final class Bridge implements ILayoutBridge { map = new HashMap<String, SoftReference<Bitmap>>(); sProjectBitmapCache.put(projectKey, map); } - + map.put(value, new SoftReference<Bitmap>(bmp)); } else { sFrameworkBitmapCache.put(value, new SoftReference<Bitmap>(bmp)); @@ -767,7 +865,7 @@ public final class Bridge implements ILayoutBridge { static NinePatch getCached9Patch(String value, Object projectKey) { if (projectKey != null) { Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); - + if (map != null) { SoftReference<NinePatch> ref = map.get(value); if (ref != null) { @@ -780,7 +878,7 @@ public final class Bridge implements ILayoutBridge { return ref.get(); } } - + return null; } @@ -798,13 +896,21 @@ public final class Bridge implements ILayoutBridge { map = new HashMap<String, SoftReference<NinePatch>>(); sProject9PatchCache.put(projectKey, map); } - + map.put(value, new SoftReference<NinePatch>(ninePatch)); } else { sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch)); } } + private static final class PostInflateException extends Exception { + private static final long serialVersionUID = 1L; + + public PostInflateException(String message) { + super(message); + } + } + /** * Implementation of {@link IWindowSession} so that mSession is not null in * the {@link SurfaceView}. @@ -839,7 +945,7 @@ public final class Bridge implements ILayoutBridge { // pass for now. return false; } - + @SuppressWarnings("unused") public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { // pass for now. @@ -863,7 +969,7 @@ public final class Bridge implements ILayoutBridge { public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { // pass for now. } - + @SuppressWarnings("unused") public void remove(IWindow arg0) throws RemoteException { // pass for now. @@ -883,13 +989,13 @@ public final class Bridge implements ILayoutBridge { Rect visibleInsets) { // pass for now. } - + public IBinder asBinder() { // pass for now. return null; } } - + /** * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. */ 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 1fa11af3f229..06dd96f8e628 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/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index f434e14e5968..69f3d9c9ca99 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -960,12 +961,24 @@ public final class BridgeContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override public String getPackageResourcePath() { // TODO Auto-generated method stub return null; } @Override + public File getSharedPrefsFile(String name) { + // TODO Auto-generated method stub + return null; + } + + @Override public SharedPreferences getSharedPreferences(String arg0, int arg1) { // TODO Auto-generated method stub return null; @@ -1145,12 +1158,4 @@ public final class BridgeContext extends Context { public Context getApplicationContext() { throw new UnsupportedOperationException(); } - - /** - * @hide - */ - @Override - public float getApplicationScale() { - throw new UnsupportedOperationException(); - } } diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk index 186177f53638..ab79f8dbdcfc 100644 --- a/tools/localize/Android.mk +++ b/tools/localize/Android.mk @@ -53,4 +53,3 @@ ifeq (a,a) endif include $(BUILD_HOST_EXECUTABLE) - diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp index 3425668cbdc1..1c644ede8ea8 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 9d7c5c65447e..2533f0a2650e 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 e396f8b4897f..8623b97b3134 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 51f81de1facb..4e217d9c3d2f 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 bb82a9c55f14..293e50e10bbb 100644 --- a/tools/localize/file_utils.cpp +++ b/tools/localize/file_utils.cpp @@ -1,14 +1,49 @@ #include <string.h> +#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "file_utils.h" #include "Perforce.h" +#include <utils/String8.h> #include <sys/fcntl.h> #include <sys/stat.h> #include <errno.h> -#include <host/Directories.h> #include "log.h" +using namespace android; +using namespace std; + +static string +parent_dir(const string& path) +{ + return string(String8(path.c_str()).getPathDir().string()); +} + +static int +mkdirs(const char* last) +{ + String8 dest; + const char* s = last-1; + int err; + do { + s++; + if (s > last && (*s == '.' || *s == 0)) { + String8 part(last, s-last); + dest.appendPath(part); +#ifdef HAVE_MS_C_RUNTIME + err = _mkdir(dest.string()); +#else + err = mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); +#endif + if (err != 0) { + return err; + } + last = s+1; + } + } while (*s); + return 0; +} + string translated_file_name(const string& file, const string& locale) { diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp index 63d904c388bf..678cad8d87e3 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 58a6554177ae..1fdaa0e7df53 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 5a2b0f423214..e4ab562c1583 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 236705f186d9..d8f6ff0728b5 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 new file mode 100644 index 000000000000..fedccb0d8795 --- /dev/null +++ b/vpn/java/android/net/vpn/IVpnService.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +import android.net.vpn.VpnProfile; + +/** + * Interface to access a VPN service. + * {@hide} + */ +interface IVpnService { + /** + * Sets up the VPN connection. + * @param profile the profile object + * @param username the username for authentication + * @param password the corresponding password for authentication + */ + boolean connect(in VpnProfile profile, String username, String password); + + /** + * Tears down the VPN connection. + */ + void disconnect(); + + /** + * Makes the service broadcast the connectivity state. + */ + void checkStatus(in VpnProfile profile); +} diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java new file mode 100644 index 000000000000..4ae2dec54865 --- /dev/null +++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +import android.os.Parcel; + +/** + * The profile for certificate-based L2TP-over-IPSec type of VPN. + * {@hide} + */ +public class L2tpIpsecProfile extends L2tpProfile { + private static final long serialVersionUID = 1L; + + private String mUserCertificate; + private String mCaCertificate; + + @Override + public VpnType getType() { + return VpnType.L2TP_IPSEC; + } + + public void setCaCertificate(String name) { + mCaCertificate = name; + } + + public String getCaCertificate() { + return mCaCertificate; + } + + public void setUserCertificate(String name) { + mUserCertificate = name; + } + + public String getUserCertificate() { + return mUserCertificate; + } + + @Override + protected void readFromParcel(Parcel in) { + super.readFromParcel(in); + mCaCertificate = in.readString(); + mUserCertificate = in.readString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeString(mCaCertificate); + parcel.writeString(mUserCertificate); + } +} diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java new file mode 100644 index 000000000000..7a03018d279b --- /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 new file mode 100644 index 000000000000..dbba0c500f6f --- /dev/null +++ b/vpn/java/android/net/vpn/L2tpProfile.java @@ -0,0 +1,68 @@ +/* + * 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 L2TP type of VPN. + * {@hide} + */ +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 000000000000..c68bb71f1a4e --- /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 new file mode 100644 index 000000000000..dc70b264eacf --- /dev/null +++ b/vpn/java/android/net/vpn/VpnManager.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.util.Log; + +/** + * The class provides interface to manage all VPN-related tasks, including: + * <ul> + * <li>The list of supported VPN types. + * <li>API's to start/stop the service of a particular type. + * <li>API's to start the settings activity. + * <li>API's to create a profile. + * <li>API's to register/unregister a connectivity receiver and the keys to + * access the fields in a connectivity broadcast event. + * </ul> + * {@hide} + */ +public class VpnManager { + // Action for broadcasting a connectivity state. + private static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity"; + /** Key to the profile name of a connectivity broadcast event. */ + public static final String BROADCAST_PROFILE_NAME = "profile_name"; + /** Key to the connectivity state of a connectivity broadcast event. */ + public static final String BROADCAST_CONNECTION_STATE = "connection_state"; + + public static final String PROFILES_PATH = "/data/misc/vpn/profiles"; + + private static final String PACKAGE_PREFIX = + VpnManager.class.getPackage().getName() + "."; + + // Action to start VPN service + private static final String ACTION_VPN_SERVICE = PACKAGE_PREFIX + "SERVICE"; + + // Action to start VPN settings + private static final String ACTION_VPN_SETTINGS = PACKAGE_PREFIX + "SETTINGS"; + + private static final String TAG = VpnManager.class.getSimpleName(); + + /** + * Returns all supported VPN types. + */ + public static VpnType[] getSupportedVpnTypes() { + return VpnType.values(); + } + + private Context mContext; + + /** + * Creates a manager object with the specified context. + */ + public VpnManager(Context c) { + mContext = c; + } + + /** + * Creates a VPN profile of the specified type. + * + * @param type the VPN type + * @return the profile object + */ + public VpnProfile createVpnProfile(VpnType type) { + return createVpnProfile(type, false); + } + + /** + * Creates a VPN profile of the specified type. + * + * @param type the VPN type + * @param customized true if the profile is custom made + * @return the profile object + */ + public VpnProfile createVpnProfile(VpnType type, boolean customized) { + try { + VpnProfile p = (VpnProfile) type.getProfileClass().newInstance(); + p.setCustomized(customized); + return p; + } catch (InstantiationException e) { + return null; + } catch (IllegalAccessException e) { + return null; + } + } + + /** + * Starts the VPN service to establish VPN connection. + */ + public void startVpnService() { + mContext.startService(new Intent(ACTION_VPN_SERVICE)); + } + + /** + * Stops the VPN service. + */ + public void stopVpnService() { + mContext.stopService(new Intent(ACTION_VPN_SERVICE)); + } + + /** + * Binds the specified ServiceConnection with the VPN service. + */ + public boolean bindVpnService(ServiceConnection c) { + if (!mContext.bindService(new Intent(ACTION_VPN_SERVICE), c, 0)) { + Log.w(TAG, "failed to connect to VPN service"); + return false; + } else { + Log.d(TAG, "succeeded to connect to VPN service"); + return true; + } + } + + /** Broadcasts the connectivity state of the specified profile. */ + public void broadcastConnectivity(String profileName, VpnState s) { + Intent intent = new Intent(ACTION_VPN_CONNECTIVITY); + intent.putExtra(BROADCAST_PROFILE_NAME, profileName); + intent.putExtra(BROADCAST_CONNECTION_STATE, s); + mContext.sendBroadcast(intent); + } + + public void registerConnectivityReceiver(BroadcastReceiver r) { + IntentFilter filter = new IntentFilter(); + filter.addAction(VpnManager.ACTION_VPN_CONNECTIVITY); + mContext.registerReceiver(r, filter); + } + + public void unregisterConnectivityReceiver(BroadcastReceiver r) { + mContext.unregisterReceiver(r); + } + + /** Starts the VPN settings activity. */ + public void startSettingsActivity() { + Intent intent = new Intent(ACTION_VPN_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + /** Creates an intent to start the VPN settings activity. */ + public Intent createSettingsActivityIntent() { + Intent intent = new Intent(ACTION_VPN_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } +} diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl new file mode 100644 index 000000000000..edeaef0d64fd --- /dev/null +++ b/vpn/java/android/net/vpn/VpnProfile.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable VpnProfile; diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java new file mode 100644 index 000000000000..bd6c80902ca0 --- /dev/null +++ b/vpn/java/android/net/vpn/VpnProfile.java @@ -0,0 +1,177 @@ +/* + * 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.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; +import java.io.Serializable; + +/** + * A VPN profile. + * {@hide} + */ +public abstract class VpnProfile implements Parcelable, Serializable { + private static final long serialVersionUID = 1L; + private String mName; // unique display name + private String mId; // unique identifier + private String mServerName; // VPN server name + private String mDomainSuffices; // space separated list + private String mRouteList; // space separated list + private String mSavedUsername; + private boolean mIsCustomized; + private transient VpnState mState = VpnState.IDLE; + + /** Sets a user-friendly name for this profile. */ + public void setName(String name) { + mName = name; + } + + public String getName() { + return mName; + } + + /** + * Sets an ID for this profile. The caller should make sure the + * uniqueness of the ID. + */ + public void setId(String id) { + mId = id; + } + + public String getId() { + return mId; + } + + /** + * Sets the name of the VPN server. Used for DNS lookup. + */ + public void setServerName(String name) { + mServerName = name; + } + + public String getServerName() { + return mServerName; + } + + /** + * Sets the domain suffices for DNS resolution. + * + * @param entries a comma-separated list of domain suffices + */ + public void setDomainSuffices(String entries) { + mDomainSuffices = entries; + } + + public String getDomainSuffices() { + return mDomainSuffices; + } + + /** + * Sets the routing info for this VPN connection. + * + * @param entries a comma-separated list of routes; each entry is in the + * format of "(network address)/(network mask)" + */ + public void setRouteList(String entries) { + mRouteList = entries; + } + + public String getRouteList() { + return mRouteList; + } + + public void setSavedUsername(String name) { + mSavedUsername = name; + } + + public String getSavedUsername() { + return mSavedUsername; + } + + public void setState(VpnState state) { + mState = state; + } + + public VpnState getState() { + return ((mState == null) ? VpnState.IDLE : mState); + } + + public boolean isIdle() { + return (mState == VpnState.IDLE); + } + + /** + * Returns whether this profile is custom made (as opposed to being + * created by provided user interface). + */ + public boolean isCustomized() { + return mIsCustomized; + } + + /** + * Returns the VPN type of the profile. + */ + public abstract VpnType getType(); + + void setCustomized(boolean customized) { + mIsCustomized = customized; + } + + protected void readFromParcel(Parcel in) { + mName = in.readString(); + mId = in.readString(); + mServerName = in.readString(); + mDomainSuffices = in.readString(); + mRouteList = in.readString(); + mSavedUsername = in.readString(); + } + + public static final Parcelable.Creator<VpnProfile> CREATOR = + new Parcelable.Creator<VpnProfile>() { + public VpnProfile createFromParcel(Parcel in) { + VpnType type = Enum.valueOf(VpnType.class, in.readString()); + boolean customized = in.readInt() > 0; + VpnProfile p = new VpnManager(null).createVpnProfile(type, + customized); + if (p == null) return null; + p.readFromParcel(in); + return p; + } + + public VpnProfile[] newArray(int size) { + return new VpnProfile[size]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(getType().toString()); + parcel.writeInt(mIsCustomized ? 1 : 0); + parcel.writeString(mName); + parcel.writeString(mId); + parcel.writeString(mServerName); + parcel.writeString(mDomainSuffices); + parcel.writeString(mRouteList); + parcel.writeString(mSavedUsername); + } + + public int describeContents() { + return 0; + } +} diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java new file mode 100644 index 000000000000..ebd936454f88 --- /dev/null +++ b/vpn/java/android/net/vpn/VpnState.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +/** + * Enumeration of all VPN states. + * + * A normal VPN connection lifetime starts in {@link IDLE}. When a new + * connection is about to be set up, it goes to {@link CONNECTING} and then + * {@link CONNECTED} if successful; back to {@link IDLE} if failed. + * When the connection is about to be torn down, it goes to + * {@link DISCONNECTING} and then {@link IDLE}. + * {@link CANCELLED} is a state when a VPN connection attempt is aborted, and + * is in transition to {@link IDLE}. + * {@hide} + */ +public enum VpnState { + CONNECTING, DISCONNECTING, CANCELLED, CONNECTED, IDLE +} diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java new file mode 100644 index 000000000000..c7df9438c751 --- /dev/null +++ b/vpn/java/android/net/vpn/VpnType.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +/** + * Enumeration of all supported VPN types. + * {@hide} + */ +public enum VpnType { + 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, String description, + Class<? extends VpnProfile> klass) { + mDisplayName = displayName; + mDescription = description; + mClass = klass; + } + + public String getDisplayName() { + return mDisplayName; + } + + public String getDescription() { + return mDescription; + } + + public Class<? extends VpnProfile> getProfileClass() { + return mClass; + } +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 00829d6deedc..3d65d3c66f38 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -70,10 +70,10 @@ interface IWifiManager boolean releaseWifiLock(IBinder lock); - boolean isWifiMulticastEnabled(); + boolean isMulticastEnabled(); - void enableWifiMulticast(IBinder binder, String tag); + void acquireMulticastLock(IBinder binder, String tag); - void disableWifiMulticast(); + void releaseMulticastLock(); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 3bee3b6f90a2..eda2f2d49672 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -42,13 +42,29 @@ public class WifiConfiguration implements Parcelable { public static final String priorityVarName = "priority"; /** {@hide} */ public static final String hiddenSSIDVarName = "scan_ssid"; + /** {@hide} */ + public static final String eapVarName = "eap"; + /** {@hide} */ + public static final String identityVarName = "identity"; + /** {@hide} */ + public static final String anonymousIdentityVarName = "anonymous_identity"; + /** {@hide} */ + public static final String passwordVarName = "password"; + /** {@hide} */ + public static final String clientCertVarName = "client_cert"; + /** {@hide} */ + public static final String caCertVarName = "ca_cert"; + /** {@hide} */ + public static final String privateKeyVarName = "private_key"; + /** {@hide} */ + public static final String privateKeyPasswdVarName = "private_key_passwd"; /** * Recognized key management schemes. */ public static class KeyMgmt { private KeyMgmt() { } - + /** WPA is not used; plaintext or static WEP could be used. */ public static final int NONE = 0; /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */ @@ -63,7 +79,7 @@ public class WifiConfiguration implements Parcelable { public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X" }; } - + /** * Recognized security protocols. */ @@ -112,7 +128,7 @@ public class WifiConfiguration implements Parcelable { public static final int CCMP = 2; public static final String varName = "pairwise"; - + public static final String[] strings = { "NONE", "TKIP", "CCMP" }; } @@ -202,7 +218,7 @@ public class WifiConfiguration implements Parcelable { * string otherwise. */ public String[] wepKeys; - + /** Default WEP key index, ranging from 0 to 3. */ public int wepTxKeyIndex; @@ -249,6 +265,40 @@ public class WifiConfiguration implements Parcelable { */ public BitSet allowedGroupCiphers; + /* The following fields are used for EAP/IEEE8021X authentication */ + + /** + * The eap mode should be PEAP, TLS or TTLS. + * {@hide} + */ + public String eap; + /** + * The identity of the user in string, + * which is used for the authentication. + * {@hide} + */ + public String identity; + /** {@hide} */ + public String anonymousIdentity; + /** {@hide} */ + public String password; + /** The path of the client certificate file. + * {@hide} + */ + public String clientCert; + /** The path of the CA certificate file. + * {@hide} + */ + public String caCert; + /** The path of the private key file. + * {@hide} + */ + public String privateKey; + /** The password of the private key file if encrypted. + * {@hide} + */ + public String privateKeyPasswd; + public WifiConfiguration() { networkId = -1; SSID = null; @@ -263,6 +313,14 @@ public class WifiConfiguration implements Parcelable { wepKeys = new String[4]; for (int i = 0; i < wepKeys.length; i++) wepKeys[i] = null; + eap = null; + identity = null; + anonymousIdentity = null; + password = null; + clientCert = null; + caCert = null; + privateKey = null; + privateKeyPasswd = null; } public String toString() { @@ -333,10 +391,43 @@ public class WifiConfiguration implements Parcelable { } } } - sbuf.append('\n'); + sbuf.append('\n').append(" PSK: "); if (this.preSharedKey != null) { - sbuf.append(" PSK: ").append('*'); + sbuf.append('*'); + } + sbuf.append('\n').append(" eap: "); + if (this.eap != null) { + sbuf.append(eap); } + sbuf.append('\n').append(" Identity: "); + if (this.identity != null) { + sbuf.append(identity); + } + sbuf.append('\n').append(" AnonymousIdentity: "); + if (this.anonymousIdentity != null) { + sbuf.append(anonymousIdentity); + } + sbuf.append('\n').append(" Password: "); + if (this.password != null) { + sbuf.append(password); + } + sbuf.append('\n').append(" ClientCert: "); + if (this.clientCert != null) { + sbuf.append(clientCert); + } + sbuf.append('\n').append(" CaCert: "); + if (this.caCert != null) { + sbuf.append(caCert); + } + sbuf.append('\n').append(" PrivateKey: "); + if (this.privateKey != null) { + sbuf.append(privateKey); + } + sbuf.append('\n').append(" PrivateKeyPasswd: "); + if (this.privateKeyPasswd != null) { + sbuf.append(privateKeyPasswd); + } + sbuf.append('\n'); return sbuf.toString(); } @@ -394,6 +485,14 @@ public class WifiConfiguration implements Parcelable { writeBitSet(dest, allowedAuthAlgorithms); writeBitSet(dest, allowedPairwiseCiphers); writeBitSet(dest, allowedGroupCiphers); + dest.writeString(eap); + dest.writeString(identity); + dest.writeString(anonymousIdentity); + dest.writeString(password); + dest.writeString(clientCert); + dest.writeString(caCert); + dest.writeString(privateKey); + dest.writeString(privateKeyPasswd); } /** Implement the Parcelable interface {@hide} */ @@ -416,6 +515,14 @@ public class WifiConfiguration implements Parcelable { config.allowedAuthAlgorithms = readBitSet(in); config.allowedPairwiseCiphers = readBitSet(in); config.allowedGroupCiphers = readBitSet(in); + config.eap = in.readString(); + config.identity = in.readString(); + config.anonymousIdentity = in.readString(); + config.password = in.readString(); + config.clientCert = in.readString(); + config.caCert = in.readString(); + config.privateKey = in.readString(); + config.privateKeyPasswd = in.readString(); return config; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 658a7b24805b..7a15f273d140 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -253,6 +253,15 @@ public class WifiManager { IWifiManager mService; Handler mHandler; + /* Maximum number of active locks we allow. + * This limit was added to prevent apps from creating a ridiculous number + * of locks and crashing the system by overflowing the global ref table. + */ + private static final int MAX_ACTIVE_LOCKS = 50; + + /* Number of currently active WifiLocks and MulticastLocks */ + private int mActiveLockCount; + /** * Create a new WifiManager instance. * Applications will almost always want to use @@ -702,6 +711,14 @@ public class WifiManager { if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) { try { mService.acquireWifiLock(mBinder, mLockType, mTag); + synchronized (WifiManager.this) { + if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { + mService.releaseWifiLock(mBinder); + throw new UnsupportedOperationException( + "Exceeded maximum number of wifi locks"); + } + mActiveLockCount++; + } } catch (RemoteException ignore) { } mHeld = true; @@ -726,6 +743,9 @@ public class WifiManager { if (mRefCounted ? (--mRefCount == 0) : (mHeld)) { try { mService.releaseWifiLock(mBinder); + synchronized (WifiManager.this) { + mActiveLockCount--; + } } catch (RemoteException ignore) { } mHeld = false; @@ -783,6 +803,9 @@ public class WifiManager { if (mHeld) { try { mService.releaseWifiLock(mBinder); + synchronized (WifiManager.this) { + mActiveLockCount--; + } } catch (RemoteException ignore) { } } @@ -824,62 +847,138 @@ public class WifiManager { return new WifiLock(WIFI_MODE_FULL, tag); } + /** - * Check multicast filter status. + * Create a new MulticastLock * - * @return true if multicast packets are allowed. + * @param tag a tag for the MulticastLock to identify it in debugging + * messages. * - * @hide pending API council approval + * @return a new, unacquired MulticastLock with the given tag. + * + * @see MulticastLock */ - public boolean isWifiMulticastEnabled() { - try { - return mService.isWifiMulticastEnabled(); - } catch (RemoteException e) { - return false; - } + public MulticastLock createMulticastLock(String tag) { + return new MulticastLock(tag); } /** - * Turn on the reception of multicast packets. - * The default behavior is to disable multicast packets as they - * have a noticable negative effect on battery life. An - * application can turn them on, but should not leave it on for longer - * than needed. When the app quits (or crashes) its request will - * be reverted. - * - * @param tag a string associated with this request for debugging. - * - * @return true on success - * - * @see #disableWifiMulticast - * - * @hide pending API council approval + * Allows an application to receive Wifi Multicast packets. + * Normally the Wifi stack filters out packets not explicitly + * addressed to this device. Acquring a MulticastLock will + * cause the stack to receive packets addressed to multicast + * addresses. Processing these extra packets can cause a noticable + * battery drain and should be disabled when not needed */ - public boolean enableWifiMulticast(String tag) { - try { - mService.enableWifiMulticast(new Binder(), tag); - return true; - } catch (RemoteException e) { - return false; + public class MulticastLock { + private String mTag; + private final IBinder mBinder; + private boolean mHeld; + + private MulticastLock(String tag) { + mTag = tag; + mBinder = new Binder(); + mHeld = false; + } + + /** + * Locks Wifi Multicast on until {@link #release} is called. + * + * The first call to {@code acquire} will lock the Multicast on + * but subsequent calls will be ignored. Only one call to + * {@link #release} will be required, regardless of the number of + * times that {@code acquire} is called. + * + * Note that other applications may also lock Wifi Multicast on. + * Only they can relinquish their lock. + * + * Also note that applications cannot leave Multicast locked on. + * When an app exits or crashes, any Multicast locks will be released. + */ + public void acquire() { + synchronized (mBinder) { + if (!mHeld) { + try { + mService.acquireMulticastLock(mBinder, mTag); + synchronized (WifiManager.this) { + if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { + mService.releaseMulticastLock(); + throw new UnsupportedOperationException( + "Exceeded maximum number of wifi locks"); + } + mActiveLockCount++; + } + mHeld = true; + } catch (RemoteException ignore) { + } + } + } + } + + /** + * Unlocks Wifi Multicast, restoring the filter of packets + * not addressed specifically to this device and saving power. + * + * Note that if any other Wifi Multicast Locks are still outstanding + * this {@code release} call will not have an immediate effect. Only + * when all applications have released all their Multicast Locks will + * the Multicast filter be turned back on. + * + * Also note that when an app exits or crashes all of its Multicast + * Locks will be automatically released. + */ + public void release() { + synchronized (mBinder) { + if (mHeld) { + try { + mService.releaseMulticastLock(); + synchronized (WifiManager.this) { + mActiveLockCount--; + } + mHeld = false; + } catch (RemoteException ignore) { + } + } + } + } + + /** + * Checks whether this MulticastLock is currently held. + * + * @return true if this MulticastLock is held, false otherwise + */ + public boolean isHeld() { + synchronized (mBinder) { + return mHeld; + } + } + + public String toString() { + String s1, s2; + synchronized (mBinder) { + s1 = Integer.toHexString(System.identityHashCode(this)); + s2 = mHeld ? "held; " : ""; + return "MulticastLock{ " + s1 + "; " + s2 + " }"; + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + release(); } } /** - * Return to the default multicast-off setting. - * Note that if others had turned on Multicast reception, your - * call will not turn it back off - they must also turn off their - * request for multicast reception. - * - * @return true on success + * Check multicast filter status. * - * @see #enableWifiMulticast + * @return true if multicast packets are allowed. * * @hide pending API council approval */ - public boolean disableWifiMulticast() { + public boolean isMulticastEnabled() { try { - mService.disableWifiMulticast(); - return true; + return mService.isMulticastEnabled(); } catch (RemoteException e) { return false; } diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 64084cf403c3..2fbc77950358 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -161,8 +161,8 @@ public class WifiStateTracker extends NetworkStateTracker { private WifiInfo mWifiInfo; private List<ScanResult> mScanResults; private WifiManager mWM; - private boolean mHaveIPAddress; - private boolean mObtainingIPAddress; + private boolean mHaveIpAddress; + private boolean mObtainingIpAddress; private boolean mTornDownByConnMgr; /** * A DISCONNECT event has been received, but processing it @@ -303,8 +303,8 @@ public class WifiStateTracker extends NetworkStateTracker { mWifiInfo = new WifiInfo(); mWifiMonitor = new WifiMonitor(this); - mHaveIPAddress = false; - mObtainingIPAddress = false; + mHaveIpAddress = false; + mObtainingIpAddress = false; setTornDownByConnMgr(false); mDisconnectPending = false; mScanResults = new ArrayList<ScanResult>(); @@ -444,6 +444,14 @@ public class WifiStateTracker extends NetworkStateTracker { } /** + * Report whether the Wi-Fi connection has successfully acquired an IP address. + * @return {@code true} if the Wi-Fi connection has been assigned an IP address. + */ + public boolean hasIpAddress() { + return mHaveIpAddress; + } + + /** * Send the tracker a notification that a user-entered password key * may be incorrect (i.e., caused authentication to fail). */ @@ -724,7 +732,7 @@ public class WifiStateTracker extends NetworkStateTracker { intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true); mContext.sendBroadcast(intent); } - if (supplState == SupplicantState.COMPLETED && mHaveIPAddress) { + if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) { setDetailedState(DetailedState.CONNECTED); } else { setDetailedState(WifiInfo.getDetailedStateOf(supplState)); @@ -739,8 +747,10 @@ public class WifiStateTracker extends NetworkStateTracker { * first and then off.. if nobody else wants it on it'll be * off then and it's all synchronized within the API. */ - mWM.enableWifiMulticast("WifiStateTracker"); - mWM.disableWifiMulticast(); + WifiManager.MulticastLock l = + mWM.createMulticastLock("WifiStateTracker"); + l.acquire(); + l.release(); if (mBluetoothA2dp == null) { mBluetoothA2dp = new BluetoothA2dp(mContext); @@ -783,8 +793,8 @@ public class WifiStateTracker extends NetworkStateTracker { } setDetailedState(DetailedState.DISCONNECTED); setSupplicantState(SupplicantState.UNINITIALIZED); - mHaveIPAddress = false; - mObtainingIPAddress = false; + mHaveIpAddress = false; + mObtainingIpAddress = false; if (died) { mWM.setWifiEnabled(false); } @@ -954,7 +964,7 @@ public class WifiStateTracker extends NetworkStateTracker { } requestConnectionStatus(mWifiInfo); if (!(result.state == DetailedState.CONNECTED && - (!mHaveIPAddress || mDisconnectPending))) { + (!mHaveIpAddress || mDisconnectPending))) { setDetailedState(result.state); } @@ -983,7 +993,7 @@ public class WifiStateTracker extends NetworkStateTracker { mLastBssid = result.BSSID; mLastSsid = mWifiInfo.getSSID(); mLastNetworkId = result.networkId; - if (mHaveIPAddress) { + if (mHaveIpAddress) { setDetailedState(DetailedState.CONNECTED); } else { setDetailedState(DetailedState.OBTAINING_IPADDR); @@ -1051,8 +1061,8 @@ public class WifiStateTracker extends NetworkStateTracker { break; } mReconnectCount = 0; - mHaveIPAddress = true; - mObtainingIPAddress = false; + mHaveIpAddress = true; + mObtainingIpAddress = false; mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); mLastSignalLevel = -1; // force update of signal strength if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) { @@ -1078,9 +1088,9 @@ public class WifiStateTracker extends NetworkStateTracker { // [ 0- 0] Interface configuration succeeded (1) or failed (0) EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0); - mHaveIPAddress = false; + mHaveIpAddress = false; mWifiInfo.setIpAddress(0); - mObtainingIPAddress = false; + mObtainingIpAddress = false; synchronized(this) { WifiNative.disconnectCommand(); } @@ -1156,18 +1166,18 @@ public class WifiStateTracker extends NetworkStateTracker { setPollTimer(); mLastSignalLevel = -1; if (!mUseStaticIp) { - if (!mHaveIPAddress && !mObtainingIPAddress) { - mObtainingIPAddress = true; + if (!mHaveIpAddress && !mObtainingIpAddress) { + mObtainingIpAddress = true; mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START); } } else { int event; if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { - mHaveIPAddress = true; + mHaveIpAddress = true; event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded"); } else { - mHaveIPAddress = false; + mHaveIpAddress = false; event = EVENT_INTERFACE_CONFIGURATION_FAILED; if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed"); } @@ -1200,8 +1210,8 @@ public class WifiStateTracker extends NetworkStateTracker { * using the interface, stopping DHCP, and disabling the interface. */ public void resetInterface() { - mHaveIPAddress = false; - mObtainingIPAddress = false; + mHaveIpAddress = false; + mObtainingIpAddress = false; mWifiInfo.setIpAddress(0); /* @@ -1612,8 +1622,8 @@ public class WifiStateTracker extends NetworkStateTracker { } sb.append(LS).append(mWifiInfo).append(LS); sb.append(mDhcpInfo).append(LS); - sb.append("haveIpAddress=").append(mHaveIPAddress). - append(", obtainingIpAddress=").append(mObtainingIPAddress). + sb.append("haveIpAddress=").append(mHaveIpAddress). + append(", obtainingIpAddress=").append(mObtainingIpAddress). append(", scanModeActive=").append(mIsScanModeActive).append(LS). append("lastSignalLevel=").append(mLastSignalLevel). append(", explicitlyDisabled=").append(mTornDownByConnMgr); |