diff options
250 files changed, 7354 insertions, 3341 deletions
@@ -33,6 +33,9 @@ include $(CLEAR_VARS) # FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk LOCAL_SRC_FILES := $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS)) +# EventLogTags files. +LOCAL_SRC_FILES += core/java/android/content/EventLogTags.logtags + # The following filters out code we are temporarily not including at all. # TODO: Move AWT and beans (and associated harmony code) back into libcore. # TODO: Maybe remove javax.microedition entirely? @@ -111,8 +114,8 @@ LOCAL_SRC_FILES += \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/os/ICheckinService.aidl \ core/java/android/os/IMessenger.aidl \ - core/java/android/os/IMountService.aidl \ - core/java/android/os/IMountServiceListener.aidl \ + core/java/android/os/storage/IMountService.aidl \ + core/java/android/os/storage/IMountServiceListener.aidl \ core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetStatService.aidl \ core/java/android/os/IParentalControlCallback.aidl \ @@ -367,6 +370,8 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBR sample_dir := development/samples +# the list here should match the list of samples included in the sdk samples package +# (see development/build/sdk.atree) web_docs_sample_code_flags := \ -hdf android.hasSamples 1 \ -samplecode $(sample_dir)/ApiDemos \ @@ -377,6 +382,8 @@ web_docs_sample_code_flags := \ resources/samples/BusinessCard "Business Card" \ -samplecode $(sample_dir)/ContactManager \ resources/samples/ContactManager "Contact Manager" \ + -samplecode $(sample_dir)/CubeLiveWallpaper \ + resources/samples/CubeLiveWallpaper "Live Wallpaper" \ -samplecode $(sample_dir)/Home \ resources/samples/Home "Home" \ -samplecode $(sample_dir)/JetBoy \ @@ -387,6 +394,8 @@ web_docs_sample_code_flags := \ resources/samples/MultiResolution "Multiple Resolutions" \ -samplecode $(sample_dir)/NotePad \ resources/samples/NotePad "Note Pad" \ + -samplecode $(sample_dir)/SampleSyncAdapter \ + resources/samples/SampleSyncAdapter "Sample Sync Adapter" \ -samplecode $(sample_dir)/SearchableDictionary \ resources/samples/SearchableDictionary "Searchable Dictionary" \ -samplecode $(sample_dir)/Snake \ diff --git a/api/current.xml b/api/current.xml index 16ed39b..31ab3ce 100644 --- a/api/current.xml +++ b/api/current.xml @@ -29824,6 +29824,8 @@ deprecated="not deprecated" visibility="public" > +<implements name="java.lang.Cloneable"> +</implements> <implements name="java.lang.Comparable"> </implements> <implements name="android.os.Parcelable"> @@ -29874,6 +29876,17 @@ <parameter name="in" type="android.os.Parcel"> </parameter> </constructor> +<method name="clone" + return="android.content.ComponentName" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="compareTo" return="int" abstract="false" @@ -31727,6 +31740,17 @@ visibility="public" > </field> +<field name="SYNC_EXTRAS_DO_NOT_RETRY" + type="java.lang.String" + transient="false" + volatile="false" + value=""do_not_retry"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SYNC_EXTRAS_EXPEDITED" type="java.lang.String" transient="false" @@ -31749,6 +31773,28 @@ visibility="public" > </field> +<field name="SYNC_EXTRAS_IGNORE_BACKOFF" + type="java.lang.String" + transient="false" + volatile="false" + value=""ignore_backoff"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SYNC_EXTRAS_IGNORE_SETTINGS" + type="java.lang.String" + transient="false" + volatile="false" + value=""ignore_settings"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SYNC_EXTRAS_INITIALIZE" type="java.lang.String" transient="false" @@ -32739,6 +32785,30 @@ <parameter name="mode" type="int"> </parameter> </method> +<method name="getExternalCacheDir" + return="java.io.File" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getExternalFilesDir" + return="java.io.File" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="type" type="java.lang.String"> +</parameter> +</method> <method name="getFileStreamPath" return="java.io.File" abstract="true" @@ -34247,6 +34317,30 @@ <parameter name="mode" type="int"> </parameter> </method> +<method name="getExternalCacheDir" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getExternalFilesDir" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="type" type="java.lang.String"> +</parameter> +</method> <method name="getFileStreamPath" return="java.io.File" abstract="false" @@ -41850,17 +41944,6 @@ visibility="public" > </field> -<field name="resourceDirs" - type="java.lang.String[]" - transient="false" - volatile="false" - value="null" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="sharedLibraryFiles" type="java.lang.String[]" transient="false" @@ -53070,40 +53153,40 @@ <parameter name="sleepAfterYieldDelay" type="long"> </parameter> </method> -<field name="CREATE_IF_NECESSARY" +<field name="CONFLICT_ABORT" type="int" transient="false" volatile="false" - value="268435456" + value="2" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="NO_LOCALIZED_COLLATORS" +<field name="CONFLICT_FAIL" type="int" transient="false" volatile="false" - value="16" + value="3" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="OPEN_READONLY" +<field name="CONFLICT_IGNORE" type="int" transient="false" volatile="false" - value="1" + value="4" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="OPEN_READWRITE" +<field name="CONFLICT_NONE" type="int" transient="false" volatile="false" @@ -53114,86 +53197,77 @@ visibility="public" > </field> -<field name="SQLITE_MAX_LIKE_PATTERN_LENGTH" +<field name="CONFLICT_REPLACE" type="int" transient="false" volatile="false" - value="50000" + value="5" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -</class> -<class name="SQLiteDatabase.ConflictAlgorithm" - extends="java.lang.Object" - abstract="false" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -<field name="ABORT" +<field name="CONFLICT_ROLLBACK" type="int" transient="false" volatile="false" - value="2" + value="1" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FAIL" +<field name="CREATE_IF_NECESSARY" type="int" transient="false" volatile="false" - value="3" + value="268435456" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="IGNORE" +<field name="NO_LOCALIZED_COLLATORS" type="int" transient="false" volatile="false" - value="4" + value="16" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="NONE" +<field name="OPEN_READONLY" type="int" transient="false" volatile="false" - value="0" + value="1" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="REPLACE" +<field name="OPEN_READWRITE" type="int" transient="false" volatile="false" - value="5" + value="0" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="ROLLBACK" +<field name="SQLITE_MAX_LIKE_PATTERN_LENGTH" type="int" transient="false" volatile="false" - value="1" + value="50000" static="true" final="true" deprecated="not deprecated" @@ -77577,32 +77651,6 @@ <parameter name="enabledOnly" type="boolean"> </parameter> </method> -<method name="installGeocodeProvider" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="provider" type="android.location.LocationManager.GeocodeProvider"> -</parameter> -</method> -<method name="installLocationProvider" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="provider" type="android.location.LocationProviderImpl"> -</parameter> -</method> <method name="isProviderEnabled" return="boolean" abstract="false" @@ -77694,19 +77742,6 @@ <parameter name="intent" type="android.app.PendingIntent"> </parameter> </method> -<method name="reportLocation" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="location" type="android.location.Location"> -</parameter> -</method> <method name="requestLocationUpdates" return="void" abstract="false" @@ -77899,62 +77934,6 @@ > </field> </class> -<interface name="LocationManager.GeocodeProvider" - abstract="true" - static="true" - final="false" - deprecated="not deprecated" - visibility="public" -> -<method name="getFromLocation" - return="java.lang.String" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="latitude" type="double"> -</parameter> -<parameter name="longitude" type="double"> -</parameter> -<parameter name="maxResults" type="int"> -</parameter> -<parameter name="params" type="android.location.GeocoderParams"> -</parameter> -<parameter name="addrs" type="java.util.List<android.location.Address>"> -</parameter> -</method> -<method name="getFromLocationName" - return="java.lang.String" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="locationName" type="java.lang.String"> -</parameter> -<parameter name="lowerLeftLatitude" type="double"> -</parameter> -<parameter name="lowerLeftLongitude" type="double"> -</parameter> -<parameter name="upperRightLatitude" type="double"> -</parameter> -<parameter name="upperRightLongitude" type="double"> -</parameter> -<parameter name="maxResults" type="int"> -</parameter> -<parameter name="params" type="android.location.GeocoderParams"> -</parameter> -<parameter name="addrs" type="java.util.List<android.location.Address>"> -</parameter> -</method> -</interface> <class name="LocationProvider" extends="java.lang.Object" abstract="true" @@ -78120,166 +78099,6 @@ > </field> </class> -<class name="LocationProviderImpl" - extends="android.location.LocationProvider" - abstract="true" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<constructor name="LocationProviderImpl" - type="android.location.LocationProviderImpl" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="name" type="java.lang.String"> -</parameter> -</constructor> -<method name="addListener" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="uid" type="int"> -</parameter> -</method> -<method name="disable" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> -<method name="enable" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> -<method name="enableLocationTracking" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="enable" type="boolean"> -</parameter> -</method> -<method name="getStatus" - return="int" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="extras" type="android.os.Bundle"> -</parameter> -</method> -<method name="getStatusUpdateTime" - return="long" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> -<method name="removeListener" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="uid" type="int"> -</parameter> -</method> -<method name="sendExtraCommand" - return="boolean" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="command" type="java.lang.String"> -</parameter> -<parameter name="extras" type="android.os.Bundle"> -</parameter> -</method> -<method name="setMinTime" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="minTime" type="long"> -</parameter> -</method> -<method name="updateLocation" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="location" type="android.location.Location"> -</parameter> -</method> -<method name="updateNetworkState" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="state" type="int"> -</parameter> -<parameter name="info" type="android.net.NetworkInfo"> -</parameter> -</method> -</class> </package> <package name="android.media" > @@ -83257,6 +83076,25 @@ <parameter name="mimeType" type="java.lang.String"> </parameter> </method> +<method name="scanFile" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="paths" type="java.lang.String[]"> +</parameter> +<parameter name="mimeTypes" type="java.lang.String[]"> +</parameter> +<parameter name="callback" type="android.media.MediaScannerConnection.ScanResultListener"> +</parameter> +</method> </class> <interface name="MediaScannerConnection.MediaScannerConnectionClient" abstract="true" @@ -83265,6 +83103,8 @@ deprecated="not deprecated" visibility="public" > +<implements name="android.media.MediaScannerConnection.ScanResultListener"> +</implements> <method name="onMediaScannerConnected" return="void" abstract="true" @@ -83292,6 +83132,29 @@ </parameter> </method> </interface> +<interface name="MediaScannerConnection.ScanResultListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onScanCompleted" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="path" type="java.lang.String"> +</parameter> +<parameter name="uri" type="android.net.Uri"> +</parameter> +</method> +</interface> <class name="Ringtone" extends="java.lang.Object" abstract="false" @@ -112899,6 +112762,19 @@ visibility="public" > </method> +<method name="getExternalStoragePublicDirectory" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="type" type="java.lang.String"> +</parameter> +</method> <method name="getExternalStorageState" return="java.lang.String" abstract="false" @@ -112921,6 +112797,96 @@ visibility="public" > </method> +<field name="DIRECTORY_ALARMS" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_DCIM" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_DOWNLOADS" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_MOVIES" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_MUSIC" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_NOTIFICATIONS" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_PICTURES" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_PODCASTS" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="DIRECTORY_RINGTONES" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="MEDIA_BAD_REMOVAL" type="java.lang.String" transient="false" @@ -117875,6 +117841,218 @@ </method> </class> </package> +<package name="android.os.storage" +> +<class name="StorageEventListener" + extends="java.lang.Object" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="StorageEventListener" + type="android.os.storage.StorageEventListener" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="onStorageStateChanged" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="path" type="java.lang.String"> +</parameter> +<parameter name="oldState" type="java.lang.String"> +</parameter> +<parameter name="newState" type="java.lang.String"> +</parameter> +</method> +<method name="onUsbMassStorageConnectionChanged" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="connected" type="boolean"> +</parameter> +</method> +</class> +<class name="StorageManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="disableUsbMassStorage" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="enableUsbMassStorage" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isUsbMassStorageConnected" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isUsbMassStorageEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="registerListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.os.storage.StorageEventListener"> +</parameter> +</method> +<method name="unregisterListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.os.storage.StorageEventListener"> +</parameter> +</method> +</class> +<class name="StorageResultCode" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="StorageResultCode" + type="android.os.storage.StorageResultCode" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<field name="OperationFailedInternalError" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OperationFailedMediaBlank" + type="int" + transient="false" + volatile="false" + value="-3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OperationFailedMediaCorrupt" + type="int" + transient="false" + volatile="false" + value="-4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OperationFailedNoMedia" + type="int" + transient="false" + volatile="false" + value="-2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OperationFailedVolumeNotMounted" + type="int" + transient="false" + volatile="false" + value="-5" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OperationSucceeded" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +</package> <package name="android.preference" > <class name="CheckBoxPreference" @@ -135762,6 +135940,17 @@ visibility="public" > </field> +<field name="EXTRA_PARTIAL_RESULTS" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.speech.extra.PARTIAL_RESULTS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_PROMPT" type="java.lang.String" transient="false" @@ -136530,126 +136719,6 @@ </method> </interface> </package> -<package name="android.storage" -> -<interface name="StorageEventListener" - abstract="true" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<method name="onMediaInserted" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="label" type="java.lang.String"> -</parameter> -<parameter name="path" type="java.lang.String"> -</parameter> -<parameter name="major" type="int"> -</parameter> -<parameter name="minor" type="int"> -</parameter> -</method> -<method name="onMediaRemoved" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="label" type="java.lang.String"> -</parameter> -<parameter name="path" type="java.lang.String"> -</parameter> -<parameter name="major" type="int"> -</parameter> -<parameter name="minor" type="int"> -</parameter> -<parameter name="clean" type="boolean"> -</parameter> -</method> -<method name="onShareAvailabilityChanged" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="method" type="java.lang.String"> -</parameter> -<parameter name="available" type="boolean"> -</parameter> -</method> -<method name="onVolumeStateChanged" - return="void" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="label" type="java.lang.String"> -</parameter> -<parameter name="path" type="java.lang.String"> -</parameter> -<parameter name="oldState" type="java.lang.String"> -</parameter> -<parameter name="newState" type="java.lang.String"> -</parameter> -</method> -</interface> -<class name="StorageManager" - extends="java.lang.Object" - abstract="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<method name="registerListener" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="listener" type="android.storage.StorageEventListener"> -</parameter> -</method> -<method name="unregisterListener" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="listener" type="android.storage.StorageEventListener"> -</parameter> -</method> -</class> -</package> <package name="android.telephony" > <class name="CellLocation" @@ -144380,6 +144449,30 @@ <parameter name="mode" type="int"> </parameter> </method> +<method name="getExternalCacheDir" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getExternalFilesDir" + return="java.io.File" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="type" type="java.lang.String"> +</parameter> +</method> <method name="getFileStreamPath" return="java.io.File" abstract="false" @@ -181430,6 +181523,17 @@ <parameter name="t" type="android.view.animation.Transformation"> </parameter> </method> +<method name="cancel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="computeDurationHint" return="long" abstract="false" @@ -210184,7 +210288,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/common/java/com/android/common/Base64.java b/common/java/com/android/common/Base64.java index 0c8e7c1..771875c 100644 --- a/common/java/com/android/common/Base64.java +++ b/common/java/com/android/common/Base64.java @@ -22,6 +22,11 @@ package com.android.common; */ public class Base64 { /** + * Default values for encoder/decoder flags. + */ + public static final int DEFAULT = 0; + + /** * Encoder flag bit to indicate you want the padding '=' * characters at the end (if any) to be omitted. */ @@ -106,7 +111,7 @@ public class Base64 { * @param input the input String to decode, which is converted to * bytes using the default charset * @param flags controls certain features of the decoded output. - * Passing 0 to decode standard Base64. + * Pass {@code DEFAULT} to decode standard Base64. * * @throws IllegalArgumentException if the input contains * incorrect padding @@ -124,7 +129,7 @@ public class Base64 { * * @param input the input array to decode * @param flags controls certain features of the decoded output. - * Passing 0 to decode standard Base64. + * Pass {@code DEFAULT} to decode standard Base64. * * @throws IllegalArgumentException if the input contains * incorrect padding @@ -144,7 +149,7 @@ public class Base64 { * @param offset the position within the input array at which to start * @param len the number of bytes of input to decode * @param flags controls certain features of the decoded output. - * Passing 0 to decode standard Base64. + * Pass {@code DEFAULT} to decode standard Base64. * * @throws IllegalArgumentException if the input contains * incorrect padding @@ -362,8 +367,8 @@ public class Base64 { * * @param input the data to encode * @param flags controls certain features of the encoded output. - * Passing 0 results in output that adheres to RFC - * 2045. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static String encodeToString(byte[] input, int flags) { return new String(encode(input, flags)); @@ -378,8 +383,8 @@ public class Base64 { * start * @param len the number of bytes of input to encode * @param flags controls certain features of the encoded output. - * Passing 0 results in output that adheres to RFC - * 2045. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static String encodeToString(byte[] input, int offset, int len, int flags) { return new String(encode(input, offset, len, flags)); @@ -391,8 +396,8 @@ public class Base64 { * * @param input the data to encode * @param flags controls certain features of the encoded output. - * Passing 0 results in output that adheres to RFC - * 2045. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static byte[] encode(byte[] input, int flags) { return encode(input, 0, input.length, flags); @@ -407,8 +412,8 @@ public class Base64 { * start * @param len the number of bytes of input to encode * @param flags controls certain features of the encoded output. - * Passing 0 results in output that adheres to RFC - * 2045. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static byte[] encode(byte[] input, int offset, int len, int flags) { final boolean do_padding = (flags & NO_PADDING) == 0; @@ -494,4 +499,6 @@ public class Base64 { assert op == output.length; return output; } + + private Base64() { } // don't instantiate } diff --git a/common/java/com/android/common/Patterns.java b/common/java/com/android/common/Patterns.java index 24a18c0..71c3a5e 100644 --- a/common/java/com/android/common/Patterns.java +++ b/common/java/com/android/common/Patterns.java @@ -25,87 +25,87 @@ import java.util.regex.Pattern; public class Patterns { /** * Regular expression pattern to match all IANA top-level domains. - * List accurate as of 2007/06/15. List taken from: + * List accurate as of 2010/02/05. List taken from: * http://data.iana.org/TLD/tlds-alpha-by-domain.txt - * This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py + * This pattern is auto-generated by development/tools/make-iana-tld-pattern.py */ - public static final Pattern TOP_LEVEL_DOMAIN - = Pattern.compile( - "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])" - + "|(biz|b[abdefghijmnorstvwyz])" - + "|(cat|com|coop|c[acdfghiklmnoruvxyz])" - + "|d[ejkmoz]" - + "|(edu|e[cegrstu])" - + "|f[ijkmor]" - + "|(gov|g[abdefghilmnpqrstuwy])" - + "|h[kmnrtu]" - + "|(info|int|i[delmnoqrst])" - + "|(jobs|j[emop])" - + "|k[eghimnrwyz]" - + "|l[abcikrstuvy]" - + "|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])" - + "|(name|net|n[acefgilopruz])" - + "|(org|om)" - + "|(pro|p[aefghklmnrstwy])" - + "|qa" - + "|r[eouw]" - + "|s[abcdeghijklmnortuvyz]" - + "|(tel|travel|t[cdfghjklmnoprtvwz])" - + "|u[agkmsyz]" - + "|v[aceginu]" - + "|w[fs]" - + "|y[etu]" - + "|z[amw])"); + public static final Pattern TOP_LEVEL_DOMAIN = Pattern.compile( + "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])" + + "|(biz|b[abdefghijmnorstvwyz])" + + "|(cat|com|coop|c[acdfghiklmnoruvxyz])" + + "|d[ejkmoz]" + + "|(edu|e[cegrstu])" + + "|f[ijkmor]" + + "|(gov|g[abdefghilmnpqrstuwy])" + + "|h[kmnrtu]" + + "|(info|int|i[delmnoqrst])" + + "|(jobs|j[emop])" + + "|k[eghimnprwyz]" + + "|l[abcikrstuvy]" + + "|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])" + + "|(name|net|n[acefgilopruz])" + + "|(org|om)" + + "|(pro|p[aefghklmnrstwy])" + + "|qa" + + "|r[eosuw]" + + "|s[abcdeghijklmnortuvyz]" + + "|(tel|travel|t[cdfghjklmnoprtvwz])" + + "|u[agksyz]" + + "|v[aceginu]" + + "|w[fs]" + + "|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)" + + "|y[etu]" + + "|z[amw])"); /** * Regular expression pattern to match RFC 1738 URLs - * List accurate as of 2007/06/15. List taken from: + * List accurate as of 2010/02/05. List taken from: * http://data.iana.org/TLD/tlds-alpha-by-domain.txt - * This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py + * This pattern is auto-generated by development/tools/make-iana-tld-pattern.py */ - public static final Pattern WEB_URL - = Pattern.compile( - "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" - + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" - + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" - + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+" // named host - + "(?:" // plus top level domain - + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])" - + "|(?:biz|b[abdefghijmnorstvwyz])" - + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])" - + "|d[ejkmoz]" - + "|(?:edu|e[cegrstu])" - + "|f[ijkmor]" - + "|(?:gov|g[abdefghilmnpqrstuwy])" - + "|h[kmnrtu]" - + "|(?:info|int|i[delmnoqrst])" - + "|(?:jobs|j[emop])" - + "|k[eghimnrwyz]" - + "|l[abcikrstuvy]" - + "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])" - + "|(?:name|net|n[acefgilopruz])" - + "|(?:org|om)" - + "|(?:pro|p[aefghklmnrstwy])" - + "|qa" - + "|r[eouw]" - + "|s[abcdeghijklmnortuvyz]" - + "|(?:tel|travel|t[cdfghjklmnoprtvwz])" - + "|u[agkmsyz]" - + "|v[aceginu]" - + "|w[fs]" - + "|y[etu]" - + "|z[amw]))" - + "|(?:(?:25[0-5]|2[0-4]" // or ip address - + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]" - + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]" - + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" - + "|[1-9][0-9]|[0-9])))" - + "(?:\\:\\d{1,5})?)" // plus option port number - + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params - + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" - + "(?:\\b|$)"); // and finally, a word boundary or end of - // input. This is to stop foo.sure from - // matching as foo.su + public static final Pattern WEB_URL = Pattern.compile( + "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" + + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+" // named host + + "(?:" // plus top level domain + + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])" + + "|(?:biz|b[abdefghijmnorstvwyz])" + + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])" + + "|d[ejkmoz]" + + "|(?:edu|e[cegrstu])" + + "|f[ijkmor]" + + "|(?:gov|g[abdefghilmnpqrstuwy])" + + "|h[kmnrtu]" + + "|(?:info|int|i[delmnoqrst])" + + "|(?:jobs|j[emop])" + + "|k[eghimnprwyz]" + + "|l[abcikrstuvy]" + + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])" + + "|(?:name|net|n[acefgilopruz])" + + "|(?:org|om)" + + "|(?:pro|p[aefghklmnrstwy])" + + "|qa" + + "|r[eosuw]" + + "|s[abcdeghijklmnortuvyz]" + + "|(?:tel|travel|t[cdfghjklmnoprtvwz])" + + "|u[agksyz]" + + "|v[aceginu]" + + "|w[fs]" + + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)" + + "|y[etu]" + + "|z[amw]))" + + "|(?:(?:25[0-5]|2[0-4]" // or ip address + + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]" + + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]" + + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + + "|[1-9][0-9]|[0-9])))" + + "(?:\\:\\d{1,5})?)" // plus option port number + + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" + + "(?:\\b|$)"); // and finally, a word boundary or end of + // input. This is to stop foo.sure from + // matching as foo.su public static final Pattern IP_ADDRESS = Pattern.compile( diff --git a/common/tests/src/com/android/common/PatternsTest.java b/common/tests/src/com/android/common/PatternsTest.java index 7fabe5e..635601e 100644 --- a/common/tests/src/com/android/common/PatternsTest.java +++ b/common/tests/src/com/android/common/PatternsTest.java @@ -31,6 +31,20 @@ public class PatternsTest extends TestCase { t = Patterns.TOP_LEVEL_DOMAIN.matcher("com").matches(); assertTrue("Missed valid TLD", t); + // One of the new top level domain. + t = Patterns.TOP_LEVEL_DOMAIN.matcher("me").matches(); + assertTrue("Missed valid TLD", t); + + // One of the new top level test domain. + t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn--0zwm56d").matches(); + assertTrue("Missed valid TLD", t); + + t = Patterns.TOP_LEVEL_DOMAIN.matcher("mem").matches(); + assertFalse("Matched invalid TLD!", t); + + t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn").matches(); + assertFalse("Matched invalid TLD!", t); + t = Patterns.TOP_LEVEL_DOMAIN.matcher("xer").matches(); assertFalse("Matched invalid TLD!", t); } @@ -42,6 +56,18 @@ public class PatternsTest extends TestCase { t = Patterns.WEB_URL.matcher("http://www.google.com").matches(); assertTrue("Valid URL", t); + // Google in one of the new top level domain. + t = Patterns.WEB_URL.matcher("http://www.google.me").matches(); + assertTrue("Valid URL", t); + t = Patterns.WEB_URL.matcher("google.me").matches(); + assertTrue("Valid URL", t); + + // Test url in Chinese: http://xn--fsqu00a.xn--0zwm56d + t = Patterns.WEB_URL.matcher("http://xn--fsqu00a.xn--0zwm56d").matches(); + assertTrue("Valid URL", t); + t = Patterns.WEB_URL.matcher("xn--fsqu00a.xn--0zwm56d").matches(); + assertTrue("Valid URL", t); + t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches(); assertFalse("Matched invalid protocol", t); diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 19e741a..be15ac9 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -27,6 +27,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.Parcelable; +import android.os.Build; import android.util.Log; import java.io.IOException; @@ -837,14 +838,13 @@ public class AccountManager { private void ensureNotOnMainThread() { final Looper looper = Looper.myLooper(); if (looper != null && looper == mContext.getMainLooper()) { - // We really want to throw an exception here, but GTalkService exercises this - // path quite a bit and needs some serious rewrite in order to work properly. - //noinspection ThrowableInstanceNeverThrow -// Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs", -// new Exception()); - // TODO remove the log and throw this exception when the callers are fixed -// throw new IllegalStateException( -// "calling this from your main thread can lead to deadlock"); + final IllegalStateException exception = new IllegalStateException( + "calling this from your main thread can lead to deadlock"); + Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs", + exception); + if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) { + throw exception; + } } } @@ -909,7 +909,9 @@ public class AccountManager { private Bundle internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException, IOException, AuthenticatorException { - ensureNotOnMainThread(); + if (!isDone()) { + ensureNotOnMainThread(); + } try { if (timeout == null) { return get(); @@ -1075,7 +1077,9 @@ public class AccountManager { private T internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException, IOException, AuthenticatorException { - ensureNotOnMainThread(); + if (!isDone()) { + ensureNotOnMainThread(); + } try { if (timeout == null) { return get(); diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index e3ccd00..7850124 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -1788,7 +1788,7 @@ public class AccountManagerService if (!permissionGranted && isDebuggableMonkeyBuild) { // TODO: Skip this check when running automated tests. Replace this // with a more general solution. - Log.w(TAG, "no credentials permission for usage of " + account + ", " + Log.d(TAG, "no credentials permission for usage of " + account + ", " + authTokenType + " by uid " + Binder.getCallingUid() + " but ignoring since this is a monkey build"); return true; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5f89496..9b9cbd5 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -84,7 +84,7 @@ import android.os.ServiceManager; import android.os.StatFs; import android.os.Vibrator; import android.os.FileUtils.FileStatus; -import android.storage.StorageManager; +import android.os.storage.StorageManager; import android.telephony.TelephonyManager; import android.text.ClipboardManager; import android.util.AndroidRuntimeException; @@ -197,9 +197,9 @@ class ContextImpl extends Context { private File mDatabasesDir; private File mPreferencesDir; private File mFilesDir; - - private File mCacheDir; + private File mExternalFilesDir; + private File mExternalCacheDir; private static long sInstanceCount = 0; @@ -438,6 +438,38 @@ class ContextImpl extends Context { } @Override + public File getExternalFilesDir(String type) { + synchronized (mSync) { + if (mExternalFilesDir == null) { + mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory( + getPackageName()); + } + if (!mExternalFilesDir.exists()) { + try { + (new File(Environment.getExternalStorageAndroidDataDir(), + ".nomedia")).createNewFile(); + } catch (IOException e) { + } + if (!mExternalFilesDir.mkdirs()) { + Log.w(TAG, "Unable to create external files directory"); + return null; + } + } + if (type == null) { + return mExternalFilesDir; + } + File dir = new File(mExternalFilesDir, type); + if (!dir.exists()) { + if (!dir.mkdirs()) { + Log.w(TAG, "Unable to create external media directory " + dir); + return null; + } + } + return dir; + } + } + + @Override public File getCacheDir() { synchronized (mSync) { if (mCacheDir == null) { @@ -457,7 +489,28 @@ class ContextImpl extends Context { return mCacheDir; } - + @Override + public File getExternalCacheDir() { + synchronized (mSync) { + if (mExternalCacheDir == null) { + mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory( + getPackageName()); + } + if (!mExternalCacheDir.exists()) { + try { + (new File(Environment.getExternalStorageAndroidDataDir(), + ".nomedia")).createNewFile(); + } catch (IOException e) { + } + if (!mExternalCacheDir.mkdirs()) { + Log.w(TAG, "Unable to create external cache directory"); + return null; + } + } + return mExternalCacheDir; + } + } + @Override public File getFileStreamPath(String name) { return makeFilename(getFilesDir(), name); @@ -2166,7 +2219,7 @@ class ContextImpl extends Context { filter, null, null, null); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiverInternal(sPackageRemovedReceiver, sdFilter, null, null, null); } @@ -2189,7 +2242,7 @@ class ContextImpl extends Context { String pkgList[] = null; String action = intent.getAction(); boolean immediateGc = false; - if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); immediateGc = true; } else { diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java index ecbad01..af9c379 100644 --- a/core/java/android/app/DeviceAdmin.java +++ b/core/java/android/app/DeviceAdmin.java @@ -40,7 +40,7 @@ import android.os.Bundle; * to the device administrator, as parsed by the {@link DeviceAdminInfo} class. * A typical file would be:</p> * - * {@sample development/samples/ApiDemos/res/xml/sample_device_admin.xml meta_data} + * {@sample development/samples/ApiDemos/res/xml/device_admin_sample.xml meta_data} */ public class DeviceAdmin extends BroadcastReceiver { private static String TAG = "DevicePolicy"; diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java index 08cdd05..847e879 100644 --- a/core/java/android/app/DevicePolicyManager.java +++ b/core/java/android/app/DevicePolicyManager.java @@ -328,14 +328,20 @@ public class DevicePolicyManager { } /** - * Set the maximum number of failed password attempts that are allowed - * before the device wipes its data. This is convenience for implementing - * the corresponding functionality with a combination of watching failed - * password attempts and calling {@link #wipeData} upon reaching a certain - * count, and as such requires that you request both - * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and + * Setting this to a value greater than zero enables a built-in policy + * that will perform a device wipe after too many incorrect + * device-unlock passwords have been entered. This built-in policy combines + * watching for failed passwords and wiping the device, and requires + * that you request both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}. * + * <p>To implement any other policy (e.g. wiping data for a particular + * application only, erasing or revoking credentials, or reporting the + * failure to a server), you should implement + * {@link DeviceAdmin#onPasswordFailed(Context, android.content.Intent)} + * instead. Do not use this API, because if the maximum count is reached, + * the device will be wiped immediately, and your callback will not be invoked. + * * @param admin Which {@link DeviceAdmin} this request is associated with. * @param num The number of failed password attempts at which point the * device will wipe its data. diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java index 804c8eb..3fd36a3 100644 --- a/core/java/android/app/IntentService.java +++ b/core/java/android/app/IntentService.java @@ -42,7 +42,7 @@ public abstract class IntentService extends Service { * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT} instead of * {@link Service#START_NOT_STICKY}, so that if this service's process - * is called while it is executing the Intent in + * is killed while it is executing the Intent in * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent * will be re-delivered to it, to retry its execution. */ diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index 0455202..c4ba05d 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -30,7 +30,7 @@ import java.lang.Comparable; * name inside of that package. * */ -public final class ComponentName implements Parcelable, Comparable<ComponentName> { +public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> { private final String mPackage; private final String mClass; @@ -76,6 +76,10 @@ public final class ComponentName implements Parcelable, Comparable<ComponentName mClass = cls.getName(); } + public ComponentName clone() { + return new ComponentName(mPackage, mClass); + } + /** * Return the package name of this component. */ diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b5587ed..bdbe341 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -16,6 +16,8 @@ package android.content; +import android.accounts.Account; +import android.app.ActivityThread; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; @@ -30,8 +32,8 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; -import android.accounts.Account; import android.util.Config; +import android.util.EventLog; import android.util.Log; import java.io.File; @@ -41,8 +43,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.Random; import java.util.ArrayList; -import java.util.Collection; /** @@ -62,7 +64,31 @@ public abstract class ContentResolver { */ @Deprecated public static final String SYNC_EXTRAS_FORCE = "force"; + + /** + * If this extra is set to true then the sync settings (like getSyncAutomatically()) + * are ignored by the sync scheduler. + */ + public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings"; + + /** + * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries) + * are ignored by the sync scheduler. If this request fails and gets rescheduled then the + * retries will still honor the backoff. + */ + public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff"; + + /** + * If this extra is set to true then the request will not be retried if it fails. + */ + public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry"; + + /** + * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS} + * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF} + */ public static final String SYNC_EXTRAS_MANUAL = "force"; + public static final String SYNC_EXTRAS_UPLOAD = "upload"; public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override"; public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions"; @@ -140,6 +166,11 @@ public abstract class ContentResolver { /** @hide */ public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff; + // Always log queries which take 100ms+; shorter queries are + // sampled accordingly. + private static final int SLOW_THRESHOLD_MILLIS = 100; + private final Random mRandom = new Random(); // guarded by itself + public ContentResolver(Context context) { mContext = context; } @@ -212,12 +243,15 @@ public abstract class ContentResolver { return null; } try { + long startTime = System.currentTimeMillis(); Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder); - if(qCursor == null) { + if (qCursor == null) { releaseProvider(provider); return null; } - //Wrap the cursor object into CursorWrapperInner object + long durationMillis = System.currentTimeMillis() - startTime; + maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); + // Wrap the cursor object into CursorWrapperInner object return new CursorWrapperInner(qCursor, provider); } catch (RemoteException e) { releaseProvider(provider); @@ -549,7 +583,11 @@ public abstract class ContentResolver { throw new IllegalArgumentException("Unknown URL " + url); } try { - return provider.insert(url, values); + long startTime = System.currentTimeMillis(); + Uri createdRow = provider.insert(url, values); + long durationMillis = System.currentTimeMillis() - startTime; + maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); + return createdRow; } catch (RemoteException e) { return null; } finally { @@ -604,7 +642,11 @@ public abstract class ContentResolver { throw new IllegalArgumentException("Unknown URL " + url); } try { - return provider.bulkInsert(url, values); + long startTime = System.currentTimeMillis(); + int rowsCreated = provider.bulkInsert(url, values); + long durationMillis = System.currentTimeMillis() - startTime; + maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */); + return rowsCreated; } catch (RemoteException e) { return 0; } finally { @@ -629,7 +671,11 @@ public abstract class ContentResolver { throw new IllegalArgumentException("Unknown URL " + url); } try { - return provider.delete(url, where, selectionArgs); + long startTime = System.currentTimeMillis(); + int rowsDeleted = provider.delete(url, where, selectionArgs); + long durationMillis = System.currentTimeMillis() - startTime; + maybeLogUpdateToEventLog(durationMillis, url, "delete", where); + return rowsDeleted; } catch (RemoteException e) { return -1; } finally { @@ -657,7 +703,11 @@ public abstract class ContentResolver { throw new IllegalArgumentException("Unknown URI " + uri); } try { - return provider.update(uri, values, where, selectionArgs); + long startTime = System.currentTimeMillis(); + int rowsUpdated = provider.update(uri, values, where, selectionArgs); + long durationMillis = System.currentTimeMillis() - startTime; + maybeLogUpdateToEventLog(durationMillis, uri, "update", where); + return rowsUpdated; } catch (RemoteException e) { return -1; } finally { @@ -976,15 +1026,38 @@ public abstract class ContentResolver { * Although these sync are scheduled at the specified frequency, it may take longer for it to * actually be started if other syncs are ahead of it in the sync operation queue. This means * that the actual start time may drift. + * <p> + * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY}, + * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, + * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE}, + * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true. + * If any are supplied then an {@link IllegalArgumentException} will be thrown. * * @param account the account to specify in the sync * @param authority the provider to specify in the sync request * @param extras extra parameters to go along with the sync request * @param pollFrequency how frequently the sync should be performed, in seconds. + * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters + * are null. */ public static void addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency) { validateSyncExtrasBundle(extras); + if (account == null) { + throw new IllegalArgumentException("account must not be null"); + } + if (authority == null) { + throw new IllegalArgumentException("authority must not be null"); + } + if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) + || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false) + || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false) + || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false) + || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false) + || extras.getBoolean(SYNC_EXTRAS_FORCE, false) + || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) { + throw new IllegalArgumentException("illegal extras were set"); + } try { getContentService().addPeriodicSync(account, authority, extras, pollFrequency); } catch (RemoteException e) { @@ -1003,6 +1076,12 @@ public abstract class ContentResolver { */ public static void removePeriodicSync(Account account, String authority, Bundle extras) { validateSyncExtrasBundle(extras); + if (account == null) { + throw new IllegalArgumentException("account must not be null"); + } + if (authority == null) { + throw new IllegalArgumentException("authority must not be null"); + } try { getContentService().removePeriodicSync(account, authority, extras); } catch (RemoteException e) { @@ -1018,6 +1097,12 @@ public abstract class ContentResolver { * @return a list of PeriodicSync objects. This list may be empty but will never be null. */ public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) { + if (account == null) { + throw new IllegalArgumentException("account must not be null"); + } + if (authority == null) { + throw new IllegalArgumentException("authority must not be null"); + } try { return getContentService().getPeriodicSyncs(account, authority); } catch (RemoteException e) { @@ -1159,6 +1244,78 @@ public abstract class ContentResolver { } } + /** + * Returns sampling percentage for a given duration. + * + * Always returns at least 1%. + */ + private int samplePercentForDuration(long durationMillis) { + if (durationMillis >= SLOW_THRESHOLD_MILLIS) { + return 100; + } + return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1; + } + + private void maybeLogQueryToEventLog(long durationMillis, + Uri uri, String[] projection, + String selection, String sortOrder) { + int samplePercent = samplePercentForDuration(durationMillis); + if (samplePercent < 100) { + synchronized (mRandom) { + if (mRandom.nextInt(100) >= samplePercent) { + return; + } + } + } + + StringBuilder projectionBuffer = new StringBuilder(100); + if (projection != null) { + for (int i = 0; i < projection.length; ++i) { + // Note: not using a comma delimiter here, as the + // multiple arguments to EventLog.writeEvent later + // stringify with a comma delimiter, which would make + // parsing uglier later. + if (i != 0) projectionBuffer.append('/'); + projectionBuffer.append(projection[i]); + } + } + + // ActivityThread.currentPackageName() only returns non-null if the + // current thread is an application main thread. This parameter tells + // us whether an event loop is blocked, and if so, which app it is. + String blockingPackage = ActivityThread.currentPackageName(); + + EventLog.writeEvent( + EventLogTags.CONTENT_QUERY_OPERATION, + uri.toString(), + projectionBuffer.toString(), + selection != null ? selection : "", + sortOrder != null ? sortOrder : "", + durationMillis, + blockingPackage != null ? blockingPackage : "", + samplePercent); + } + + private void maybeLogUpdateToEventLog( + long durationMillis, Uri uri, String operation, String selection) { + int samplePercent = samplePercentForDuration(durationMillis); + if (samplePercent < 100) { + synchronized (mRandom) { + if (mRandom.nextInt(100) >= samplePercent) { + return; + } + } + } + String blockingPackage = ActivityThread.currentPackageName(); + EventLog.writeEvent( + EventLogTags.CONTENT_UPDATE_OPERATION, + uri.toString(), + operation, + selection != null ? selection : "", + durationMillis, + blockingPackage != null ? blockingPackage : "", + samplePercent); + } private final class CursorWrapperInner extends CursorWrapper { private IContentProvider mContentProvider; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5aefe4c..672e5f7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -25,6 +25,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.media.MediaScannerConnection.ScanResultListener; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -137,7 +138,28 @@ public abstract class Context { /** * Return the context of the single, global Application object of the - * current process. + * current process. This generally should only be used if you need a + * Context whose lifecycle is separate from the current context, that is + * tied to the lifetime of the process rather than the current component. + * + * <p>Consider for example how this interacts with + * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}: + * <ul> + * <li> <p>If used from an Activity context, the receiver is being registered + * within that activity. This means that you are expected to unregister + * before the activity is done being destroyed; in fact if you do not do + * so, the framework will clean up your leaked registration as it removes + * the activity and log an error. Thus, if you use the Activity context + * to register a receiver that is static (global to the process, not + * associated with an Activity instance) then that registration will be + * removed on you at whatever point the activity you used is destroyed. + * <li> <p>If used from the Context returned here, the receiver is being + * registered with the global state associated with your application. Thus + * it will never be unregistered for you. This is necessary if the receiver + * is associated with static data, not a particular component. However + * using the ApplicationContext elsewhere can easily lead to serious leaks + * if you forget to unregister, unbind, etc. + * </ul> */ public abstract Context getApplicationContext(); @@ -393,11 +415,84 @@ public abstract class Context { public abstract File getFilesDir(); /** + * Returns the absolute path to the directory on the external filesystem + * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() + * Environment.getExternalStorageDirectory()} where the application can + * place persistent files it owns. These files are private to the + * applications, and not typically visible to the user as media. + * + * <p>This is like {@link #getFilesDir()} in that these + * files will be deleted when the application is uninstalled, however there + * are some important differences: + * + * <ul> + * <li>External files are not always available: they will disappear if the + * user mounts the external storage on a computer or removes it. See the + * APIs on {@link android.os.Environment} for information in the storage state. + * <li>There is no security enforced with these files. All applications + * can read and write files placed here. + * </ul> + * + * <p>Here is an example of typical code to manipulate a file in + * an application's private storage:</p> + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_file} + * + * <p>If you install a non-null <var>type</var> to this function, the returned + * file will be a path to a sub-directory of the given type. Though these files + * are not automatically scanned by the media scanner, you can explicitly + * add them to the media database with + * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], + * ScanResultListener) MediaScannerConnection.scanFile}. + * Note that this is not the same as + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, which provides + * directories of media shared by all applications. The + * directories returned here are + * owned by the application, and its contents will be removed when the + * application is uninstalled. Unlike + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, the directory + * returned here will be automatically created for you. + * + * <p>Here is an example of typical code to manipulate a picture in + * an application's private storage and add it to the media database:</p> + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_picture} + * + * @param type The type of files directory to return. May be null for + * the root of the files directory or one of + * the following Environment constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * + * @return Returns the path of the directory holding application files + * on external storage. Returns null if external storage is not currently + * mounted so it could not ensure the path exists; you will need to call + * this method again when it is available. + * + * @see #getFilesDir + */ + public abstract File getExternalFilesDir(String type); + + /** * Returns the absolute path to the application specific cache directory * on the filesystem. These files will be ones that get deleted first when the - * device runs low on storage + * device runs low on storage. * There is no guarantee when these files will be deleted. - * + * + * <strong>Note: you should not <em>rely</em> on the system deleting these + * files for you; you should always have a reasonable maximum, such as 1 MB, + * for the amount of space you consume with cache files, and prune those + * files when exceeding that space.</strong> + * * @return Returns the path of the directory holding application cache files. * * @see #openFileOutput @@ -407,6 +502,37 @@ public abstract class Context { public abstract File getCacheDir(); /** + * Returns the absolute path to the directory on the external filesystem + * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() + * Environment.getExternalStorageDirectory()} where the application can + * place cache files it owns. + * + * <p>This is like {@link #getCacheDir()} in that these + * files will be deleted when the application is uninstalled, however there + * are some important differences: + * + * <ul> + * <li>The platform does not monitor the space available in external storage, + * and thus will not automatically delete these files. Note that you should + * be managing the maximum space you will use for these anyway, just like + * with {@link #getCacheDir()}. + * <li>External files are not always available: they will disappear if the + * user mounts the external storage on a computer or removes it. See the + * APIs on {@link android.os.Environment} for information in the storage state. + * <li>There is no security enforced with these files. All applications + * can read and write files placed here. + * </ul> + * + * @return Returns the path of the directory holding application cache files + * on external storage. Returns null if external storage is not currently + * mounted so it could not ensure the path exists; you will need to call + * this method again when it is available. + * + * @see #getCacheDir + */ + public abstract File getExternalCacheDir(); + + /** * Returns an array of strings naming the private files associated with * this Context's application package. * @@ -1110,7 +1236,7 @@ public abstract class Context { * @see #SENSOR_SERVICE * @see android.hardware.SensorManager * @see #STORAGE_SERVICE - * @see android.storage.StorageManager + * @see android.os.storage.StorageManager * @see #VIBRATOR_SERVICE * @see android.os.Vibrator * @see #CONNECTIVITY_SERVICE @@ -1243,11 +1369,11 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link - * android.storage.StorageManager} for accesssing system storage + * android.os.storage.StorageManager} for accesssing system storage * functions. * * @see #getSystemService - * @see android.storage.StorageManager + * @see android.os.storage.StorageManager */ public static final String STORAGE_SERVICE = "storage"; diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 1b34320..a447108 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -179,11 +179,21 @@ public class ContextWrapper extends Context { } @Override + public File getExternalFilesDir(String type) { + return mBase.getExternalFilesDir(type); + } + + @Override public File getCacheDir() { return mBase.getCacheDir(); } @Override + public File getExternalCacheDir() { + return mBase.getExternalCacheDir(); + } + + @Override public File getDir(String name, int mode) { return mBase.getDir(name, mode); } diff --git a/core/java/android/content/EventLogTags.logtags b/core/java/android/content/EventLogTags.logtags new file mode 100644 index 0000000..a815b95 --- /dev/null +++ b/core/java/android/content/EventLogTags.logtags @@ -0,0 +1,6 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package android.content; + +52002 content_query_operation (uri|3),(projection|3),(selection|3),(sortorder|3),(time|1|3),(blocking_package|3),(sample_percent|1|6) +52003 content_update_operation (uri|3),(operation|3),(selection|3),(time|1|3),(blocking_package|3),(sample_percent|1|6) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e957e20..c32999f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1381,8 +1381,8 @@ public class Intent implements Parcelable, Cloneable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MEDIA_RESOURCES_AVAILABLE = - "android.intent.action.MEDIA_RESOURCES_AVAILABILE"; + public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = + "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE"; /** * Broadcast Action: Resources for a set of packages are currently @@ -1406,8 +1406,8 @@ public class Intent implements Parcelable, Cloneable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MEDIA_RESOURCES_UNAVAILABLE = - "android.intent.action.MEDIA_RESOURCES_UNAVAILABILE"; + public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = + "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABILE"; /** * Broadcast Action: The current system wallpaper has changed. See @@ -2198,8 +2198,8 @@ public class Intent implements Parcelable, Cloneable { /** * This field is part of - * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE}, - * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE} + * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE}, + * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE} * and contains a string array of all of the components that have changed. * @hide */ @@ -2208,8 +2208,8 @@ public class Intent implements Parcelable, Cloneable { /** * This field is part of - * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE}, - * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE} + * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE}, + * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE} * and contains an integer array of uids of all of the components * that have changed. * @hide diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 619c7d5..ebb95e8 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -124,7 +124,7 @@ public class SyncManager implements OnAccountsUpdateListener { private Context mContext; - private volatile Account[] mAccounts = null; + private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY; volatile private PowerManager.WakeLock mSyncWakeLock; volatile private PowerManager.WakeLock mHandleAlarmWakeLock; @@ -186,9 +186,11 @@ public class SyncManager implements OnAccountsUpdateListener { } }; + private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0]; + public void onAccountsUpdated(Account[] accounts) { // remember if this was the first time this was called after an update - final boolean justBootedUp = mAccounts == null; + final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY; mAccounts = accounts; // if a sync is in progress yet it is no longer in the accounts list, @@ -486,11 +488,6 @@ public class SyncManager implements OnAccountsUpdateListener { Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (mAccounts == null) { - Log.e(TAG, "scheduleSync: the accounts aren't known yet, this should never happen"); - return; - } - if (!isSyncEnabled()) { if (isLoggable) { Log.v(TAG, "not syncing because sync is disabled"); @@ -515,13 +512,6 @@ public class SyncManager implements OnAccountsUpdateListener { // if the accounts aren't configured yet then we can't support an account-less // sync request accounts = mAccounts; - if (accounts == null) { - // not ready yet - if (isLoggable) { - Log.v(TAG, "scheduleSync: no accounts yet, dropping"); - } - return; - } if (accounts.length == 0) { if (isLoggable) { Log.v(TAG, "scheduleSync: no accounts configured, dropping"); @@ -532,6 +522,12 @@ public class SyncManager implements OnAccountsUpdateListener { final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); + if (manualSync) { + extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); + } + final boolean ignoreSettings = + extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); int source; if (uploadOnly) { @@ -590,7 +586,7 @@ public class SyncManager implements OnAccountsUpdateListener { final boolean syncAutomatically = masterSyncAutomatically && mSyncStorageEngine.getSyncAutomatically(account, authority); boolean syncAllowed = - manualSync || (backgroundDataUsageAllowed && syncAutomatically); + ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically); if (!syncAllowed) { if (isLoggable) { Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority @@ -797,21 +793,29 @@ public class SyncManager implements OnAccountsUpdateListener { Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); } + operation = new SyncOperation(operation); + + // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given + // request. Retries of the request will always honor the backoff, so clear the + // flag in case we retry this request. + if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { + operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); + } + // If this sync aborted because the internal sync loop retried too many times then // don't reschedule. Otherwise we risk getting into a retry loop. // If the operation succeeded to some extent then retry immediately. // If this was a two-way sync then retry soft errors with an exponential backoff. // If this was an upward sync then schedule a two-way sync immediately. // Otherwise do not reschedule. - if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) { - Log.d(TAG, "not retrying sync operation because it is a manual sync: " + if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { + Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " + operation); } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) { - final SyncOperation newSyncOperation = new SyncOperation(operation); - newSyncOperation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); + operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " + "encountered an error: " + operation); - scheduleSyncOperation(newSyncOperation); + scheduleSyncOperation(operation); } else if (syncResult.tooManyRetries) { Log.d(TAG, "not retrying sync operation because it retried too many times: " + operation); @@ -820,13 +824,13 @@ public class SyncManager implements OnAccountsUpdateListener { Log.d(TAG, "retrying sync operation because even though it had an error " + "it achieved some success"); } - scheduleSyncOperation(new SyncOperation(operation)); + scheduleSyncOperation(operation); } else if (syncResult.hasSoftError()) { if (isLoggable) { Log.d(TAG, "retrying sync operation because it encountered a soft error: " + operation); } - scheduleSyncOperation(new SyncOperation(operation)); + scheduleSyncOperation(operation); } else { Log.d(TAG, "not retrying sync operation because the error is a hard error: " + operation); @@ -942,10 +946,10 @@ public class SyncManager implements OnAccountsUpdateListener { final Account[] accounts = mAccounts; pw.print("accounts: "); - if (accounts != null) { + if (accounts != INITIAL_ACCOUNTS_ARRAY) { pw.println(accounts.length); } else { - pw.println("none"); + pw.println("not known yet"); } final long now = SystemClock.elapsedRealtime(); pw.print("now: "); pw.print(now); @@ -1448,7 +1452,7 @@ public class SyncManager implements OnAccountsUpdateListener { } } - private boolean isSyncAllowed(Account account, String authority, boolean manualSync, + private boolean isSyncAllowed(Account account, String authority, boolean ignoreSettings, boolean backgroundDataUsageAllowed) { Account[] accounts = mAccounts; @@ -1458,7 +1462,7 @@ public class SyncManager implements OnAccountsUpdateListener { } // skip the sync if the account of this operation no longer exists - if (accounts == null || !ArrayUtils.contains(accounts, account)) { + if (!ArrayUtils.contains(accounts, account)) { return false; } @@ -1466,7 +1470,7 @@ public class SyncManager implements OnAccountsUpdateListener { final boolean syncAutomatically = mSyncStorageEngine.getSyncAutomatically(account, authority) && mSyncStorageEngine.getMasterSyncAutomatically(); - if (!(manualSync || (backgroundDataUsageAllowed && syncAutomatically))) { + if (!(ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically))) { return false; } @@ -1525,7 +1529,7 @@ public class SyncManager implements OnAccountsUpdateListener { // If the accounts aren't known yet then we aren't ready to run. We will be kicked // when the account lookup request does complete. Account[] accounts = mAccounts; - if (accounts == null) { + if (accounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "runStateIdle: accounts not known, skipping"); } @@ -1553,8 +1557,9 @@ public class SyncManager implements OnAccountsUpdateListener { // from the queue now mSyncQueue.remove(op); - if (!isSyncAllowed(op.account, op.authority, - op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false), + final boolean ignoreSettings = op.extras + .getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); + if (!isSyncAllowed(op.account, op.authority, ignoreSettings, backgroundDataUsageAllowed)) { continue; } @@ -1609,7 +1614,7 @@ public class SyncManager implements OnAccountsUpdateListener { Bundle bestExtras = null; ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); for (SyncStorageEngine.AuthorityInfo info : infos) { - if (!isSyncAllowed(info.account, info.authority, false /* manualSync */, + if (!isSyncAllowed(info.account, info.authority, false /* ignoreSettings */, backgroundDataUsageAllowed)) { continue; } @@ -1881,7 +1886,6 @@ public class SyncManager implements OnAccountsUpdateListener { // in each of these cases the sync loop will be kicked, which will cause this // method to be called again if (!mDataConnectionIsConnected) return; - if (mAccounts == null) return; if (mStorageIsLow) return; final long now = SystemClock.elapsedRealtime(); @@ -1895,7 +1899,7 @@ public class SyncManager implements OnAccountsUpdateListener { if (activeSyncContext == null) { synchronized (mSyncQueue) { Pair<SyncOperation, Long> candidate = bestSyncOperationCandidate(); - alarmTime = candidate != null ? candidate.second : 0; + alarmTime = candidate != null ? candidate.second : null; } } else { final long notificationTime = @@ -2040,18 +2044,4 @@ public class SyncManager implements OnAccountsUpdateListener { resultMessage, downstreamActivity, upstreamActivity); } } - - public static long runTimeWithBackoffs(SyncStorageEngine syncStorageEngine, - Account account, String authority, boolean isManualSync, long runTime) { - // if this is a manual sync, the run time is unchanged - // otherwise, the run time is the max of the backoffs and the run time. - if (isManualSync) { - return runTime; - } - - Pair<Long, Long> backoff = syncStorageEngine.getBackoff(account, authority); - long delayUntilTime = syncStorageEngine.getDelayUntilTime(account, authority); - - return Math.max(Math.max(runTime, delayUntilTime), backoff != null ? backoff.first : 0); - } } diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java index 2d6e833..4599165 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -26,6 +26,9 @@ public class SyncOperation implements Comparable { this.extras = new Bundle(extras); removeFalseExtra(ContentResolver.SYNC_EXTRAS_UPLOAD); removeFalseExtra(ContentResolver.SYNC_EXTRAS_MANUAL); + removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS); + removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); + removeFalseExtra(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY); removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS); removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED); removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS); diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java index 2eead3a..bb21488 100644 --- a/core/java/android/content/SyncQueue.java +++ b/core/java/android/content/SyncQueue.java @@ -113,10 +113,14 @@ public class SyncQueue { SyncOperation best = null; long bestRunTime = 0; for (SyncOperation op : mOperationsMap.values()) { - long opRunTime = SyncManager.runTimeWithBackoffs(mSyncStorageEngine, op.account, - op.authority, - op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false), - op.earliestRunTime); + long opRunTime = op.earliestRunTime; + if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { + Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(op.account, op.authority); + long delayUntil = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority); + opRunTime = Math.max( + Math.max(opRunTime, delayUntil), + backoff != null ? backoff.first : 0); + } // if the expedited state of both ops are the same then compare their runtime. // Otherwise the candidate is only better than the current best if the candidate // is expedited. diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 808c839..6591313 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -270,6 +270,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Full paths to the locations of extra resource packages this application * uses. This field is only used if there are extra resource packages, * otherwise it is null. + * + * {@hide} */ public String[] resourceDirs; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 54db5e0..2c8c112 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -305,4 +305,5 @@ interface IPackageManager { */ void updateExternalMediaStatus(boolean mounted); + String nextPackageToClean(String lastPackage); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 745628a..fca8588 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -437,6 +437,15 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the new package couldn't be installed in the specified install + * location. + * @hide + */ + public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19; + + /** * 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 @@ -613,6 +622,13 @@ public abstract class PackageManager { public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; /** + * Action to external storage service to clean out removed apps. + * @hide + */ + public static final String ACTION_CLEAN_EXTERNAL_STORAGE + = "android.content.pm.CLEAN_EXTERNAL_STORAGE"; + + /** * Determines best place to install an application: either SD or internal FLASH. * Tweak the algorithm for best results. * @param appInfo ApplicationInfo object of the package to install. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index bac55cc..b31df32 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2138,7 +2138,7 @@ public class PackageParser { havePerm = true; } if (writePermission != null) { - writePermission = readPermission.intern(); + writePermission = writePermission.intern(); havePerm = true; } diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 7362394..a885820 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -117,8 +117,8 @@ public abstract class RegisteredServicesCache<V> { mContext.registerReceiver(receiver, intentFilter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(receiver, sdFilter); } diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java index f64261c..7776520 100644 --- a/core/java/android/database/sqlite/SQLiteClosable.java +++ b/core/java/android/database/sqlite/SQLiteClosable.java @@ -29,7 +29,7 @@ public abstract class SQLiteClosable { synchronized(mLock) { if (mReferenceCount <= 0) { throw new IllegalStateException( - "attempt to acquire a reference on a close SQLiteClosable"); + "attempt to acquire a reference on an already-closed SQLiteClosable obj."); } mReferenceCount++; } diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 22e2a83..5d1e7cf 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -66,64 +66,60 @@ public class SQLiteDatabase extends SQLiteClosable { * Algorithms used in ON CONFLICT clause * http://www.sqlite.org/lang_conflict.html */ - public static final class ConflictAlgorithm { - /** - * When a constraint violation occurs, an immediate ROLLBACK occurs, - * thus ending the current transaction, and the command aborts with a - * return code of SQLITE_CONSTRAINT. If no transaction is active - * (other than the implied transaction that is created on every command) - * then this algorithm works the same as ABORT. - */ - public static final int ROLLBACK = 1; - - /** - * When a constraint violation occurs,no ROLLBACK is executed - * so changes from prior commands within the same transaction - * are preserved. This is the default behavior. - */ - public static final int ABORT = 2; + /** + * When a constraint violation occurs, an immediate ROLLBACK occurs, + * thus ending the current transaction, and the command aborts with a + * return code of SQLITE_CONSTRAINT. If no transaction is active + * (other than the implied transaction that is created on every command) + * then this algorithm works the same as ABORT. + */ + public static final int CONFLICT_ROLLBACK = 1; - /** - * When a constraint violation occurs, the command aborts with a return - * code SQLITE_CONSTRAINT. But any changes to the database that - * the command made prior to encountering the constraint violation - * are preserved and are not backed out. - */ - public static final int FAIL = 3; + /** + * When a constraint violation occurs,no ROLLBACK is executed + * so changes from prior commands within the same transaction + * are preserved. This is the default behavior. + */ + public static final int CONFLICT_ABORT = 2; - /** - * When a constraint violation occurs, the one row that contains - * the constraint violation is not inserted or changed. - * But the command continues executing normally. Other rows before and - * after the row that contained the constraint violation continue to be - * inserted or updated normally. No error is returned. - */ - public static final int IGNORE = 4; + /** + * When a constraint violation occurs, the command aborts with a return + * code SQLITE_CONSTRAINT. But any changes to the database that + * the command made prior to encountering the constraint violation + * are preserved and are not backed out. + */ + public static final int CONFLICT_FAIL = 3; - /** - * When a UNIQUE constraint violation occurs, the pre-existing rows that - * are causing the constraint violation are removed prior to inserting - * or updating the current row. Thus the insert or update always occurs. - * The command continues executing normally. No error is returned. - * If a NOT NULL constraint violation occurs, the NULL value is replaced - * by the default value for that column. If the column has no default - * value, then the ABORT algorithm is used. If a CHECK constraint - * violation occurs then the IGNORE algorithm is used. When this conflict - * resolution strategy deletes rows in order to satisfy a constraint, - * it does not invoke delete triggers on those rows. - * This behavior might change in a future release. - */ - public static final int REPLACE = 5; + /** + * When a constraint violation occurs, the one row that contains + * the constraint violation is not inserted or changed. + * But the command continues executing normally. Other rows before and + * after the row that contained the constraint violation continue to be + * inserted or updated normally. No error is returned. + */ + public static final int CONFLICT_IGNORE = 4; - /** - * use the following when no conflict action is specified. - */ - public static final int NONE = 0; - private static final String[] VALUES = new String[] - {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "}; + /** + * When a UNIQUE constraint violation occurs, the pre-existing rows that + * are causing the constraint violation are removed prior to inserting + * or updating the current row. Thus the insert or update always occurs. + * The command continues executing normally. No error is returned. + * If a NOT NULL constraint violation occurs, the NULL value is replaced + * by the default value for that column. If the column has no default + * value, then the ABORT algorithm is used. If a CHECK constraint + * violation occurs then the IGNORE algorithm is used. When this conflict + * resolution strategy deletes rows in order to satisfy a constraint, + * it does not invoke delete triggers on those rows. + * This behavior might change in a future release. + */ + public static final int CONFLICT_REPLACE = 5; - private ConflictAlgorithm() {} // disable instantiation of this class - } + /** + * use the following when no conflict action is specified. + */ + public static final int CONFLICT_NONE = 0; + private static final String[] CONFLICT_VALUES = new String[] + {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "}; /** * Maximum Length Of A LIKE Or GLOB Pattern @@ -290,10 +286,6 @@ public class SQLiteDatabase extends SQLiteClosable { @Override protected void onAllReferencesReleased() { if (isOpen()) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " + - getDatabaseName(mPath) + ";"); - } if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); } @@ -782,7 +774,14 @@ public class SQLiteDatabase extends SQLiteClosable { SQLiteDatabase db = null; try { // Open the database. - return new SQLiteDatabase(path, factory, flags); + SQLiteDatabase sqliteDatabase = new SQLiteDatabase(path, factory, flags); + if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { + sqliteDatabase.enableSqlTracing(path); + } + if (SQLiteDebug.DEBUG_SQL_TIME) { + sqliteDatabase.enableSqlProfiling(path); + } + return sqliteDatabase; } catch (SQLiteDatabaseCorruptException e) { // Try to recover from this, if we can. // TODO: should we do this for other open failures? @@ -1338,7 +1337,7 @@ public class SQLiteDatabase extends SQLiteClosable { */ public long insert(String table, String nullColumnHack, ContentValues values) { try { - return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE); + return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); } catch (SQLException e) { Log.e(TAG, "Error inserting " + values, e); return -1; @@ -1360,7 +1359,7 @@ public class SQLiteDatabase extends SQLiteClosable { */ public long insertOrThrow(String table, String nullColumnHack, ContentValues values) throws SQLException { - return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE); + return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); } /** @@ -1377,7 +1376,7 @@ public class SQLiteDatabase extends SQLiteClosable { public long replace(String table, String nullColumnHack, ContentValues initialValues) { try { return insertWithOnConflict(table, nullColumnHack, initialValues, - ConflictAlgorithm.REPLACE); + CONFLICT_REPLACE); } catch (SQLException e) { Log.e(TAG, "Error inserting " + initialValues, e); return -1; @@ -1399,7 +1398,7 @@ public class SQLiteDatabase extends SQLiteClosable { public long replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues) throws SQLException { return insertWithOnConflict(table, nullColumnHack, initialValues, - ConflictAlgorithm.REPLACE); + CONFLICT_REPLACE); } /** @@ -1412,10 +1411,10 @@ public class SQLiteDatabase extends SQLiteClosable { * @param initialValues this map contains the initial column values for the * row. The keys should be the column names and the values the * column values - * @param conflictAlgorithm {@link ConflictAlgorithm} for insert conflict resolver + * @param conflictAlgorithm for insert conflict resolver * @return the row ID of the newly inserted row * OR the primary key of the existing row if the input param 'conflictAlgorithm' = - * {@link ConflictAlgorithm#IGNORE} + * {@link #CONFLICT_IGNORE} * OR -1 if any error */ public long insertWithOnConflict(String table, String nullColumnHack, @@ -1427,7 +1426,7 @@ public class SQLiteDatabase extends SQLiteClosable { // Measurements show most sql lengths <= 152 StringBuilder sql = new StringBuilder(152); sql.append("INSERT"); - sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]); + sql.append(CONFLICT_VALUES[conflictAlgorithm]); sql.append(" INTO "); sql.append(table); // Measurements show most values lengths < 40 @@ -1551,7 +1550,7 @@ public class SQLiteDatabase extends SQLiteClosable { * @return the number of rows affected */ public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { - return updateWithOnConflict(table, values, whereClause, whereArgs, ConflictAlgorithm.NONE); + return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE); } /** @@ -1562,7 +1561,7 @@ public class SQLiteDatabase extends SQLiteClosable { * valid value that will be translated to NULL. * @param whereClause the optional WHERE clause to apply when updating. * Passing null will update all rows. - * @param conflictAlgorithm {@link ConflictAlgorithm} for update conflict resolver + * @param conflictAlgorithm for update conflict resolver * @return the number of rows affected */ public int updateWithOnConflict(String table, ContentValues values, @@ -1577,7 +1576,7 @@ public class SQLiteDatabase extends SQLiteClosable { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); - sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]); + sql.append(CONFLICT_VALUES[conflictAlgorithm]); sql.append(table); sql.append(" SET "); @@ -1652,9 +1651,6 @@ public class SQLiteDatabase extends SQLiteClosable { */ public void execSQL(String sql) throws SQLException { long timeStart = Debug.threadCpuTimeNanos(); - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, null)); - } lock(); try { native_execSQL(sql); @@ -1680,9 +1676,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (bindArgs == null) { throw new IllegalArgumentException("Empty bindArgs"); } - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, bindArgs)); - } long timeStart = Debug.threadCpuTimeNanos(); lock(); SQLiteStatement statement = null; @@ -1741,10 +1734,6 @@ public class SQLiteDatabase extends SQLiteClosable { mLeakedException = new IllegalStateException(path + " SQLiteDatabase created and never closed"); mFactory = factory; - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.d(TAG, "captured_sql|" + mPath + "|ATTACH DATABASE '" + mPath + - "' as " + getDatabaseName(mPath) + ";"); - } dbopen(mPath, mFlags); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeOpened = getTime(); @@ -1754,10 +1743,6 @@ public class SQLiteDatabase extends SQLiteClosable { setLocale(Locale.getDefault()); } catch (RuntimeException e) { Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e); - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " + - getDatabaseName(mPath) + ";"); - } dbclose(); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); @@ -1770,20 +1755,6 @@ public class SQLiteDatabase extends SQLiteClosable { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis()); } - private String getDatabaseName(String path) { - if (path == null || path.trim().length() == 0) { - return "db not specified?"; - } - - if (path.equalsIgnoreCase(":memory:")) { - return "memorydb"; - } - String[] tokens = path.split("/"); - String[] lastNodeTokens = tokens[tokens.length - 1].split("\\.", 2); - return (lastNodeTokens.length == 1) ? lastNodeTokens[0] - : lastNodeTokens[0] + lastNodeTokens[1]; - } - /** * return whether the DB is opened as read only. * @return true if DB is opened as read only @@ -1909,8 +1880,7 @@ public class SQLiteDatabase extends SQLiteClosable { */ Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + getPath() + "; i.e., NO space for this sql statement in cache: " + - sql + ". Make sure your sql " + - "statements are using prepared-sql-statement syntax with '?' for " + + sql + ". Please change your sql statements to use '?' for " + "bindargs, instead of using actual values"); /* increment the number of times this warnings has been printed. @@ -2062,6 +2032,23 @@ public class SQLiteDatabase extends SQLiteClosable { private native void dbopen(String path, int flags); /** + * Native call to setup tracing of all sql statements + * + * @param path the full path to the database + */ + private native void enableSqlTracing(String path); + + /** + * Native call to setup profiling of all sql statements. + * currently, sqlite's profiling = printing of execution-time + * (wall-clock time) of each of the sql statements, as they + * are executed. + * + * @param path the full path to the database + */ + private native void enableSqlProfiling(String path); + + /** * Native call to execute a raw SQL statement. {@link #lock} must be held * when calling this method. * diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java index b12034a..4ea680e 100644 --- a/core/java/android/database/sqlite/SQLiteDebug.java +++ b/core/java/android/database/sqlite/SQLiteDebug.java @@ -31,17 +31,17 @@ public final class SQLiteDebug { Log.isLoggable("SQLiteStatements", Log.VERBOSE); /** - * Controls the printing of compiled-sql-statement cache stats. + * Controls the printing of wall-clock time taken to execute SQL statements + * as they are executed. */ - public static final boolean DEBUG_SQL_CACHE = - Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE); + public static final boolean DEBUG_SQL_TIME = + Log.isLoggable("SQLiteTime", Log.VERBOSE); /** - * Controls the capturing and printing of complete sql statement including the bind args and - * the database name. + * Controls the printing of compiled-sql-statement cache stats. */ - public static final boolean DEBUG_CAPTURE_SQL = - Log.isLoggable("SQLiteCaptureSql", Log.VERBOSE); + public static final boolean DEBUG_SQL_CACHE = + Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE); /** * Controls the stack trace reporting of active cursors being @@ -121,62 +121,4 @@ public final class SQLiteDebug { static synchronized void notifyActiveCursorFinalized() { sNumActiveCursorsFinalized++; } - - /** - * returns a message containing the given database name (path) and the string built by - * replacing "?" characters in the given sql string with the corresponding - * positional values from the given param bindArgs. - * - * @param path the database name - * @param sql sql string with possibly "?" for bindargs - * @param bindArgs args for "?"s in the above string - * @return the String to be logged - */ - /* package */ static String captureSql(String path, String sql, Object[] bindArgs) { - // how many bindargs in sql - sql = sql.trim(); - String args[] = sql.split("\\?"); - // how many "?"s in the given sql string? - int varArgsInSql = (sql.endsWith("?")) ? args.length : args.length - 1; - - // how many bind args do we have in the given input param bindArgs - int bindArgsLen = (bindArgs == null) ? 0 : bindArgs.length; - if (varArgsInSql < bindArgsLen) { - return "too many bindArgs provided. " + - "# of bindArgs = " + bindArgsLen + ", # of varargs = " + varArgsInSql + - "; sql = " + sql; - } - - // if there are no bindArgs, we are done. log the sql as is. - if (bindArgsLen == 0 && varArgsInSql == 0) { - return logSql(path, sql); - } - - StringBuilder buf = new StringBuilder(); - - // take the supplied bindArgs and plug them into sql - for (int i = 0; i < bindArgsLen; i++) { - buf.append(args[i]); - buf.append(bindArgs[i]); - } - - // does given sql have more varArgs than the supplied bindArgs - // if so, assign nulls to the extra varArgs in sql - for (int i = bindArgsLen; i < varArgsInSql; i ++) { - buf.append(args[i]); - buf.append("null"); - } - - // if there are any characters left in the given sql string AFTER the last "?" - // log them also. for example, if the given sql = "select * from test where a=? and b=1 - // then the following code appends " and b=1" string to buf. - if (varArgsInSql < args.length) { - buf.append(args[varArgsInSql]); - } - return logSql(path, buf.toString()); - } - - private static String logSql(String path, String sql) { - return "captured_sql|" + path + "|" + sql + ";"; - } } diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 1159c1d..2d0aa39 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -16,19 +16,10 @@ package android.database.sqlite; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import android.util.Log; - /** * A base class for compiled SQLite programs. */ public abstract class SQLiteProgram extends SQLiteClosable { - private static final String TAG = "SQLiteProgram"; /** The database this program is compiled against. */ protected SQLiteDatabase mDatabase; @@ -53,16 +44,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ protected int nStatement = 0; - /** - * stores all bindargs for debugging purposes - */ - private Map<Integer, String> mBindArgs = null; - /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.d(TAG, "processing sql: " + sql); - } - mDatabase = db; mSql = sql; db.acquireReference(); @@ -120,7 +102,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { } /** - * @deprecated use this.compiledStatement.compile instead + * @deprecated This method is deprecated and must not be used. * * @param sql the SQL string to compile * @param forceCompilation forces the SQL to be recompiled in the event that there is an @@ -138,9 +120,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param index The 1-based index to the parameter to bind null to */ public void bindNull(int index) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - addToBindArgs(index, "null"); - } acquireReference(); try { native_bind_null(index); @@ -157,9 +136,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindLong(int index, long value) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - addToBindArgs(index, value + ""); - } acquireReference(); try { native_bind_long(index, value); @@ -176,9 +152,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindDouble(int index, double value) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - addToBindArgs(index, value + ""); - } acquireReference(); try { native_bind_double(index, value); @@ -195,9 +168,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindString(int index, String value) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - addToBindArgs(index, "'" + value + "'"); - } if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } @@ -217,9 +187,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindBlob(int index, byte[] value) { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - addToBindArgs(index, "blob"); - } if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } @@ -235,9 +202,6 @@ public abstract class SQLiteProgram extends SQLiteClosable { * Clears all existing bindings. Unset bindings are treated as NULL. */ public void clearBindings() { - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - mBindArgs = null; - } acquireReference(); try { native_clear_bindings(); @@ -259,39 +223,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { } /** - * this method is called under the debug flag {@link SQLiteDebug.DEBUG_CAPTURE_SQL} only. - * it collects the bindargs as they are called by the callers the bind... methods in this - * class. - */ - private void addToBindArgs(int index, String obj) { - if (mBindArgs == null) { - mBindArgs = new HashMap<Integer, String>(); - } - mBindArgs.put(index, obj); - } - - /** - * constructs all the bindargs in sequence and returns a String Array of the values. - * it uses the HashMap built up by the above method. - * - * @return the string array of bindArgs with the args arranged in sequence - */ - /* package */ String[] getBindArgs() { - if (mBindArgs == null) { - return null; - } - Set<Integer> indexSet = mBindArgs.keySet(); - ArrayList<Integer> indexList = new ArrayList<Integer>(indexSet); - Collections.sort(indexList); - int len = indexList.size(); - String[] bindObjs = new String[len]; - for (int i = 0; i < len; i++) { - bindObjs[i] = mBindArgs.get(indexList.get(i)); - } - return bindObjs; - } - - /** + * @deprecated This method is deprecated and must not be used. * Compiles SQL into a SQLite program. * * <P>The database lock must be held when calling this method. @@ -299,6 +231,10 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ @Deprecated protected final native void native_compile(String sql); + + /** + * @deprecated This method is deprecated and must not be used. + */ @Deprecated protected final native void native_finalize(); diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index c34661d..5bcad4b 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -18,7 +18,6 @@ package android.database.sqlite; import android.database.CursorWindow; import android.os.Debug; -import android.os.SystemClock; import android.util.Log; /** diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index 0cee3c5..f1f5a2a 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -17,7 +17,6 @@ package android.database.sqlite; import android.os.Debug; -import android.util.Log; /** * A pre-compiled statement against a {@link SQLiteDatabase} that can be reused. @@ -27,8 +26,6 @@ import android.util.Log; */ public class SQLiteStatement extends SQLiteProgram { - private static final String TAG = "SQLiteStatement"; - /** * Don't use SQLiteStatement constructor directly, please use * {@link SQLiteDatabase#compileStatement(String)} @@ -52,12 +49,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "execute() for [" + mSql + "]"); - } - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs())); - } native_execute(); mDatabase.logTimeStat(mSql, timeStart); } finally { @@ -82,12 +73,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "executeInsert() for [" + mSql + "]"); - } - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs())); - } native_execute(); mDatabase.logTimeStat(mSql, timeStart); return mDatabase.lastInsertRow(); @@ -111,12 +96,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "simpleQueryForLong() for [" + mSql + "]"); - } - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs())); - } long retValue = native_1x1_long(); mDatabase.logTimeStat(mSql, timeStart); return retValue; @@ -140,12 +119,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "simpleQueryForString() for [" + mSql + "]"); - } - if (SQLiteDebug.DEBUG_CAPTURE_SQL) { - Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs())); - } String retValue = native_1x1_string(); mDatabase.logTimeStat(mSql, timeStart); return retValue; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 47c2cac..c0bff66 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -723,6 +723,7 @@ public class Camera { private static final String KEY_FOCAL_LENGTH = "focal-length"; private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle"; private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle"; + private static final String KEY_EXPOSURE_COMPENSATION = "exposure-compensation"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -1051,7 +1052,8 @@ public class Camera { } /** - * Sets the rate at which preview frames are received. + * Sets the rate at which preview frames are received. This is the + * target frame rate. The actual frame rate depends on the driver. * * @param fps the frame rate (frames per second) */ @@ -1060,8 +1062,9 @@ public class Camera { } /** - * Returns the setting for the rate at which preview frames - * are received. + * Returns the setting for the rate at which preview frames are + * received. This is the target frame rate. The actual frame rate + * depends on the driver. * * @return the frame rate setting (frames per second) */ @@ -1540,6 +1543,41 @@ public class Camera { } /** + * Gets the current exposure compensation setting. + * + * @return the current exposure compensation value multiplied by 100. + * null if exposure compensation is not supported. Ex: -100 + * means -1 EV. 130 means +1.3 EV. + * @hide + */ + public int getExposureCompensation() { + return getInt(KEY_EXPOSURE_COMPENSATION); + } + + /** + * Sets the exposure compensation. + * + * @param value exposure compensation multiplied by 100. Ex: -100 means + * -1 EV. 130 means +1.3 EV. + * @hide + */ + public void setExposureCompensation(int value) { + set(KEY_EXPOSURE_COMPENSATION, value); + } + + /** + * Gets the supported exposure compensation. + * + * @return a List of Integer constants. null if exposure compensation is + * not supported. The list is sorted from small to large. Ex: + * -100, -66, -33, 0, 33, 66, 100. + * @hide + */ + public List<Integer> getSupportedExposureCompensation() { + return splitInt(get(KEY_EXPOSURE_COMPENSATION + SUPPORTED_VALUES_SUFFIX)); + } + + /** * Gets current zoom value. This also works when smooth zoom is in * progress. * diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index b0c3909..28a86b8 100755..100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -620,7 +620,10 @@ public class KeyboardView extends View implements View.OnClickListener { if (mBuffer == null || mKeyboardChanged) { if (mBuffer == null || mKeyboardChanged && (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { - mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + // Make sure our bitmap is at least 1x1 + final int width = Math.max(1, getWidth()); + final int height = Math.max(1, getHeight()); + mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBuffer); } invalidateAllKeys(); @@ -739,6 +742,10 @@ public class KeyboardView extends View implements View.OnClickListener { final Key key = keys[nearestKeyIndices[i]]; int dist = 0; boolean isInside = key.isInside(x,y); + if (isInside) { + primaryIndex = nearestKeyIndices[i]; + } + if (((mProximityCorrectOn && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) || isInside) @@ -767,10 +774,6 @@ public class KeyboardView extends View implements View.OnClickListener { } } } - - if (isInside) { - primaryIndex = nearestKeyIndices[i]; - } } if (primaryIndex == NOT_A_KEY) { primaryIndex = closestKey; diff --git a/core/java/android/os/Base64Utils.java b/core/java/android/os/Base64Utils.java deleted file mode 100644 index 684a469..0000000 --- a/core/java/android/os/Base64Utils.java +++ /dev/null @@ -1,31 +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 android.os; - -/** - * {@hide} - */ -public class Base64Utils -{ - // TODO add encode api here if possible - - public static byte [] decodeBase64(String data) { - return decodeBase64Native(data); - } - private static native byte[] decodeBase64Native(String data); -} - diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 9491bd4..a9831aa 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -18,7 +18,7 @@ package android.os; import java.io.File; -import android.os.IMountService; +import android.os.storage.IMountService; /** * Provides access to environment variables. @@ -91,6 +91,14 @@ public class Environment { private static final File EXTERNAL_STORAGE_DIRECTORY = getDirectory("EXTERNAL_STORAGE", "/sdcard"); + private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY + = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"), + "Android"), "data"); + + private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY + = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"), + "Android"), "media"); + private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache"); @@ -102,13 +110,183 @@ public class Environment { } /** - * Gets the Android external storage directory. + * Gets the Android external storage directory. This directory may not + * currently be accessible if it has been mounted by the user on their + * computer, has been removed from the device, or some other problem has + * happened. You can determine its current state with + * {@link #getExternalStorageState()}. + * + * <p>Here is an example of typical code to monitor the state of + * external storage:</p> + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * monitor_storage} */ public static File getExternalStorageDirectory() { return EXTERNAL_STORAGE_DIRECTORY; } /** + * Standard directory in which to place any audio files that should be + * in the regular list of music for the user. + * This may be combined with + * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, + * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series + * of directories to categories a particular audio file as more than one + * type. + */ + public static String DIRECTORY_MUSIC = "Music"; + + /** + * Standard directory in which to place any audio files that should be + * in the list of podcasts that the user can select (not as regular + * music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_NOTIFICATIONS}, + * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series + * of directories to categories a particular audio file as more than one + * type. + */ + public static String DIRECTORY_PODCASTS = "Podcasts"; + + /** + * Standard directory in which to place any audio files that should be + * in the list of ringtones that the user can select (not as regular + * music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and + * {@link #DIRECTORY_ALARMS} as a series + * of directories to categories a particular audio file as more than one + * type. + */ + public static String DIRECTORY_RINGTONES = "Ringtones"; + + /** + * Standard directory in which to place any audio files that should be + * in the list of alarms that the user can select (not as regular + * music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, + * and {@link #DIRECTORY_RINGTONES} as a series + * of directories to categories a particular audio file as more than one + * type. + */ + public static String DIRECTORY_ALARMS = "Alarms"; + + /** + * Standard directory in which to place any audio files that should be + * in the list of notifications that the user can select (not as regular + * music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series + * of directories to categories a particular audio file as more than one + * type. + */ + public static String DIRECTORY_NOTIFICATIONS = "Notifications"; + + /** + * Standard directory in which to place pictures that are available to + * the user. Note that this is primarily a convention for the top-level + * public directory, as the media scanner will find and collect pictures + * in any directory. + */ + public static String DIRECTORY_PICTURES = "Pictures"; + + /** + * Standard directory in which to place movies that are available to + * the user. Note that this is primarily a convention for the top-level + * public directory, as the media scanner will find and collect movies + * in any directory. + */ + public static String DIRECTORY_MOVIES = "Movies"; + + /** + * Standard directory in which to place files that have been downloaded by + * the user. Note that this is primarily a convention for the top-level + * public directory, you are free to download files anywhere in your own + * private directories. + */ + public static String DIRECTORY_DOWNLOADS = "Downloads"; + + /** + * The traditional location for pictures and videos when mounting the + * device as a camera. Note that this is primarily a convention for the + * top-level public directory, as this convention makes no sense elsewhere. + */ + public static String DIRECTORY_DCIM = "DCIM"; + + /** + * Get a top-level public external storage directory for placing files of + * a particular type. This is where the user will typically place and + * manage their own files, so you should be careful about what you put here + * to ensure you don't erase their files or get in the way of their own + * organization. + * + * <p>Here is an example of typical code to manipulate a picture on + * the public external storage:</p> + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * public_picture} + * + * @param type The type of storage directory to return. Should be one of + * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, + * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or + * {@link #DIRECTORY_DCIM}. May not be null. + * + * @return Returns the File path for the directory. Note that this + * directory may not yet exist, so you must make sure it exists before + * using it such as with {@link File#mkdirs File.mkdirs()}. + */ + public static File getExternalStoragePublicDirectory(String type) { + return new File(getExternalStorageDirectory(), type); + } + + /** + * Returns the path for android-specific data on the SD card. + * @hide + */ + public static File getExternalStorageAndroidDataDir() { + return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY; + } + + /** + * Generates the raw path to an application's data + * @hide + */ + public static File getExternalStorageAppDataDirectory(String packageName) { + return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName); + } + + /** + * Generates the raw path to an application's media + * @hide + */ + public static File getExternalStorageAppMediaDirectory(String packageName) { + return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName); + } + + /** + * Generates the path to an application's files. + * @hide + */ + public static File getExternalStorageAppFilesDirectory(String packageName) { + return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, + packageName), "files"); + } + + /** + * Generates the path to an application's cache. + * @hide + */ + public static File getExternalStorageAppCacheDirectory(String packageName) { + return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, + packageName), "cache"); + } + + /** * Gets the Android Download/Cache content directory. */ public static File getDownloadCacheDirectory() { @@ -173,6 +351,8 @@ public class Environment { * Gets the current state of the external storage device. * Note: This call should be deprecated as it doesn't support * multiple volumes. + * + * <p>See {@link #getExternalStorageDirectory()} for an example of its use. */ public static String getExternalStorageState() { try { diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java index 3457815..7e99f38 100644 --- a/core/java/android/os/FileObserver.java +++ b/core/java/android/os/FileObserver.java @@ -52,73 +52,75 @@ public abstract class FileObserver { public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE - | DELETE_SELF | MOVE_SELF; + | DELETE_SELF | MOVE_SELF; private static final String LOG_TAG = "FileObserver"; private static class ObserverThread extends Thread { - private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>(); - private int m_fd; - - public ObserverThread() { - super("FileObserver"); - m_fd = init(); - } - - public void run() { - observe(m_fd); - } - - public int startWatching(String path, int mask, FileObserver observer) { - int wfd = startWatching(m_fd, path, mask); - - Integer i = new Integer(wfd); - if (wfd >= 0) { - synchronized (m_observers) { - m_observers.put(i, new WeakReference(observer)); - } - } - - return i; - } - - public void stopWatching(int descriptor) { - stopWatching(m_fd, descriptor); - } - - public void onEvent(int wfd, int mask, String path) { - // look up our observer, fixing up the map if necessary... - FileObserver observer; - - synchronized (m_observers) { - WeakReference weak = m_observers.get(wfd); - observer = (FileObserver) weak.get(); - if (observer == null) { - m_observers.remove(wfd); + private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>(); + private int m_fd; + + public ObserverThread() { + super("FileObserver"); + m_fd = init(); + } + + public void run() { + observe(m_fd); + } + + public int startWatching(String path, int mask, FileObserver observer) { + int wfd = startWatching(m_fd, path, mask); + + Integer i = new Integer(wfd); + if (wfd >= 0) { + synchronized (m_observers) { + m_observers.put(i, new WeakReference(observer)); + } } + + return i; } - // ...then call out to the observer without the sync lock held - if (observer != null) { - try { - observer.onEvent(mask, path); - } catch (Throwable throwable) { - Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); + public void stopWatching(int descriptor) { + stopWatching(m_fd, descriptor); + } + + public void onEvent(int wfd, int mask, String path) { + // look up our observer, fixing up the map if necessary... + FileObserver observer = null; + + synchronized (m_observers) { + WeakReference weak = m_observers.get(wfd); + if (weak != null) { // can happen with lots of events from a dead wfd + observer = (FileObserver) weak.get(); + if (observer == null) { + m_observers.remove(wfd); + } + } + } + + // ...then call out to the observer without the sync lock held + if (observer != null) { + try { + observer.onEvent(mask, path); + } catch (Throwable throwable) { + Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); + } } } - } - private native int init(); - private native void observe(int fd); - private native int startWatching(int fd, String path, int mask); - private native void stopWatching(int fd, int wfd); + private native int init(); + private native void observe(int fd); + private native int startWatching(int fd, String path, int mask); + private native void stopWatching(int fd, int wfd); } private static ObserverThread s_observerThread; static { - s_observerThread = new ObserverThread(); - s_observerThread.start(); + s_observerThread = new ObserverThread(); + s_observerThread.start(); } // instance @@ -127,30 +129,30 @@ public abstract class FileObserver { private int m_mask; public FileObserver(String path) { - this(path, ALL_EVENTS); + this(path, ALL_EVENTS); } public FileObserver(String path, int mask) { - m_path = path; - m_mask = mask; - m_descriptor = -1; + m_path = path; + m_mask = mask; + m_descriptor = -1; } protected void finalize() { - stopWatching(); + stopWatching(); } public void startWatching() { - if (m_descriptor < 0) { - m_descriptor = s_observerThread.startWatching(m_path, m_mask, this); - } + if (m_descriptor < 0) { + m_descriptor = s_observerThread.startWatching(m_path, m_mask, this); + } } public void stopWatching() { - if (m_descriptor >= 0) { - s_observerThread.stopWatching(m_descriptor); - m_descriptor = -1; - } + if (m_descriptor >= 0) { + s_observerThread.stopWatching(m_descriptor); + m_descriptor = -1; + } } public abstract void onEvent(int event, String path); diff --git a/core/java/android/os/IMountServiceListener.aidl b/core/java/android/os/IMountServiceListener.aidl deleted file mode 100644 index 3df64b2..0000000 --- a/core/java/android/os/IMountServiceListener.aidl +++ /dev/null @@ -1,66 +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. - */ - -package android.os; - -/** - * Callback class for receiving events from MountService. - * - * @hide - */ -interface IMountServiceListener { - /** - * A sharing method has changed availability state. - * - * @param method The share method which has changed. - * @param available The share availability state. - */ - void onShareAvailabilityChanged(String method, boolean available); - - /** - * Media has been inserted - * - * @param label The volume label. - * @param path The volume mount path. - * @param major The backing device major number. - * @param minor The backing device minor number. - */ - void onMediaInserted(String label, String path, int major, int minor); - - /** - * Media has been removed - * - * @param label The volume label. - * @param path The volume mount path. - * @param major The backing device major number. - * @param minor The backing device minor number. - * @param clean Indicates if the removal was clean (unmounted first). - */ - void onMediaRemoved(String label, String path, int major, int minor, boolean clean); - - /** - * Volume state has changed. - * - * @param label The volume label. - * @param path The volume mount path. - * @param oldState The old state of the volume. - * @param newState The new state of the volume. - * - * Note: State is one of the values returned by Environment.getExternalStorageState() - */ - void onVolumeStateChanged(String label, String path, String oldState, String newState); - -} diff --git a/core/java/android/os/MountServiceListener.java b/core/java/android/os/MountServiceListener.java deleted file mode 100644 index a68f464..0000000 --- a/core/java/android/os/MountServiceListener.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -/** - * Callback class for receiving progress reports during a restore operation. These - * methods will all be called on your application's main thread. - * @hide - */ -public abstract class MountServiceListener { - /** - * A sharing method has changed availability state. - * - * @param method The share method which has changed. - * @param available The share availability state. - */ - void shareAvailabilityChange(String method, boolean available) { - } - - /** - * Media has been inserted - * - * @param label The volume label. - * @param path The volume mount path. - * @param major The backing device major number. - * @param minor The backing device minor number. - */ - void mediaInserted(String label, String path, int major, int minor) { - } - - /** - * Media has been removed - * - * @param label The volume label. - * @param path The volume mount path. - * @param major The backing device major number. - * @param minor The backing device minor number. - * @param clean Indicates if the removal was clean (unmounted first). - */ - void mediaRemoved(String label, String path, int major, int minor, boolean clean) { - } - - /** - * Volume state has changed. - * - * @param label The volume label. - * @param path The volume mount path. - * @param oldState The old state of the volume. - * @param newState The new state of the volume. - * - * Note: State is one of the values returned by Environment.getExternalStorageState() - */ - void volumeStateChange(String label, String path, String oldState, String newState) { - } -} diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index bc76180..b3df522 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -18,7 +18,7 @@ package android.os; import java.io.IOException; import android.os.ServiceManager; -import android.os.IMountService; +import android.os.storage.IMountService; /** * Class that provides access to some of the power management functions. diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl index a5828f6..3d1ef25 100644 --- a/core/java/android/os/IMountService.aidl +++ b/core/java/android/os/storage/IMountService.aidl @@ -15,14 +15,15 @@ ** limitations under the License. */ -package android.os; +package android.os.storage; -import android.os.IMountServiceListener; +import android.os.storage.IMountServiceListener; /** WARNING! Update IMountService.h and IMountService.cpp if you change this file. * In particular, the ordering of the methods below must match the * _TRANSACTION enum in IMountService.cpp - * @hide + * @hide - Applications should use android.os.storage.StorageManager to access + * storage functions. */ interface IMountService { @@ -38,31 +39,19 @@ interface IMountService void unregisterListener(IMountServiceListener listener); /** - * Gets an Array of supported share methods + * Returns true if a USB mass storage host is connected */ - String[] getShareMethodList(); + boolean isUsbMassStorageConnected(); /** - * Returns true if the share method is available + * Enables / disables USB mass storage. */ - boolean getShareMethodAvailable(String method); + int setUsbMassStorageEnabled(boolean enable); /** - * Shares a volume via the specified method - * Returns an int consistent with MountServiceResultCode - */ - int shareVolume(String path, String method); - - /** - * Unshares a volume via the specified method - * Returns an int consistent with MountServiceResultCode + * Returns true if a USB mass storage host is enabled (media is shared) */ - int unshareVolume(String path, String method); - - /** - * Returns true if the volume is shared via the specified method. - */ - boolean getVolumeShared(String path, String method); + boolean isUsbMassStorageEnabled(); /** * Mount external storage at given mount point. @@ -120,6 +109,11 @@ interface IMountService int unmountSecureContainer(String id); /* + * Returns true if the specified container is mounted + */ + boolean isSecureContainerMounted(String id); + + /* * Rename an unmounted secure container. * Returns an int consistent with MountServiceResultCode */ diff --git a/core/java/android/os/storage/IMountServiceListener.aidl b/core/java/android/os/storage/IMountServiceListener.aidl new file mode 100644 index 0000000..883413a --- /dev/null +++ b/core/java/android/os/storage/IMountServiceListener.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.os.storage; + +/** + * Callback class for receiving events from MountService. + * + * @hide - Applications should use android.os.storage.IStorageEventListener + * for storage event callbacks. + */ +interface IMountServiceListener { + /** + * Detection state of USB Mass Storage has changed + * + * @param available true if a UMS host is connected. + */ + void onUsbMassStorageConnectionChanged(boolean connected); + + /** + * Storage state has changed. + * + * @param path The volume mount path. + * @param oldState The old state of the volume. + * @param newState The new state of the volume. + * + * Note: State is one of the values returned by Environment.getExternalStorageState() + */ + void onStorageStateChanged(String path, String oldState, String newState); +} diff --git a/core/java/android/os/storage/MountServiceListener.java b/core/java/android/os/storage/MountServiceListener.java new file mode 100644 index 0000000..bebb3f6 --- /dev/null +++ b/core/java/android/os/storage/MountServiceListener.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.storage; + +/** + * Callback class for receiving progress reports during a restore operation. These + * methods will all be called on your application's main thread. + * @hide + */ +public abstract class MountServiceListener { + /** + * USB Mass storage connection state has changed. + * + * @param connected True if UMS is connected. + */ + void onUsbMassStorageConnectionChanged(boolean connected) { + } + + /** + * Storage state has changed. + * + * @param path The volume mount path. + * @param oldState The old state of the volume. + * @param newState The new state of the volume. + * + * @Note: State is one of the values returned by Environment.getExternalStorageState() + */ + void onStorageStateChange(String path, String oldState, String newState) { + } +} diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java new file mode 100644 index 0000000..d3d39d6 --- /dev/null +++ b/core/java/android/os/storage/StorageEventListener.java @@ -0,0 +1,38 @@ +/* + * 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.os.storage; + +/** + * Used for receiving notifications from the StorageManager + */ +public abstract class StorageEventListener { + /** + * Called when the detection state of a USB Mass Storage host has changed. + * @param connected true if the USB mass storage is connected. + */ + public void onUsbMassStorageConnectionChanged(boolean connected) { + } + + /** + * Called when storage has changed state + * @param path the filesystem path for the storage + * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}. + * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}. + */ + public void onStorageStateChanged(String path, String oldState, String newState) { + } +} diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java new file mode 100644 index 0000000..e421ea5 --- /dev/null +++ b/core/java/android/os/storage/StorageManager.java @@ -0,0 +1,297 @@ +/* + * 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.os.storage; + +import android.content.Context; +import android.os.Binder; +import android.os.Bundle; +import android.os.Looper; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.Handler; +import android.os.Message; +import android.os.ServiceManager; +import android.os.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.util.Log; +import android.util.SparseArray; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * StorageManager is the interface to the systems storage service. + * Get an instance of this class by calling + * {@link android.content.Context#getSystemService(java.lang.String)} with an argument + * of {@link android.content.Context#STORAGE_SERVICE}. + * + */ + +public class StorageManager +{ + private static final String TAG = "StorageManager"; + + /* + * Our internal MountService binder reference + */ + private IMountService mMountService; + + /* + * The looper target for callbacks + */ + Looper mTgtLooper; + + /* + * Target listener for binder callbacks + */ + private MountServiceBinderListener mBinderListener; + + /* + * List of our listeners + */ + private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>(); + + private class MountServiceBinderListener extends IMountServiceListener.Stub { + public void onUsbMassStorageConnectionChanged(boolean available) { + final int size = mListeners.size(); + for (int i = 0; i < size; i++) { + mListeners.get(i).sendShareAvailabilityChanged(available); + } + } + + public void onStorageStateChanged(String path, String oldState, String newState) { + final int size = mListeners.size(); + for (int i = 0; i < size; i++) { + mListeners.get(i).sendStorageStateChanged(path, oldState, newState); + } + } + } + + /** + * Private base class for messages sent between the callback thread + * and the target looper handler. + */ + private class StorageEvent { + public static final int EVENT_UMS_CONNECTION_CHANGED = 1; + public static final int EVENT_STORAGE_STATE_CHANGED = 2; + + private Message mMessage; + + public StorageEvent(int what) { + mMessage = Message.obtain(); + mMessage.what = what; + mMessage.obj = this; + } + + public Message getMessage() { + return mMessage; + } + } + + /** + * Message sent on a USB mass storage connection change. + */ + private class UmsConnectionChangedStorageEvent extends StorageEvent { + public boolean available; + + public UmsConnectionChangedStorageEvent(boolean a) { + super(EVENT_UMS_CONNECTION_CHANGED); + available = a; + } + } + + /** + * Message sent on volume state change. + */ + private class StorageStateChangedStorageEvent extends StorageEvent { + public String path; + public String oldState; + public String newState; + + public StorageStateChangedStorageEvent(String p, String oldS, String newS) { + super(EVENT_STORAGE_STATE_CHANGED); + path = p; + oldState = oldS; + newState = newS; + } + } + + /** + * Private class containing sender and receiver code for StorageEvents. + */ + private class ListenerDelegate { + final StorageEventListener mStorageEventListener; + private final Handler mHandler; + + ListenerDelegate(StorageEventListener listener) { + mStorageEventListener = listener; + mHandler = new Handler(mTgtLooper) { + @Override + public void handleMessage(Message msg) { + StorageEvent e = (StorageEvent) msg.obj; + + if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) { + UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e; + mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available); + } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) { + StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e; + mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState); + } else { + Log.e(TAG, "Unsupported event " + msg.what); + } + } + }; + } + + StorageEventListener getListener() { + return mStorageEventListener; + } + + void sendShareAvailabilityChanged(boolean available) { + UmsConnectionChangedStorageEvent e = new UmsConnectionChangedStorageEvent(available); + mHandler.sendMessage(e.getMessage()); + } + + void sendStorageStateChanged(String path, String oldState, String newState) { + StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState); + mHandler.sendMessage(e.getMessage()); + } + } + + /** + * Constructs a StorageManager object through which an application can + * can communicate with the systems mount service. + * + * @param tgtLooper The {@android.os.Looper} which events will be received on. + * + * <p>Applications can get instance of this class by calling + * {@link android.content.Context#getSystemService(java.lang.String)} with an argument + * of {@link android.content.Context#STORAGE_SERVICE}. + * + * @hide + */ + public StorageManager(Looper tgtLooper) throws RemoteException { + mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); + if (mMountService == null) { + Log.e(TAG, "Unable to connect to mount service! - is it running yet?"); + return; + } + mTgtLooper = tgtLooper; + mBinderListener = new MountServiceBinderListener(); + mMountService.registerListener(mBinderListener); + } + + + /** + * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. + * + * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. + * + */ + public void registerListener(StorageEventListener listener) { + if (listener == null) { + return; + } + + synchronized (mListeners) { + mListeners.add(new ListenerDelegate(listener)); + } + } + + /** + * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. + * + * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. + * + */ + public void unregisterListener(StorageEventListener listener) { + if (listener == null) { + return; + } + + synchronized (mListeners) { + final int size = mListeners.size(); + for (int i=0 ; i<size ; i++) { + ListenerDelegate l = mListeners.get(i); + if (l.getListener() == listener) { + mListeners.remove(i); + break; + } + } + } + } + + /** + * Enables USB Mass Storage (UMS) on the device. + * @return an integer value representing the outcome of the operation. + * @see android.os.storage.StorageResultCode + */ + public int enableUsbMassStorage() { + try { + return mMountService.setUsbMassStorageEnabled(true); + } catch (Exception ex) { + Log.e(TAG, "Failed to enable UMS", ex); + } + return StorageResultCode.OperationFailedInternalError; + } + + /** + * Disables USB Mass Storage (UMS) on the device. + * @return an integer value representing the outcome of the operation. + * @see android.os.storage.StorageResultCode + */ + public int disableUsbMassStorage() { + try { + return mMountService.setUsbMassStorageEnabled(false); + } catch (Exception ex) { + Log.e(TAG, "Failed to disable UMS", ex); + } + return StorageResultCode.OperationFailedInternalError; + } + + /** + * Query if a USB Mass Storage (UMS) host is connected. + * @return true if UMS host is connected. + */ + public boolean isUsbMassStorageConnected() { + try { + return mMountService.isUsbMassStorageConnected(); + } catch (Exception ex) { + Log.e(TAG, "Failed to get UMS connection state", ex); + } + return false; + } + + /** + * Query if a USB Mass Storage (UMS) is enabled on the device. + * @return true if UMS host is enabled. + */ + public boolean isUsbMassStorageEnabled() { + try { + return mMountService.isUsbMassStorageEnabled(); + } catch (RemoteException rex) { + Log.e(TAG, "Failed to get UMS enable state", rex); + } + return false; + } +} diff --git a/core/java/android/os/MountServiceResultCode.java b/core/java/android/os/storage/StorageResultCode.java index e71dbf4..584f160 100644 --- a/core/java/android/os/MountServiceResultCode.java +++ b/core/java/android/os/storage/StorageResultCode.java @@ -14,21 +14,47 @@ * limitations under the License. */ -package android.os; - -import java.io.IOException; +package android.os.storage; /** - * Class that provides access to constants returned from MountService APIs - * - * {@hide} + * Class that provides access to constants returned from StorageManager + * and lower level MountService APIs. */ -public class MountServiceResultCode +public class StorageResultCode { + /** + * Operation succeeded. + * @see android.os.storage.StorageManager + */ public static final int OperationSucceeded = 0; + + /** + * Operation failed: Internal error. + * @see android.os.storage.StorageManager + */ public static final int OperationFailedInternalError = -1; + + /** + * Operation failed: Missing media. + * @see android.os.storage.StorageManager + */ public static final int OperationFailedNoMedia = -2; + + /** + * Operation failed: Media is blank. + * @see android.os.storage.StorageManager + */ public static final int OperationFailedMediaBlank = -3; + + /** + * Operation failed: Media is corrupt. + * @see android.os.storage.StorageManager + */ public static final int OperationFailedMediaCorrupt = -4; + + /** + * Operation failed: Media not mounted. + * @see android.os.storage.StorageManager + */ public static final int OperationFailedVolumeNotMounted = -5; } diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 324fbaa..fbc4a81 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -75,8 +75,8 @@ public class SearchManagerService extends ISearchManager.Stub { mContext.registerReceiver(mPackageChangedReceiver, packageFilter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mPackageChangedReceiver, sdFilter); } @@ -96,8 +96,8 @@ public class SearchManagerService extends ISearchManager.Stub { if (Intent.ACTION_PACKAGE_ADDED.equals(action) || Intent.ACTION_PACKAGE_REMOVED.equals(action) || Intent.ACTION_PACKAGE_CHANGED.equals(action) || - Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) || - Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) || + Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { if (DBG) Log.d(TAG, "Got " + action); // Update list of searchable activities getSearchables().buildSearchableList(); diff --git a/core/java/android/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java index 5434887..2f5bcc3 100644 --- a/core/java/android/speech/RecognitionListener.java +++ b/core/java/android/speech/RecognitionListener.java @@ -79,7 +79,8 @@ public interface RecognitionListener { * time between {@link #onBeginningOfSpeech()} and {@link #onResults(Bundle)} when partial * results are ready. This method may be called zero, one or multiple times for each call to * {@link RecognitionManager#startListening(Intent)}, depending on the speech recognition - * service implementation. + * service implementation. To request partial results, use + * {@link RecognizerIntent#EXTRA_PARTIAL_RESULTS} * * @param partialResults the returned results. To retrieve the results in * ArrayList<String> format use {@link Bundle#getStringArrayList(String)} with diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 334b049..241a8b0 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -77,6 +77,7 @@ public class RecognizerIntent { * <li>{@link #EXTRA_PROMPT} * <li>{@link #EXTRA_LANGUAGE} * <li>{@link #EXTRA_MAX_RESULTS} + * <li>{@link #EXTRA_PARTIAL_RESULTS} * </ul> * * <p> Result extras (returned in the result, not to be specified in the request): @@ -166,6 +167,13 @@ public class RecognizerIntent { public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"; /** + * Optional boolean to indicate whether partial results should be returned by the recognizer + * as the user speaks (default is false). The server may ignore a request for partial + * results in some or all cases. + */ + public static final String EXTRA_PARTIAL_RESULTS = "android.speech.extra.PARTIAL_RESULTS"; + + /** * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will * return results to you via the activity results mechanism. Alternatively, if you use this * extra to supply a PendingIntent, the results will be added to its bundle and the diff --git a/core/java/android/storage/StorageEventListener.java b/core/java/android/storage/StorageEventListener.java deleted file mode 100644 index cd71090..0000000 --- a/core/java/android/storage/StorageEventListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.storage; - -/** - * Used for receiving notifications from the StorageManager - */ -public interface StorageEventListener { - /** - * Called when the ability to share a volume has changed. - * @param method the share-method which has changed. - * @param available true if the share is available. - */ - public void onShareAvailabilityChanged(String method, boolean available); - - /** - * Called when media has been inserted - * @param label the system defined label for the volume. - * @param path the filesystem path for the volume. - * @param major the major number of the device. - * @param minor the minor number of the device. - */ - public void onMediaInserted(String label, String path, int major, int minor); - - /** - * Called when media has been removed - * @param label the system defined label for the volume. - * @param path the filesystem path for the volume. - * @param major the major number of the device. - * @param minor the minor number of the device. - * @param clean the media was removed cleanly. - */ - public void onMediaRemoved(String label, String path, int major, int minor, boolean clean); - - /** - * Called when a volume has changed state - * @param label the system defined label for the volume. - * @param path the filesystem path for the volume. - * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}. - * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}. - */ - public void onVolumeStateChanged(String label, String path, String oldState, String newState); -} diff --git a/core/java/android/storage/StorageManager.java b/core/java/android/storage/StorageManager.java deleted file mode 100644 index dd8bd63..0000000 --- a/core/java/android/storage/StorageManager.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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.storage; - -import android.content.Context; -import android.os.Binder; -import android.os.Bundle; -import android.os.Looper; -import android.os.Parcelable; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.os.RemoteException; -import android.os.Handler; -import android.os.Message; -import android.os.ServiceManager; -import android.os.IMountService; -import android.os.IMountServiceListener; -import android.util.Log; -import android.util.SparseArray; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -/** - * Class that lets you access the device's storage management functions. Get an instance of this - * class by calling {@link android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with an argument of {@link android.content.Context#STORAGE_SERVICE}. - */ -public class StorageManager -{ - private static final String TAG = "StorageManager"; - - /* - * Our internal MountService binder reference - */ - private IMountService mMountService; - - /* - * The looper target for callbacks - */ - Looper mTgtLooper; - - /* - * Target listener for binder callbacks - */ - private MountServiceBinderListener mBinderListener; - - /* - * *static* list of our listeners - */ - static final ArrayList<ListenerDelegate> sListeners = new ArrayList<ListenerDelegate>(); - - private class MountServiceBinderListener extends IMountServiceListener.Stub { - public void onShareAvailabilityChanged(String method, boolean available) { - final int size = sListeners.size(); - for (int i = 0; i < size; i++) { - sListeners.get(i).sendShareAvailabilityChanged(method, available); - } - } - - public void onMediaInserted(String label, String path, int major, int minor) { - final int size = sListeners.size(); - for (int i = 0; i < size; i++) { - sListeners.get(i).sendMediaInserted(label, path, major, minor); - } - } - - public void onMediaRemoved(String label, String path, int major, int minor, boolean clean) { - final int size = sListeners.size(); - for (int i = 0; i < size; i++) { - sListeners.get(i).sendMediaRemoved(label, path, major, minor, clean); - } - } - - public void onVolumeStateChanged(String label, String path, String oldState, String newState) { - final int size = sListeners.size(); - for (int i = 0; i < size; i++) { - sListeners.get(i).sendVolumeStateChanged(label, path, oldState, newState); - } - } - } - - /** - * Private base class for messages sent between the callback thread - * and the target looper handler - */ - private class StorageEvent { - public static final int EVENT_SHARE_AVAILABILITY_CHANGED = 1; - public static final int EVENT_MEDIA_INSERTED = 2; - public static final int EVENT_MEDIA_REMOVED = 3; - public static final int EVENT_VOLUME_STATE_CHANGED = 4; - - private Message mMessage; - - public StorageEvent(int what) { - mMessage = Message.obtain(); - mMessage.what = what; - mMessage.obj = this; - } - - public Message getMessage() { - return mMessage; - } - } - - /** - * Message sent on a share availability change. - */ - private class ShareAvailabilityChangedStorageEvent extends StorageEvent { - public String method; - public boolean available; - - public ShareAvailabilityChangedStorageEvent(String m, boolean a) { - super(EVENT_SHARE_AVAILABILITY_CHANGED); - method = m; - available = a; - } - } - - /** - * Message sent on media insertion - */ - private class MediaInsertedStorageEvent extends StorageEvent { - public String label; - public String path; - public int major; - public int minor; - - public MediaInsertedStorageEvent(String l, String p, int maj, int min) { - super(EVENT_MEDIA_INSERTED); - label = l; - path = p; - major = maj; - minor = min; - } - } - - /** - * Message sent on media removal - */ - private class MediaRemovedStorageEvent extends StorageEvent { - public String label; - public String path; - public int major; - public int minor; - public boolean clean; - - public MediaRemovedStorageEvent(String l, String p, int maj, int min, boolean c) { - super(EVENT_MEDIA_REMOVED); - label = l; - path = p; - major = maj; - minor = min; - clean = c; - } - } - - /** - * Message sent on volume state change - */ - private class VolumeStateChangedStorageEvent extends StorageEvent { - public String label; - public String path; - public String oldState; - public String newState; - - public VolumeStateChangedStorageEvent(String l, String p, String oldS, String newS) { - super(EVENT_VOLUME_STATE_CHANGED); - label = l; - path = p; - oldState = oldS; - newState = newS; - } - } - - /** - * Private class containing sender and receiver code for StorageEvents - */ - private class ListenerDelegate { - final StorageEventListener mStorageEventListener; - private final Handler mHandler; - - ListenerDelegate(StorageEventListener listener) { - mStorageEventListener = listener; - mHandler = new Handler(mTgtLooper) { - @Override - public void handleMessage(Message msg) { - StorageEvent e = (StorageEvent) msg.obj; - - if (msg.what == StorageEvent.EVENT_SHARE_AVAILABILITY_CHANGED) { - ShareAvailabilityChangedStorageEvent ev = (ShareAvailabilityChangedStorageEvent) e; - mStorageEventListener.onShareAvailabilityChanged(ev.method, ev.available); - } else if (msg.what == StorageEvent.EVENT_MEDIA_INSERTED) { - MediaInsertedStorageEvent ev = (MediaInsertedStorageEvent) e; - mStorageEventListener.onMediaInserted(ev.label, ev.path, ev.major, ev.minor); - } else if (msg.what == StorageEvent.EVENT_MEDIA_REMOVED) { - MediaRemovedStorageEvent ev = (MediaRemovedStorageEvent) e; - mStorageEventListener.onMediaRemoved(ev.label, ev.path, ev.major, ev.minor, ev.clean); - } else if (msg.what == StorageEvent.EVENT_VOLUME_STATE_CHANGED) { - VolumeStateChangedStorageEvent ev = (VolumeStateChangedStorageEvent) e; - mStorageEventListener.onVolumeStateChanged(ev.label, ev.path, ev.oldState, ev.newState); - } else { - Log.e(TAG, "Unsupported event " + msg.what); - } - } - }; - } - - StorageEventListener getListener() { - return mStorageEventListener; - } - - void sendShareAvailabilityChanged(String method, boolean available) { - ShareAvailabilityChangedStorageEvent e = new ShareAvailabilityChangedStorageEvent(method, available); - mHandler.sendMessage(e.getMessage()); - } - - void sendMediaInserted(String label, String path, int major, int minor) { - MediaInsertedStorageEvent e = new MediaInsertedStorageEvent(label, path, major, minor); - mHandler.sendMessage(e.getMessage()); - } - - void sendMediaRemoved(String label, String path, int major, int minor, boolean clean) { - MediaRemovedStorageEvent e = new MediaRemovedStorageEvent(label, path, major, minor, clean); - mHandler.sendMessage(e.getMessage()); - } - - void sendVolumeStateChanged(String label, String path, String oldState, String newState) { - VolumeStateChangedStorageEvent e = new VolumeStateChangedStorageEvent(label, path, oldState, newState); - mHandler.sendMessage(e.getMessage()); - } - } - - /** - * {@hide} - */ - public StorageManager(Looper tgtLooper) throws RemoteException { - mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); - mTgtLooper = tgtLooper; - mBinderListener = new MountServiceBinderListener(); - mMountService.registerListener(mBinderListener); - } - - - /** - * Registers a {@link android.storage.StorageEventListener StorageEventListener}. - * - * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object. - * - */ - public void registerListener(StorageEventListener listener) { - if (listener == null) { - return; - } - - synchronized (sListeners) { - sListeners.add(new ListenerDelegate(listener)); - } - } - - /** - * Unregisters a {@link android.storage.StorageEventListener StorageEventListener}. - * - * @param listener A {@link android.storage.StorageEventListener StorageEventListener} object. - * - */ - public void unregisterListener(StorageEventListener listener) { - if (listener == null) { - return; - } - synchronized (sListeners) { - final int size = sListeners.size(); - for (int i=0 ; i<size ; i++) { - ListenerDelegate l = sListeners.get(i); - if (l.getListener() == listener) { - sListeners.remove(i); - break; - } - } - } - } -} diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 9581080..9a8ee02 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -61,6 +61,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; + int mLastTouch; private VelocityTracker mNext; @@ -105,8 +106,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Reset the velocity tracker back to its initial state. */ public void clear() { - for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) { - mPastTime[i][0] = 0; + final long[][] pastTime = mPastTime; + for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) { + for (int i = 0; i < NUM_PAST; i++) { + pastTime[p][i] = 0; + } } } @@ -133,42 +137,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } private void addPoint(int pos, float x, float y, long time) { - int drop = -1; - int i; - if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time); - final long[] pastTime = mPastTime[pos]; - for (i=0; i<NUM_PAST; i++) { - if (pastTime[i] == 0) { - break; - } else if (pastTime[i] < time-LONGEST_PAST_TIME) { - if (localLOGV) Log.v(TAG, "Dropping past too old at " - + i + " time=" + pastTime[i]); - drop = i; - } - } - if (localLOGV) Log.v(TAG, "Add index: " + i); - if (i == NUM_PAST && drop < 0) { - drop = 0; - } - if (drop == i) drop--; - final float[] pastX = mPastX[pos]; - final float[] pastY = mPastY[pos]; - if (drop >= 0) { - if (localLOGV) Log.v(TAG, "Dropping up to #" + drop); - final int start = drop+1; - final int count = NUM_PAST-drop-1; - System.arraycopy(pastX, start, pastX, 0, count); - System.arraycopy(pastY, start, pastY, 0, count); - System.arraycopy(pastTime, start, pastTime, 0, count); - i -= (drop+1); - } - pastX[i] = x; - pastY[i] = y; - pastTime[i] = time; - i++; - if (i < NUM_PAST) { - pastTime[i] = 0; - } + final int lastTouch = (mLastTouch + 1) % NUM_PAST; + mPastX[pos][lastTouch] = x; + mPastY[pos][lastTouch] = y; + mPastTime[pos][lastTouch] = time; + mLastTouch = lastTouch; } /** @@ -199,36 +172,41 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { final float[] pastX = mPastX[pos]; final float[] pastY = mPastY[pos]; final long[] pastTime = mPastTime[pos]; - + final int lastTouch = mLastTouch; + + // find oldest acceptable time + int oldestTouch = lastTouch; + if (pastTime[lastTouch] > 0) { // cleared ? + oldestTouch = (lastTouch + 1) % NUM_PAST; + final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME; + while (pastTime[oldestTouch] < acceptableTime) { + oldestTouch = (oldestTouch + 1) % NUM_PAST; + } + } + // Kind-of stupid. - final float oldestX = pastX[0]; - final float oldestY = pastY[0]; - final long oldestTime = pastTime[0]; + final float oldestX = pastX[oldestTouch]; + final float oldestY = pastY[oldestTouch]; + final long oldestTime = pastTime[oldestTouch]; float accumX = 0; float accumY = 0; - int N=0; - while (N < NUM_PAST) { - if (pastTime[N] == 0) { - break; - } - N++; - } + float N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1; // Skip the last received event, since it is probably pretty noisy. if (N > 3) N--; for (int i=1; i < N; i++) { - final int dur = (int)(pastTime[i] - oldestTime); + final int j = (oldestTouch + i) % NUM_PAST; + final int dur = (int)(pastTime[j] - oldestTime); if (dur == 0) continue; - float dist = pastX[i] - oldestX; + float dist = pastX[j] - oldestX; float vel = (dist/dur) * units; // pixels/frame. - if (accumX == 0) accumX = vel; - else accumX = (accumX + vel) * .5f; - - dist = pastY[i] - oldestY; + accumX = (accumX == 0) ? vel : (accumX + vel) * .5f; + + dist = pastY[j] - oldestY; vel = (dist/dur) * units; // pixels/frame. - if (accumY == 0) accumY = vel; - else accumY = (accumY + vel) * .5f; + accumY = (accumY == 0) ? vel : (accumY + vel) * .5f; } + mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f5c465e..fb19dcf 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8281,6 +8281,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Cancels any animations for this view. */ public void clearAnimation() { + if (mCurrentAnimation != null) { + mCurrentAnimation.detach(); + } mCurrentAnimation = null; } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index c8396c4..337fe58 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -256,6 +256,37 @@ public abstract class Animation implements Cloneable { } /** + * Cancel the animation. Cancelling an animation invokes the animation + * listener, if set, to notify the end of the animation. + * + * If you cancel an animation manually, you must call {@link #reset()} + * before starting the animation again. + * + * @see #reset() + * @see #start() + * @see #startNow() + */ + public void cancel() { + if (mStarted && !mEnded) { + if (mListener != null) mListener.onAnimationEnd(this); + mEnded = true; + } + // Make sure we move the animation to the end + mStartTime = Long.MIN_VALUE; + mMore = mOneMoreTime = false; + } + + /** + * @hide + */ + public void detach() { + if (mStarted && !mEnded) { + if (mListener != null) mListener.onAnimationEnd(this); + mEnded = true; + } + } + + /** * Whether or not the animation has been initialized. * * @return Has this animation been initialized. @@ -880,7 +911,7 @@ public abstract class Animation implements Cloneable { region.inset(-1.0f, -1.0f); if (mFillBefore) { final Transformation previousTransformation = mPreviousTransformation; - applyTransformation(0.0f, previousTransformation); + applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation); } } diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 98b2594..1546dcd 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -282,7 +282,9 @@ public class AnimationSet extends Animation { final Animation a = animations.get(i); temp.clear(); - a.applyTransformation(0.0f, temp); + final Interpolator interpolator = a.mInterpolator; + a.applyTransformation(interpolator != null ? interpolator.getInterpolation(0.0f) + : 0.0f, temp); previousTransformation.compose(temp); } } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 6d0be43..9e9cc7e 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -145,6 +145,12 @@ import java.util.ArrayList; break; } + if (KeyEvent.KEYCODE_TAB == keyCode) { + if (down) { + onEditorAction(EditorInfo.IME_ACTION_NEXT); + } + return true; + } Spannable text = (Spannable) getText(); int oldLength = text.length(); // Normally the delete key's dom events are sent via onTextChanged. @@ -810,6 +816,9 @@ import java.util.ArrayList; int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; switch (type) { + case 0: // NORMAL_TEXT_FIELD + imeOptions |= EditorInfo.IME_ACTION_GO; + break; case 1: // TEXT_AREA single = false; inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE @@ -819,6 +828,7 @@ import java.util.ArrayList; break; case 2: // PASSWORD inPassword = true; + imeOptions |= EditorInfo.IME_ACTION_GO; break; case 3: // SEARCH imeOptions |= EditorInfo.IME_ACTION_SEARCH; @@ -826,22 +836,25 @@ import java.util.ArrayList; case 4: // EMAIL // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents EMAIL_ADDRESS // from working, so exclude it for now. - inputType = EditorInfo.TYPE_CLASS_TEXT - | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; + imeOptions |= EditorInfo.IME_ACTION_GO; break; case 5: // NUMBER - inputType = EditorInfo.TYPE_CLASS_NUMBER; + inputType |= EditorInfo.TYPE_CLASS_NUMBER; + // Number and telephone do not have both a Tab key and an + // action, so set the action to NEXT + imeOptions |= EditorInfo.IME_ACTION_NEXT; break; case 6: // TELEPHONE - inputType = EditorInfo.TYPE_CLASS_PHONE; + inputType |= EditorInfo.TYPE_CLASS_PHONE; + imeOptions |= EditorInfo.IME_ACTION_NEXT; break; case 7: // URL - // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents URI - // from working, so exclude it for now. - inputType = EditorInfo.TYPE_CLASS_TEXT - | EditorInfo.TYPE_TEXT_VARIATION_URI; + // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so + // exclude it for now. + imeOptions |= EditorInfo.IME_ACTION_GO; break; default: + imeOptions |= EditorInfo.IME_ACTION_GO; break; } setHint(null); @@ -855,22 +868,6 @@ import java.util.ArrayList; mWebView.requestFormData(name, mNodePointer); } } - if (type != 3 /* SEARCH */) { - int action = mWebView.nativeTextFieldAction(); - switch (action) { - // Keep in sync with CachedRoot::ImeAction - case 0: // NEXT - imeOptions |= EditorInfo.IME_ACTION_NEXT; - break; - case 1: // GO - imeOptions |= EditorInfo.IME_ACTION_GO; - break; - case -1: // FAILURE - case 2: // DONE - imeOptions |= EditorInfo.IME_ACTION_DONE; - break; - } - } } mSingle = single; setMaxLength(maxLength); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6627973..8fa8f09 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -26,6 +26,7 @@ import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Interpolator; import android.graphics.Picture; import android.graphics.Point; import android.graphics.Rect; @@ -2972,8 +2973,16 @@ public class WebView extends AbsoluteLayout if (mTitleBar != null) { canvas.translate(0, (int) mTitleBar.getHeight()); } - if (mDragTrackerHandler == null || !mDragTrackerHandler.draw(canvas)) { + if (mDragTrackerHandler == null) { drawContent(canvas); + } else { + if (!mDragTrackerHandler.draw(canvas)) { + // sometimes the tracker doesn't draw, even though its active + drawContent(canvas); + } + if (mDragTrackerHandler.isFinished()) { + mDragTrackerHandler = null; + } } canvas.restoreToCount(saveCount); @@ -3370,6 +3379,10 @@ public class WebView extends AbsoluteLayout text = ""; } mWebTextView.setTextAndKeepSelection(text); + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null && imm.isActive(mWebTextView)) { + imm.restartInput(mWebTextView); + } } mWebTextView.requestFocus(); } @@ -4066,6 +4079,14 @@ public class WebView extends AbsoluteLayout private final float mMaxDY, mMaxDX; private float mCurrStretchY, mCurrStretchX; private int mSX, mSY; + private Interpolator mInterp; + private float[] mXY = new float[2]; + + // inner (non-state) classes can't have enums :( + private static final int DRAGGING_STATE = 0; + private static final int ANIMATING_STATE = 1; + private static final int FINISHED_STATE = 2; + private int mState; public DragTrackerHandler(float x, float y, DragTracker proxy) { mProxy = proxy; @@ -4090,6 +4111,7 @@ public class WebView extends AbsoluteLayout mMinDX = -viewLeft; mMaxDX = docRight - viewRight; + mState = DRAGGING_STATE; mProxy.onStartDrag(x, y); // ensure we buildBitmap at least once @@ -4112,6 +4134,12 @@ public class WebView extends AbsoluteLayout float sy = computeStretch(mStartY - y, mMinDY, mMaxDY); float sx = computeStretch(mStartX - x, mMinDX, mMaxDX); + if ((mSnapScrollMode & SNAP_X) != 0) { + sy = 0; + } else if ((mSnapScrollMode & SNAP_Y) != 0) { + sx = 0; + } + if (mCurrStretchX != sx || mCurrStretchY != sy) { mCurrStretchX = sx; mCurrStretchY = sy; @@ -4126,10 +4154,26 @@ public class WebView extends AbsoluteLayout } public void stopDrag() { + final int DURATION = 200; + int now = (int)SystemClock.uptimeMillis(); + mInterp = new Interpolator(2); + mXY[0] = mCurrStretchX; + mXY[1] = mCurrStretchY; + // float[] blend = new float[] { 0.5f, 0, 0.75f, 1 }; + float[] blend = new float[] { 0, 0.5f, 0.75f, 1 }; + mInterp.setKeyFrame(0, now, mXY, blend); + float[] zerozero = new float[] { 0, 0 }; + mInterp.setKeyFrame(1, now + DURATION, zerozero, null); + mState = ANIMATING_STATE; + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { - Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag"); + Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation"); } - mProxy.onStopDrag(); + } + + // Call this after each draw. If it ruturns null, the tracker is done + public boolean isFinished() { + return mState == FINISHED_STATE; } private int hiddenHeightOfTitleBar() { @@ -4150,13 +4194,23 @@ public class WebView extends AbsoluteLayout if (mCurrStretchX != 0 || mCurrStretchY != 0) { int sx = getScrollX(); int sy = getScrollY() - hiddenHeightOfTitleBar(); - if (mSX != sx || mSY != sy) { buildBitmap(sx, sy); mSX = sx; mSY = sy; } + if (mState == ANIMATING_STATE) { + Interpolator.Result result = mInterp.timeToValues(mXY); + if (result == Interpolator.Result.FREEZE_END) { + mState = FINISHED_STATE; + return false; + } else { + mProxy.onStretchChange(mXY[0], mXY[1]); + invalidate(); + // fall through to the draw + } + } int count = canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.translate(sx, sy); mProxy.onDraw(canvas); @@ -4600,7 +4654,6 @@ public class WebView extends AbsoluteLayout case MotionEvent.ACTION_UP: { if (mDragTrackerHandler != null) { mDragTrackerHandler.stopDrag(); - mDragTrackerHandler = null; } mLastTouchUpTime = eventTime; switch (mTouchMode) { @@ -4712,7 +4765,6 @@ public class WebView extends AbsoluteLayout private void cancelTouch() { if (mDragTrackerHandler != null) { mDragTrackerHandler.stopDrag(); - mDragTrackerHandler = null; } // we also use mVelocityTracker == null to tell us that we are // not "moving around", so we can take the slower/prettier @@ -6124,6 +6176,9 @@ public class WebView extends AbsoluteLayout // mContentHeight may not be updated yet y = Math.max(0, (Math.min(maxHeight, y + viewHeight) - viewHeight)); + // We need to take into account the visible title height + // when scrolling since y is an absolute view position. + y = Math.max(0, y - getVisibleTitleHeight()); scrollTo(x, y); } break; @@ -6736,8 +6791,6 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsUp(); private native void nativeSetFollowedLink(boolean followed); private native void nativeSetHeightCanMeasure(boolean measure); - // Returns a value corresponding to CachedFrame::ImeAction - /* package */ native int nativeTextFieldAction(); private native int nativeTextGeneration(); // Never call this version except by updateCachedTextfield(String) - // we always want to pass in our generation number. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index fd6af05..2f96aef 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1953,6 +1953,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } + mLastTouchMode = isInTouchMode ? TOUCH_MODE_ON : TOUCH_MODE_OFF; } @Override diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 299ed8a..9ef5e0b 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -103,6 +103,18 @@ public class DatePicker extends FrameLayout { mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); DateFormatSymbols dfs = new DateFormatSymbols(); String[] months = dfs.getShortMonths(); + + /* + * If the user is in a locale where the month names are numeric, + * use just the number instead of the "month" character for + * consistency with the other fields. + */ + if (months[0].startsWith("1")) { + for (int i = 0; i < months.length; i++) { + months[i] = String.valueOf(i + 1); + } + } + mMonthPicker.setRange(1, 12, months); mMonthPicker.setSpeed(200); mMonthPicker.setOnChangeListener(new OnChangedListener() { diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java index 2b07ae6..7e9bbd1 100644 --- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java +++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java @@ -24,7 +24,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; -import android.os.IMountService; +import android.os.storage.IMountService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 98fb236..24818a8 100755 --- a/core/java/com/android/internal/app/NetInitiatedActivity.java +++ b/core/java/com/android/internal/app/NetInitiatedActivity.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; -import android.os.IMountService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java index c110f95..2f48499 100644 --- a/core/java/com/android/internal/app/ShutdownThread.java +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -32,7 +32,7 @@ import android.os.RemoteException; import android.os.Power; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.IMountService; +import android.os.storage.IMountService; import com.android.internal.telephony.ITelephony; import android.util.Log; diff --git a/core/java/com/android/internal/app/StorageNotification.java b/core/java/com/android/internal/app/StorageNotification.java new file mode 100644 index 0000000..8876612 --- /dev/null +++ b/core/java/com/android/internal/app/StorageNotification.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2010 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 com.android.internal.app; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.storage.IMountService; +import android.os.Message; +import android.os.ServiceManager; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; +import android.os.storage.StorageResultCode; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +public class StorageNotification extends StorageEventListener { + private static final String TAG = "StorageNotification"; + + /** + * Binder context for this service + */ + private Context mContext; + + /** + * The notification that is shown when a USB mass storage host + * is connected. + * <p> + * This is lazily created, so use {@link #setUsbStorageNotification()}. + */ + private Notification mUsbStorageNotification; + + /** + * The notification that is shown when the following media events occur: + * - Media is being checked + * - Media is blank (or unknown filesystem) + * - Media is corrupt + * - Media is safe to unmount + * - Media is missing + * <p> + * This is lazily created, so use {@link #setMediaStorageNotification()}. + */ + private Notification mMediaStorageNotification; + private boolean mUmsAvailable; + private StorageManager mStorageManager; + + public StorageNotification(Context context) { + mContext = context; + + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + mUmsAvailable = mStorageManager.isUsbMassStorageConnected(); + Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable, + Environment.getExternalStorageState())); + } + + /* + * @override com.android.os.storage.StorageEventListener + */ + @Override + public void onUsbMassStorageConnectionChanged(boolean connected) { + mUmsAvailable = connected; + /* + * Even though we may have a UMS host connected, we the SD card + * may not be in a state for export. + */ + String st = Environment.getExternalStorageState(); + + Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st)); + + if (connected && (st.equals( + Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) { + /* + * No card or card being checked = don't display + */ + connected = false; + } + updateUsbMassStorageNotification(connected); + } + + /* + * @override com.android.os.storage.StorageEventListener + */ + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + Log.i(TAG, String.format( + "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState)); + if (newState.equals(Environment.MEDIA_SHARED)) { + /* + * Storage is now shared. Modify the UMS notification + * for stopping UMS. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_stop_notification_title, + com.android.internal.R.string.usb_storage_stop_notification_message, + com.android.internal.R.drawable.stat_sys_warning, false, true, pi); + } else if (newState.equals(Environment.MEDIA_CHECKING)) { + /* + * Storage is now checking. Update media notification and disable + * UMS notification. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_checking_notification_title, + com.android.internal.R.string.ext_media_checking_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null); + updateUsbMassStorageNotification(false); + } else if (newState.equals(Environment.MEDIA_MOUNTED)) { + /* + * Storage is now mounted. Dismiss any media notifications, + * and enable UMS notification if connected. + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) { + /* + * Storage is now unmounted. We may have been unmounted + * because the user is enabling/disabling UMS, in which case we don't + * want to display the 'safe to unmount' notification. + */ + if (!mStorageManager.isUsbMassStorageEnabled()) { + if (oldState.equals(Environment.MEDIA_SHARED)) { + /* + * The unmount was due to UMS being enabled. Dismiss any + * media notifications, and enable UMS notification if connected + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(mUmsAvailable); + } else { + /* + * Show safe to unmount media notification, and enable UMS + * notification if connected. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_safe_unmount_notification_title, + com.android.internal.R.string.ext_media_safe_unmount_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); + updateUsbMassStorageNotification(mUmsAvailable); + } + } else { + /* + * The unmount was due to UMS being enabled. Dismiss any + * media notifications, and disable the UMS notification + */ + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(false); + } + } else if (newState.equals(Environment.MEDIA_NOFS)) { + /* + * Storage has no filesystem. Show blank media notification, + * and enable UMS notification if connected. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification( + com.android.internal.R.string.ext_media_nofs_notification_title, + com.android.internal.R.string.ext_media_nofs_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) { + /* + * Storage is corrupt. Show corrupt media notification, + * and enable UMS notification if connected. + */ + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification( + com.android.internal.R.string.ext_media_unmountable_notification_title, + com.android.internal.R.string.ext_media_unmountable_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); + updateUsbMassStorageNotification(mUmsAvailable); + } else if (newState.equals(Environment.MEDIA_REMOVED)) { + /* + * Storage has been removed. Show nomedia media notification, + * and disable UMS notification regardless of connection state. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_nomedia_notification_title, + com.android.internal.R.string.ext_media_nomedia_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard_usb, + true, false, null); + updateUsbMassStorageNotification(false); + } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) { + /* + * Storage has been removed unsafely. Show bad removal media notification, + * and disable UMS notification regardless of connection state. + */ + setMediaStorageNotification( + com.android.internal.R.string.ext_media_badremoval_notification_title, + com.android.internal.R.string.ext_media_badremoval_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + true, true, null); + updateUsbMassStorageNotification(false); + } else { + Log.w(TAG, String.format("Ignoring unknown state {%s}", newState)); + } + } + + /** + * Update the state of the USB mass storage notification + */ + void updateUsbMassStorageNotification(boolean available) { + + if (available) { + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_notification_title, + com.android.internal.R.string.usb_storage_notification_message, + com.android.internal.R.drawable.stat_sys_data_usb, + false, true, pi); + } else { + setUsbStorageNotification(0, 0, 0, false, false, null); + } + } + + /** + * Sets the USB storage notification. + */ + private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, + PendingIntent pi) { + + if (!visible && mUsbStorageNotification == null) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (notificationManager == null) { + return; + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mUsbStorageNotification == null) { + mUsbStorageNotification = new Notification(); + mUsbStorageNotification.icon = icon; + mUsbStorageNotification.when = 0; + } + + if (sound) { + mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } else { + mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + + mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + + mUsbStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mUsbStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mUsbStorageNotification); + } else { + notificationManager.cancel(notificationId); + } + } + + private synchronized boolean getMediaStorageNotificationDismissable() { + if ((mMediaStorageNotification != null) && + ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == + Notification.FLAG_AUTO_CANCEL)) + return true; + + return false; + } + + /** + * Sets the media storage notification. + */ + private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, + boolean dismissable, PendingIntent pi) { + + if (!visible && mMediaStorageNotification == null) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (notificationManager == null) { + return; + } + + if (mMediaStorageNotification != null && visible) { + /* + * Dismiss the previous notification - we're about to + * re-use it. + */ + final int notificationId = mMediaStorageNotification.icon; + notificationManager.cancel(notificationId); + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mMediaStorageNotification == null) { + mMediaStorageNotification = new Notification(); + mMediaStorageNotification.when = 0; + } + + mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + + if (dismissable) { + mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; + } else { + mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + } + + mMediaStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mMediaStorageNotification.icon = icon; + mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mMediaStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mMediaStorageNotification); + } else { + notificationManager.cancel(notificationId); + } + } +} diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java index 2b93dbc..cb268b3 100644 --- a/core/java/com/android/internal/app/TetherActivity.java +++ b/core/java/com/android/internal/app/TetherActivity.java @@ -25,7 +25,6 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.Handler; -import android.os.IMountService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java index 34ae2b4..991f04b 100644 --- a/core/java/com/android/internal/app/UsbStorageActivity.java +++ b/core/java/com/android/internal/app/UsbStorageActivity.java @@ -25,8 +25,9 @@ import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Environment; -import android.os.IMountService; -import android.os.MountServiceResultCode; +import android.os.storage.StorageManager; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageResultCode; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -35,6 +36,7 @@ import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import android.view.View; +import android.util.Log; /** * This activity is shown to the user for him/her to enable USB mass storage @@ -42,11 +44,13 @@ import android.view.View; * dialog style. It will be launched from a notification. */ public class UsbStorageActivity extends Activity { + private static final String TAG = "UsbStorageActivity"; private Button mMountButton; private Button mUnmountButton; private TextView mBanner; private TextView mMessage; private ImageView mIcon; + private StorageManager mStorageManager = null; /** Used to detect when the USB cable is unplugged, so we can call finish() */ private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { @@ -57,11 +61,30 @@ public class UsbStorageActivity extends Activity { } } }; + + private StorageEventListener mStorageListener = new StorageEventListener() { + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + if (newState.equals(Environment.MEDIA_SHARED)) { + switchDisplay(true); + } else { + switchDisplay(false); + } + } + }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (mStorageManager == null) { + mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); + if (mStorageManager == null) { + Log.w(TAG, "Failed to get StorageManager"); + } + mStorageManager.registerListener(mStorageListener); + } + setTitle(getString(com.android.internal.R.string.usb_storage_activity_title)); setContentView(com.android.internal.R.layout.usb_storage_activity); @@ -74,9 +97,11 @@ public class UsbStorageActivity extends Activity { mMountButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { - mountAsUsbStorage(); - // TODO: replace with forthcoming MountService callbacks - switchDisplay(true); + int rc = mStorageManager.enableUsbMassStorage(); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, String.format("UMS enable failed (%d)", rc)); + showSharingError(); + } } }); @@ -84,9 +109,11 @@ public class UsbStorageActivity extends Activity { mUnmountButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { - stopUsbStorage(); - // TODO: replace with forthcoming MountService callbacks - switchDisplay(false); + int rc = mStorageManager.disableUsbMassStorage(); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, String.format("UMS disable failed (%d)", rc)); + showStoppingError(); + } } }); } @@ -112,19 +139,11 @@ public class UsbStorageActivity extends Activity { super.onResume(); registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - - boolean umsOn = false; try { - IMountService mountService = IMountService.Stub.asInterface(ServiceManager - .getService("mount")); - if (mountService != null) { - umsOn = mountService.getVolumeShared( - Environment.getExternalStorageDirectory().getPath(), "ums"); - } - } catch (android.os.RemoteException exc) { - // pass + switchDisplay(mStorageManager.isUsbMassStorageEnabled()); + } catch (Exception ex) { + Log.e(TAG, "Failed to read UMS enable state", ex); } - switchDisplay(umsOn); } @Override @@ -134,42 +153,6 @@ public class UsbStorageActivity extends Activity { unregisterReceiver(mBatteryReceiver); } - private void mountAsUsbStorage() { - IMountService mountService = IMountService.Stub.asInterface(ServiceManager - .getService("mount")); - if (mountService == null) { - showSharingError(); - return; - } - - try { - if (mountService.shareVolume( - Environment.getExternalStorageDirectory().getPath(), "ums") != - MountServiceResultCode.OperationSucceeded) { - showSharingError(); - } - } catch (RemoteException e) { - showSharingError(); - } - } - - private void stopUsbStorage() { - IMountService mountService = IMountService.Stub.asInterface(ServiceManager - .getService("mount")); - if (mountService == null) { - showStoppingError(); - return; - } - - try { - mountService.unshareVolume( - Environment.getExternalStorageDirectory().getPath(), "ums"); - } catch (RemoteException e) { - showStoppingError(); - return; - } - } - private void handleBatteryChanged(Intent intent) { int pluggedType = intent.getIntExtra("plugged", 0); if (pluggedType == 0) { diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java new file mode 100644 index 0000000..51f7f69 --- /dev/null +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2010 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 com.android.internal.widget; + +import java.util.Locale; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Paint.Align; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.inputmethodservice.Keyboard; +import android.inputmethodservice.KeyboardView; +import android.util.Log; +import com.android.internal.R; + +/** + * A basic, embed-able keyboard designed for password entry. Allows entry of all Latin-1 characters. + * + * It has two modes: alpha and numeric. In alpha mode, it allows all Latin-1 characters and enables + * an additional keyboard with symbols. In numeric mode, it shows a 12-key DTMF dialer-like + * keypad with alpha characters hints. + */ +public class PasswordEntryKeyboard extends Keyboard { + private static final String TAG = "PasswordEntryKeyboard"; + private static final int SHIFT_OFF = 0; + private static final int SHIFT_ON = 1; + private static final int SHIFT_LOCKED = 2; + public static final int KEYCODE_SPACE = ' '; + + private Drawable mShiftIcon; + private Drawable mShiftLockIcon; + private Drawable mShiftLockPreviewIcon; + private Drawable mOldShiftIcon; + private Drawable mOldShiftPreviewIcon; + private Drawable mSpaceIcon; + private Key mShiftKey; + private Key mEnterKey; + private Key mF1Key; + private Key mSpaceKey; + private Locale mLocale; + private Resources mRes; + private int mExtensionResId; + private int mShiftState = SHIFT_OFF; + + static int sSpacebarVerticalCorrection; + + public PasswordEntryKeyboard(Context context, int xmlLayoutResId) { + this(context, xmlLayoutResId, 0); + } + + public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) { + super(context, xmlLayoutResId, mode); + final Resources res = context.getResources(); + mRes = res; + mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift); + mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked); + mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked); + mShiftLockPreviewIcon.setBounds(0, 0, + mShiftLockPreviewIcon.getIntrinsicWidth(), + mShiftLockPreviewIcon.getIntrinsicHeight()); + mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space); + sSpacebarVerticalCorrection = res.getDimensionPixelOffset( + R.dimen.password_keyboard_spacebar_vertical_correction); + } + + public PasswordEntryKeyboard(Context context, int layoutTemplateResId, + CharSequence characters, int columns, int horizontalPadding) { + super(context, layoutTemplateResId, characters, columns, horizontalPadding); + } + + @Override + protected Key createKeyFromXml(Resources res, Row parent, int x, int y, + XmlResourceParser parser) { + LatinKey key = new LatinKey(res, parent, x, y, parser); + final int code = key.codes[0]; + if (code >=0 && code != '\n' && (code < 32 || code > 127)) { + Log.w(TAG, "Key code for " + key.label + " is not latin-1"); + key.label = " "; + key.setEnabled(false); + } + switch (key.codes[0]) { + case 10: + mEnterKey = key; + break; + case PasswordEntryKeyboardView.KEYCODE_F1: + mF1Key = key; + break; + case 32: + mSpaceKey = key; + break; + } + return key; + } + + /** + * Allows enter key resources to be overridden + * @param res resources to grab given items from + * @param previewId preview drawable shown on enter key + * @param iconId normal drawable shown on enter key + * @param labelId string shown on enter key + */ + void setEnterKeyResources(Resources res, int previewId, int iconId, int labelId) { + if (mEnterKey != null) { + // Reset some of the rarely used attributes. + mEnterKey.popupCharacters = null; + mEnterKey.popupResId = 0; + mEnterKey.text = null; + + mEnterKey.iconPreview = res.getDrawable(previewId); + mEnterKey.icon = res.getDrawable(iconId); + mEnterKey.label = res.getText(labelId); + + // Set the initial size of the preview icon + if (mEnterKey.iconPreview != null) { + mEnterKey.iconPreview.setBounds(0, 0, + mEnterKey.iconPreview.getIntrinsicWidth(), + mEnterKey.iconPreview.getIntrinsicHeight()); + } + } + } + + /** + * Allows shiftlock to be turned on. See {@link #setShiftLocked(boolean)} + * + */ + void enableShiftLock() { + int index = getShiftKeyIndex(); + if (index >= 0) { + mShiftKey = getKeys().get(index); + if (mShiftKey instanceof LatinKey) { + ((LatinKey)mShiftKey).enableShiftLock(); + } + mOldShiftIcon = mShiftKey.icon; + mOldShiftPreviewIcon = mShiftKey.iconPreview; + } + } + + /** + * Turn on shift lock. This turns on the LED for this key, if it has one. + * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} + * or {@link KeyboardView#invalidateAllKeys()} + * + * @param shiftLocked + */ + void setShiftLocked(boolean shiftLocked) { + if (mShiftKey != null) { + if (shiftLocked) { + mShiftKey.on = true; + mShiftKey.icon = mShiftLockIcon; + mShiftState = SHIFT_LOCKED; + } else { + mShiftKey.on = false; + mShiftKey.icon = mShiftLockIcon; + mShiftState = SHIFT_ON; + } + } + } + + /** + * Turn on shift mode. Sets shift mode and turns on icon for shift key. + * It should be followed by a call to {@link KeyboardView#invalidateKey(int)} + * or {@link KeyboardView#invalidateAllKeys()} + * + * @param shiftLocked + */ + @Override + public boolean setShifted(boolean shiftState) { + boolean shiftChanged = false; + if (mShiftKey != null) { + if (shiftState == false) { + shiftChanged = mShiftState != SHIFT_OFF; + mShiftState = SHIFT_OFF; + mShiftKey.on = false; + mShiftKey.icon = mOldShiftIcon; + } else if (mShiftState == SHIFT_OFF) { + shiftChanged = mShiftState == SHIFT_OFF; + mShiftState = SHIFT_ON; + mShiftKey.on = false; + mShiftKey.icon = mShiftIcon; + } + } else { + return super.setShifted(shiftState); + } + return shiftChanged; + } + + /** + * Whether or not keyboard is shifted. + * @return true if keyboard state is shifted. + */ + @Override + public boolean isShifted() { + if (mShiftKey != null) { + return mShiftState != SHIFT_OFF; + } else { + return super.isShifted(); + } + } + + /** + * Sets keyboard extension. Keyboard extension is shown when input is detected above keyboard + * while keyboard has focus. + * + * @param resId + */ + public void setExtension(int resId) { + mExtensionResId = resId; + } + + /** + * Get current extesion resource id. + * + * @return resource id, 0 if not set. + */ + public int getExtension() { + return mExtensionResId; + } + + private void updateSpaceBarForLocale() { + if (mLocale != null) { + // Create the graphic for spacebar + Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(buffer); + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + Paint paint = new Paint(); + paint.setAntiAlias(true); + // TODO: Make the text size a customizable attribute + paint.setTextSize(22); + paint.setTextAlign(Align.CENTER); + // Draw a drop shadow for the text + paint.setShadowLayer(1f, 0, 0, 0xFF000000); + paint.setColor(0x80C0C0C0); + canvas.drawText(mLocale.getDisplayLanguage(mLocale), + buffer.getWidth() / 2, - paint.ascent() + 2, paint); + int x = (buffer.getWidth() - mSpaceIcon.getIntrinsicWidth()) / 2; + int y = buffer.getHeight() - mSpaceIcon.getIntrinsicHeight(); + mSpaceIcon.setBounds(x, y, + x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight()); + mSpaceIcon.draw(canvas); + mSpaceKey.icon = new BitmapDrawable(mRes, buffer); + mSpaceKey.repeatable = false; + } else { + mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space); + mSpaceKey.repeatable = true; + } + } + + public void setLanguage(Locale locale) { + if (mLocale != null && mLocale.equals(locale)) return; + mLocale = locale; + updateSpaceBarForLocale(); + } + + static class LatinKey extends Keyboard.Key { + private boolean mShiftLockEnabled; + private boolean mEnabled = true; + + public LatinKey(Resources res, Keyboard.Row parent, int x, int y, + XmlResourceParser parser) { + super(res, parent, x, y, parser); + if (popupCharacters != null && popupCharacters.length() == 0) { + // If there is a keyboard with no keys specified in popupCharacters + popupResId = 0; + } + } + + void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + void enableShiftLock() { + mShiftLockEnabled = true; + } + + @Override + public void onReleased(boolean inside) { + if (!mShiftLockEnabled) { + super.onReleased(inside); + } else { + pressed = !pressed; + } + } + + /** + * Overriding this method so that we can reduce the target area for certain keys. + */ + @Override + public boolean isInside(int x, int y) { + if (!mEnabled) { + return false; + } + final int code = codes[0]; + if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) { + y -= height / 10; + if (code == KEYCODE_SHIFT) x += width / 6; + if (code == KEYCODE_DELETE) x -= width / 6; + } else if (code == KEYCODE_SPACE) { + y += PasswordEntryKeyboard.sSpacebarVerticalCorrection; + } + return super.isInside(x, y); + } + } +} diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java new file mode 100644 index 0000000..b809afc --- /dev/null +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2010 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 com.android.internal.widget; + +import android.content.Context; +import android.inputmethodservice.Keyboard; +import android.inputmethodservice.KeyboardView; +import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; +import android.os.Handler; +import android.os.SystemClock; +import android.text.Editable; +import android.text.Selection; +import android.util.Log; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewRoot; +import android.view.inputmethod.InputConnection; +import android.widget.EditText; +import com.android.internal.R; + +public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { + + public static final int KEYBOARD_MODE_ALPHA = 0; + public static final int KEYBOARD_MODE_NUMERIC = 1; + private static final int KEYBOARD_STATE_NORMAL = 0; + private static final int KEYBOARD_STATE_SHIFTED = 1; + private static final int KEYBOARD_STATE_CAPSLOCK = 2; + private static final String TAG = "PasswordEntryKeyboardHelper"; + private int mKeyboardMode = KEYBOARD_MODE_ALPHA; + private int mKeyboardState = KEYBOARD_STATE_NORMAL; + private PasswordEntryKeyboard mQwertyKeyboard; + private PasswordEntryKeyboard mQwertyKeyboardShifted; + private PasswordEntryKeyboard mSymbolsKeyboard; + private PasswordEntryKeyboard mSymbolsKeyboardShifted; + private PasswordEntryKeyboard mNumericKeyboard; + private Context mContext; + private View mTargetView; + private KeyboardView mKeyboardView; + + public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) { + mContext = context; + mTargetView = targetView; + mKeyboardView = keyboardView; + createKeyboards(); + mKeyboardView.setOnKeyboardActionListener(this); + } + + public boolean isAlpha() { + return mKeyboardMode == KEYBOARD_MODE_ALPHA; + } + + private void createKeyboards() { + mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric); + mQwertyKeyboard = new PasswordEntryKeyboard(mContext, + R.xml.password_kbd_qwerty, R.id.mode_normal); + mQwertyKeyboard.enableShiftLock(); + + mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, + R.xml.password_kbd_qwerty_shifted, + R.id.mode_normal); + mQwertyKeyboardShifted.enableShiftLock(); + mQwertyKeyboardShifted.setShifted(true); // always shifted. + + mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols); + mSymbolsKeyboard.enableShiftLock(); + + mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, + R.xml.password_kbd_symbols_shift); + mSymbolsKeyboardShifted.enableShiftLock(); + mSymbolsKeyboardShifted.setShifted(true); // always shifted + } + + public void setKeyboardMode(int mode) { + switch (mode) { + case KEYBOARD_MODE_ALPHA: + mKeyboardView.setKeyboard(mQwertyKeyboard); + mKeyboardState = KEYBOARD_STATE_NORMAL; + break; + case KEYBOARD_MODE_NUMERIC: + mKeyboardView.setKeyboard(mNumericKeyboard); + mKeyboardState = KEYBOARD_STATE_NORMAL; + break; + } + mKeyboardMode = mode; + } + + private void sendKeyEventsToTarget(int keyEventCode) { + Handler handler = mTargetView.getHandler(); + KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.ALPHA).getEvents( + new char[] { (char) keyEventCode }); + if (events != null) { + for (KeyEvent event : events) { + handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY, event)); + } + } + } + + public void sendDownUpKeyEvents(int keyEventCode) { + long eventTime = SystemClock.uptimeMillis(); + Handler handler = mTargetView.getHandler(); + handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, + new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); + handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, + new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); + } + + public void onKey(int primaryCode, int[] keyCodes) { + Log.v(TAG, "Key code = " + Integer.toHexString(primaryCode)); + if (primaryCode == Keyboard.KEYCODE_DELETE) { + handleBackspace(); + } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { + handleShift(); + } else if (primaryCode == Keyboard.KEYCODE_CANCEL) { + handleClose(); + return; + } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mKeyboardView != null) { + handleModeChange(); + } else { + handleCharacter(primaryCode, keyCodes); + // Switch back to old keyboard if we're not in capslock mode + if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { + // skip to the unlocked state + mKeyboardState = KEYBOARD_STATE_CAPSLOCK; + handleShift(); + } + } + } + + private void handleModeChange() { + final Keyboard current = mKeyboardView.getKeyboard(); + Keyboard next = null; + if (current == mQwertyKeyboard || current == mQwertyKeyboardShifted) { + next = mSymbolsKeyboard; + } else if (current == mSymbolsKeyboard || current == mSymbolsKeyboardShifted) { + next = mQwertyKeyboard; + } + if (next != null) { + mKeyboardView.setKeyboard(next); + mKeyboardState = KEYBOARD_STATE_NORMAL; + } + } + + private void handleBackspace() { + sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); + } + + private void handleShift() { + if (mKeyboardView == null) { + return; + } + Keyboard current = mKeyboardView.getKeyboard(); + PasswordEntryKeyboard next = null; + final boolean isAlphaMode = current == mQwertyKeyboard + || current == mQwertyKeyboardShifted; + if (mKeyboardState == KEYBOARD_STATE_NORMAL) { + mKeyboardState = isAlphaMode ? KEYBOARD_STATE_SHIFTED : KEYBOARD_STATE_CAPSLOCK; + next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; + } else if (mKeyboardState == KEYBOARD_STATE_SHIFTED) { + mKeyboardState = KEYBOARD_STATE_CAPSLOCK; + next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted; + } else if (mKeyboardState == KEYBOARD_STATE_CAPSLOCK) { + mKeyboardState = KEYBOARD_STATE_NORMAL; + next = isAlphaMode ? mQwertyKeyboard : mSymbolsKeyboard; + } + if (next != null) { + if (next != current) { + mKeyboardView.setKeyboard(next); + } + next.setShiftLocked(mKeyboardState == KEYBOARD_STATE_CAPSLOCK); + mKeyboardView.setShifted(mKeyboardState != KEYBOARD_STATE_NORMAL); + } + } + + private void handleCharacter(int primaryCode, int[] keyCodes) { + // Maybe turn off shift if not in capslock mode. + if (mKeyboardView.isShifted() && primaryCode != ' ' && primaryCode != '\n') { + primaryCode = Character.toUpperCase(primaryCode); + } + sendKeyEventsToTarget(primaryCode); + } + + private void handleClose() { + + } + + public void onPress(int primaryCode) { + + } + + public void onRelease(int primaryCode) { + + } + + public void onText(CharSequence text) { + + } + + public void swipeDown() { + + } + + public void swipeLeft() { + + } + + public void swipeRight() { + + } + + public void swipeUp() { + + } +}; diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java new file mode 100644 index 0000000..9b93fc2 --- /dev/null +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 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 com.android.internal.widget; + +import android.content.Context; +import android.inputmethodservice.Keyboard; +import android.inputmethodservice.KeyboardView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.widget.PopupWindow; +import com.android.internal.R; + +public class PasswordEntryKeyboardView extends KeyboardView { + + public static final int KEYCODE_OPTIONS = -100; + static final int KEYCODE_SHIFT_LONGPRESS = -101; + static final int KEYCODE_VOICE = -102; + static final int KEYCODE_F1 = -103; + static final int KEYCODE_NEXT_LANGUAGE = -104; + + private boolean mExtensionVisible; + private PasswordEntryKeyboardView mExtension; + private PopupWindow mExtensionPopup; + private boolean mFirstEvent; + + public PasswordEntryKeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PasswordEntryKeyboardView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean onTouchEvent(MotionEvent me) { + if (((PasswordEntryKeyboard) getKeyboard()).getExtension() == 0) { + return super.onTouchEvent(me); + } + if (me.getY() < 0) { + if (mExtensionVisible) { + int action = me.getAction(); + if (mFirstEvent) action = MotionEvent.ACTION_DOWN; + mFirstEvent = false; + MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(), + action, + me.getX(), me.getY() + mExtension.getHeight(), me.getMetaState()); + boolean result = mExtension.onTouchEvent(translated); + translated.recycle(); + if (me.getAction() == MotionEvent.ACTION_UP + || me.getAction() == MotionEvent.ACTION_CANCEL) { + closeExtension(); + } + return result; + } else { + if (openExtension()) { + MotionEvent cancel = MotionEvent.obtain(me.getDownTime(), me.getEventTime(), + MotionEvent.ACTION_CANCEL, me.getX() - 100, me.getY() - 100, 0); + super.onTouchEvent(cancel); + cancel.recycle(); + if (mExtension.getHeight() > 0) { + MotionEvent translated = MotionEvent.obtain(me.getEventTime(), + me.getEventTime(), + MotionEvent.ACTION_DOWN, + me.getX(), me.getY() + mExtension.getHeight(), + me.getMetaState()); + mExtension.onTouchEvent(translated); + translated.recycle(); + } else { + mFirstEvent = true; + } + } + return true; + } + } else if (mExtensionVisible) { + closeExtension(); + // Send a down event into the main keyboard first + MotionEvent down = MotionEvent.obtain(me.getEventTime(), me.getEventTime(), + MotionEvent.ACTION_DOWN, me.getX(), me.getY(), me.getMetaState()); + super.onTouchEvent(down); + down.recycle(); + // Send the actual event + return super.onTouchEvent(me); + } else { + return super.onTouchEvent(me); + } + } + + private boolean openExtension() { + if (((PasswordEntryKeyboard) getKeyboard()).getExtension() == 0) return false; + makePopupWindow(); + mExtensionVisible = true; + return true; + } + + private void makePopupWindow() { + if (mExtensionPopup == null) { + int[] windowLocation = new int[2]; + mExtensionPopup = new PopupWindow(getContext()); + mExtensionPopup.setBackgroundDrawable(null); + LayoutInflater li = (LayoutInflater) getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mExtension = (PasswordEntryKeyboardView) li.inflate( + R.layout.password_keyboard_input, null); + mExtension.setOnKeyboardActionListener(getOnKeyboardActionListener()); + mExtension.setPopupParent(this); + mExtension.setPopupOffset(0, -windowLocation[1]); + Keyboard keyboard; + mExtension.setKeyboard(keyboard = new PasswordEntryKeyboard(getContext(), + ((PasswordEntryKeyboard) getKeyboard()).getExtension())); + mExtensionPopup.setContentView(mExtension); + mExtensionPopup.setWidth(getWidth()); + mExtensionPopup.setHeight(keyboard.getHeight()); + getLocationInWindow(windowLocation); + // TODO: Fix the "- 30". + mExtension.setPopupOffset(0, -windowLocation[1] - 30); + mExtensionPopup.showAtLocation(this, 0, 0, -keyboard.getHeight() + + windowLocation[1]); + } else { + mExtension.setVisibility(VISIBLE); + } + } + + @Override + public void closing() { + super.closing(); + if (mExtensionPopup != null && mExtensionPopup.isShowing()) { + mExtensionPopup.dismiss(); + mExtensionPopup = null; + } + } + + private void closeExtension() { + mExtension.setVisibility(INVISIBLE); + mExtension.closing(); + mExtensionVisible = false; + } +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 7fd58e8..1ffd265 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -70,7 +70,6 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ - android_util_Base64.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ android/graphics/Camera.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1d22de3..7c8df03 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -153,7 +153,6 @@ extern int register_android_server_BluetoothEventLoop(JNIEnv *env); extern int register_android_server_BluetoothA2dpService(JNIEnv* env); 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_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); @@ -1266,7 +1265,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_server_BluetoothA2dpService), REG_JNI(register_android_message_digest_sha1), REG_JNI(register_android_ddm_DdmHandleNativeHeap), - REG_JNI(register_android_util_Base64), REG_JNI(register_android_location_GpsLocationProvider), REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp index 020aff4..c197010 100644 --- a/core/jni/android_database_SQLiteDatabase.cpp +++ b/core/jni/android_database_SQLiteDatabase.cpp @@ -143,12 +143,68 @@ done: if (handle != NULL) sqlite3_close(handle); } +void sqlTrace(void *databaseName, const char *sql) { + LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql); +} + +/* public native void enableSqlTracing(); */ +static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + char const *path = env->GetStringUTFChars(databaseName, NULL); + if (path == NULL) { + LOGE("Failure in enableSqlTracing(). VM ran out of memory?\n"); + return; // VM would have thrown OutOfMemoryError + } + int len = strlen(path); + char *traceFuncArg = (char *)malloc(len + 1); + strncpy(traceFuncArg, path, len); + traceFuncArg[len-1] = NULL; + env->ReleaseStringUTFChars(databaseName, path); + sqlite3_trace(handle, &sqlTrace, (void *)traceFuncArg); + LOGI("will be printing all sql statements executed on database = %s\n", traceFuncArg); +} + +void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) { + double d = tm/1000000.0; + LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql); +} + +/* public native void enableSqlProfiling(); */ +static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + char const *path = env->GetStringUTFChars(databaseName, NULL); + if (path == NULL) { + LOGE("Failure in enableSqlProfiling(). VM ran out of memory?\n"); + return; // VM would have thrown OutOfMemoryError + } + int len = strlen(path); + char *traceFuncArg = (char *)malloc(len + 1); + strncpy(traceFuncArg, path, len); + traceFuncArg[len-1] = NULL; + env->ReleaseStringUTFChars(databaseName, path); + sqlite3_profile(handle, &sqlProfile, (void *)traceFuncArg); + LOGI("will be printing execution time of all sql statements executed on database = %s\n", + traceFuncArg); +} + /* public native void close(); */ static void dbclose(JNIEnv* env, jobject object) { sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); if (handle != NULL) { + // release the memory associated with the traceFuncArg in enableSqlTracing function + void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL); + if (traceFuncArg != NULL) { + free(traceFuncArg); + } + // release the memory associated with the traceFuncArg in enableSqlProfiling function + traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL); + if (traceFuncArg != NULL) { + free(traceFuncArg); + } LOGV("Closing database: handle=%p\n", handle); int result = sqlite3_close(handle); if (result == SQLITE_OK) { @@ -357,6 +413,8 @@ static JNINativeMethod sMethods[] = /* name, signature, funcPtr */ {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen}, {"dbclose", "()V", (void *)dbclose}, + {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing}, + {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling}, {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL}, {"lastInsertRow", "()J", (void *)lastInsertRow}, {"lastChangeCount", "()I", (void *)lastChangeCount}, diff --git a/core/jni/android_util_Base64.cpp b/core/jni/android_util_Base64.cpp deleted file mode 100644 index bc69747..0000000 --- a/core/jni/android_util_Base64.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* //device/libs/android_runtime/android_util_Base64.cpp -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -/********************************************************* -* -* This code was copied from -* system/extra/ssh/dropbear-0.49/libtomcrypt/src/misc/base64/base64_decode.c -* -*********************************************************/ - -#define LOG_TAG "Base64" - -#include <utils/Log.h> - -#include <android_runtime/AndroidRuntime.h> - -#include "JNIHelp.h" - -#include <sys/errno.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <fcntl.h> -#include <signal.h> - -namespace android { - -static const unsigned char map[256] = { -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, -255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, -255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -255, 255, 255, 255 }; - -/** - base64 decode a block of memory - @param in The base64 data to decode - @param inlen The length of the base64 data - @param out [out] The destination of the binary decoded data - @param outlen [in/out] The max size and resulting size of the decoded data - @return 0 if successful -*/ -int base64_decode(const unsigned char *in, unsigned long inlen, - unsigned char *out, unsigned long *outlen) -{ - unsigned long t, x, y, z; - unsigned char c; - int g; - - g = 3; - for (x = y = z = t = 0; x < inlen; x++) { - c = map[in[x]&0xFF]; - if (c == 255) continue; - /* the final = symbols are read and used to trim the remaining bytes */ - if (c == 254) { - c = 0; - /* prevent g < 0 which would potentially allow an overflow later */ - if (--g < 0) { - return -3; - } - } else if (g != 3) { - /* we only allow = to be at the end */ - return -4; - } - - t = (t<<6)|c; - - if (++y == 4) { - if (z + g > *outlen) { - return -2; - } - out[z++] = (unsigned char)((t>>16)&255); - if (g > 1) out[z++] = (unsigned char)((t>>8)&255); - if (g > 2) out[z++] = (unsigned char)(t&255); - y = t = 0; - } - } - if (y != 0) { - return -5; - } - *outlen = z; - return 0; -} - -static jbyteArray decodeBase64(JNIEnv *env, jobject jobj, jstring jdata) -{ - const char * rawData = env->GetStringUTFChars(jdata, NULL); - int stringLength = env->GetStringUTFLength(jdata); - - int resultLength = stringLength / 4 * 3; - if (rawData[stringLength-1] == '=') { - resultLength -= 1; - if (rawData[stringLength-2] == '=') { - resultLength -= 1; - } - } - - jbyteArray byteArray = env->NewByteArray(resultLength); - jbyte* byteArrayData = env->GetByteArrayElements(byteArray, NULL); - - unsigned long outlen = resultLength; - int result = base64_decode((const unsigned char*)rawData, stringLength, (unsigned char *)byteArrayData, &outlen); - if (result != 0) - memset((unsigned char *)byteArrayData, -result, resultLength); - - env->ReleaseStringUTFChars(jdata, rawData); - env->ReleaseByteArrayElements(byteArray, byteArrayData, 0); - - return byteArray; -} - -static const JNINativeMethod methods[] = { - {"decodeBase64Native", "(Ljava/lang/String;)[B", (void*)decodeBase64 } -}; - -static const char* const kBase64PathName = "android/os/Base64Utils"; - -int register_android_util_Base64(JNIEnv* env) -{ - jclass clazz; - - clazz = env->FindClass(kBase64PathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Base64Utils"); - - return AndroidRuntime::registerNativeMethods( - env, kBase64PathName, - methods, NELEM(methods)); -} - -} diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png Binary files differnew file mode 100755 index 0000000..569369e --- /dev/null +++ b/core/res/res/drawable-en-hdpi/sym_keyboard_delete.png diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png Binary files differnew file mode 100755 index 0000000..ca76375 --- /dev/null +++ b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png diff --git a/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png b/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png Binary files differnew file mode 100644 index 0000000..f1f7c58 --- /dev/null +++ b/core/res/res/drawable-en-mdpi/sym_keyboard_delete.png diff --git a/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png Binary files differnew file mode 100644 index 0000000..3c90839 --- /dev/null +++ b/core/res/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differnew file mode 100644 index 0000000..b6c234c --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differnew file mode 100644 index 0000000..9f3c087 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_off.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differnew file mode 100644 index 0000000..4041342 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal_on.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differnew file mode 100644 index 0000000..73a8cd1 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differnew file mode 100644 index 0000000..8473e8e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_off.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differnew file mode 100644 index 0000000..f4f59c0 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed_on.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png Binary files differnew file mode 100644 index 0000000..1508653 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_off.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png Binary files differnew file mode 100644 index 0000000..66c231a --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal_on.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png Binary files differnew file mode 100644 index 0000000..cdad182 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_off.9.png diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png Binary files differnew file mode 100644 index 0000000..e95f4cf --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed_on.9.png diff --git a/core/res/res/drawable-hdpi/password_field_default.9.png b/core/res/res/drawable-hdpi/password_field_default.9.png Binary files differnew file mode 100644 index 0000000..2c424f0 --- /dev/null +++ b/core/res/res/drawable-hdpi/password_field_default.9.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_delete.png b/core/res/res/drawable-hdpi/sym_keyboard_delete.png Binary files differnew file mode 100755 index 0000000..59d78be --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_delete.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png b/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png Binary files differnew file mode 100644 index 0000000..34b6d1f --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_delete_dim.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png Binary files differnew file mode 100755 index 0000000..ca76375 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_delete.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png Binary files differnew file mode 100644 index 0000000..2d144ec --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_ok.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png Binary files differnew file mode 100755 index 0000000..ae57299 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_return.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png Binary files differnew file mode 100755 index 0000000..4db31c8 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png Binary files differnew file mode 100755 index 0000000..3fd5659 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png b/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png Binary files differnew file mode 100755 index 0000000..98266ee --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_feedback_space.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png b/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png Binary files differnew file mode 100644 index 0000000..2aad23c --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num0_no_plus.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num1.png b/core/res/res/drawable-hdpi/sym_keyboard_num1.png Binary files differnew file mode 100755 index 0000000..0fc03ef --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num1.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num2.png b/core/res/res/drawable-hdpi/sym_keyboard_num2.png Binary files differnew file mode 100755 index 0000000..283560b --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num2.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num3.png b/core/res/res/drawable-hdpi/sym_keyboard_num3.png Binary files differnew file mode 100755 index 0000000..9a3b329 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num3.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num4.png b/core/res/res/drawable-hdpi/sym_keyboard_num4.png Binary files differnew file mode 100755 index 0000000..f13ff1a --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num4.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num5.png b/core/res/res/drawable-hdpi/sym_keyboard_num5.png Binary files differnew file mode 100755 index 0000000..c251329 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num5.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num6.png b/core/res/res/drawable-hdpi/sym_keyboard_num6.png Binary files differnew file mode 100755 index 0000000..4acba4c --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num6.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num7.png b/core/res/res/drawable-hdpi/sym_keyboard_num7.png Binary files differnew file mode 100755 index 0000000..14931c1 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num7.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num8.png b/core/res/res/drawable-hdpi/sym_keyboard_num8.png Binary files differnew file mode 100755 index 0000000..d4973fd --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num8.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_num9.png b/core/res/res/drawable-hdpi/sym_keyboard_num9.png Binary files differnew file mode 100755 index 0000000..49cec66 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_num9.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_ok.png b/core/res/res/drawable-hdpi/sym_keyboard_ok.png Binary files differnew file mode 100644 index 0000000..9105da2 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_ok.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png b/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png Binary files differnew file mode 100644 index 0000000..bd419de --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_ok_dim.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return.png b/core/res/res/drawable-hdpi/sym_keyboard_return.png Binary files differnew file mode 100755 index 0000000..58505c5 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_return.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_shift.png b/core/res/res/drawable-hdpi/sym_keyboard_shift.png Binary files differnew file mode 100755 index 0000000..8149081 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_shift.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png Binary files differnew file mode 100755 index 0000000..31ca277 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_shift_locked.png diff --git a/core/res/res/drawable-hdpi/sym_keyboard_space.png b/core/res/res/drawable-hdpi/sym_keyboard_space.png Binary files differnew file mode 100755 index 0000000..3e98b30 --- /dev/null +++ b/core/res/res/drawable-hdpi/sym_keyboard_space.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differnew file mode 100644 index 0000000..20f3d50 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differnew file mode 100644 index 0000000..d09ce53 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differnew file mode 100644 index 0000000..a9e008c --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differnew file mode 100644 index 0000000..1ed3065 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differnew file mode 100644 index 0000000..5710ebf --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differnew file mode 100644 index 0000000..dd7d89e --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png Binary files differnew file mode 100644 index 0000000..77426ef --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png Binary files differnew file mode 100644 index 0000000..e4c9bd5 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal_on.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png Binary files differnew file mode 100644 index 0000000..bb98b01 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png Binary files differnew file mode 100644 index 0000000..3c7dcc8 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed_on.9.png diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png Binary files differindex 855484a..6a97d5b 100644 --- a/core/res/res/drawable-mdpi/ic_launcher_android.png +++ b/core/res/res/drawable-mdpi/ic_launcher_android.png diff --git a/core/res/res/drawable-mdpi/password_field_default.9.png b/core/res/res/drawable-mdpi/password_field_default.9.png Binary files differnew file mode 100644 index 0000000..3193275 --- /dev/null +++ b/core/res/res/drawable-mdpi/password_field_default.9.png diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png Binary files differindex 8be3b54..9777d11 100644 --- a/core/res/res/drawable-mdpi/sym_def_app_icon.png +++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_delete.png b/core/res/res/drawable-mdpi/sym_keyboard_delete.png Binary files differnew file mode 100644 index 0000000..43a033e --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_delete.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png Binary files differnew file mode 100644 index 0000000..25460d8 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_delete_dim.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png Binary files differnew file mode 100644 index 0000000..1edb10b --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_delete.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png Binary files differnew file mode 100644 index 0000000..3148836 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_ok.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png Binary files differnew file mode 100644 index 0000000..03d9c9b --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_return.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png Binary files differnew file mode 100644 index 0000000..97f4661 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png Binary files differnew file mode 100755 index 0000000..7194b30 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png b/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png Binary files differnew file mode 100644 index 0000000..739db68 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_feedback_space.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png b/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png Binary files differnew file mode 100644 index 0000000..91332b1 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num0_no_plus.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num1.png b/core/res/res/drawable-mdpi/sym_keyboard_num1.png Binary files differnew file mode 100644 index 0000000..aaac11b --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num1.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num2.png b/core/res/res/drawable-mdpi/sym_keyboard_num2.png Binary files differnew file mode 100644 index 0000000..4372eb8 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num2.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num3.png b/core/res/res/drawable-mdpi/sym_keyboard_num3.png Binary files differnew file mode 100644 index 0000000..6f54c85 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num3.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num4.png b/core/res/res/drawable-mdpi/sym_keyboard_num4.png Binary files differnew file mode 100644 index 0000000..3e50bb9 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num4.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num5.png b/core/res/res/drawable-mdpi/sym_keyboard_num5.png Binary files differnew file mode 100644 index 0000000..c39ef44 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num5.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num6.png b/core/res/res/drawable-mdpi/sym_keyboard_num6.png Binary files differnew file mode 100644 index 0000000..ea88ceb --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num6.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num7.png b/core/res/res/drawable-mdpi/sym_keyboard_num7.png Binary files differnew file mode 100644 index 0000000..ce800ba --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num7.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num8.png b/core/res/res/drawable-mdpi/sym_keyboard_num8.png Binary files differnew file mode 100644 index 0000000..1a8ff94 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num8.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_num9.png b/core/res/res/drawable-mdpi/sym_keyboard_num9.png Binary files differnew file mode 100644 index 0000000..8b344c0 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_num9.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_ok.png b/core/res/res/drawable-mdpi/sym_keyboard_ok.png Binary files differnew file mode 100644 index 0000000..b8b5292 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_ok.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png b/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png Binary files differnew file mode 100644 index 0000000..33ecff5 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_ok_dim.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return.png b/core/res/res/drawable-mdpi/sym_keyboard_return.png Binary files differnew file mode 100644 index 0000000..17f2574 --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_return.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png Binary files differnew file mode 100644 index 0000000..0566e5a --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png Binary files differnew file mode 100755 index 0000000..ccaf05d --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_space.png b/core/res/res/drawable-mdpi/sym_keyboard_space.png Binary files differnew file mode 100644 index 0000000..4e6273b --- /dev/null +++ b/core/res/res/drawable-mdpi/sym_keyboard_space.png diff --git a/core/res/res/drawable/btn_keyboard_key_fulltrans.xml b/core/res/res/drawable/btn_keyboard_key_fulltrans.xml new file mode 100644 index 0000000..cfad6e3 --- /dev/null +++ b/core/res/res/drawable/btn_keyboard_key_fulltrans.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- Toggle keys. Use checkable/checked state. --> + + <item android:state_checkable="true" android:state_checked="true" + android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed_on" /> + <item android:state_checkable="true" android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed_off" /> + <item android:state_checkable="true" android:state_checked="true" + android:drawable="@drawable/btn_keyboard_key_fulltrans_normal_on" /> + <item android:state_checkable="true" + android:drawable="@drawable/btn_keyboard_key_fulltrans_normal_off" /> + + <!-- Normal keys --> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" /> + <item android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" /> + +</selector> diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml index 5bc034b..b089df6 100644 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout/keyguard_screen_password_landscape.xml @@ -16,105 +16,68 @@ ** limitations under the License. */ --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="@android:color/background_dark"> + android:background="#70000000" + android:gravity="center_horizontal"> - <!-- displays dots as user enters pin --> - <LinearLayout android:id="@+id/pinDisplayGroup" - android:orientation="horizontal" - android:layout_centerInParent="true" - android:layout_width="fill_parent" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:addStatesFromChildren="true" - android:gravity="center_vertical" - android:baselineAligned="false" - android:paddingRight="0dip" - android:layout_marginRight="30dip" - android:layout_marginLeft="30dip" - android:layout_marginTop="6dip" - android:background="@android:drawable/edit_text"> - - <EditText android:id="@+id/pinDisplay" + android:orientation="horizontal"> + <!-- "Enter PIN(Password) to unlock" --> + <TextView android:id="@+id/enter_password_label" android:layout_width="0dip" + android:layout_height="wrap_content" android:layout_weight="1" - android:layout_height="fill_parent" - android:maxLines="1" - android:background="@null" - android:textSize="32sp" - android:inputType="textPassword" + android:orientation="horizontal" + android:layout_marginRight="6dip" + android:layout_marginLeft="6dip" + android:layout_marginTop="10dip" + android:layout_marginBottom="10dip" + android:gravity="center" + android:ellipsize="marquee" + android:text="@android:string/keyguard_password_enter_password_code" + android:textAppearance="?android:attr/textAppearanceLarge" /> - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:layout_marginTop="2dip" - android:layout_marginRight="2dip" - android:layout_marginBottom="2dip" - android:gravity="center" + <!-- Password entry field --> + <EditText android:id="@+id/passwordEntry" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:singleLine="true" + android:textStyle="bold" + android:inputType="textPassword" + android:gravity="center" + android:layout_gravity="center" + android:textSize="32sp" + android:textAppearance="?android:attr/textAppearanceLarge" + android:background="@drawable/password_field_default" + android:textColor="#ffffffff" /> - </LinearLayout> - <!-- header text ('Enter Pin Code') --> - <TextView android:id="@+id/headerText" - android:layout_above="@id/pinDisplayGroup" - android:layout_centerHorizontal="true" - android:layout_marginBottom="30dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="24sp" - /> - - <LinearLayout - android:orientation="horizontal" + <!-- Alphanumeric keyboard --> + <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" android:layout_alignParentBottom="true" - android:layout_width="fill_parent" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dip" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip"> - - <Button android:id="@+id/ok" - android:text="@android:string/ok" - android:layout_alignParentBottom="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginRight="8dip" - android:textSize="18sp" - /> - - <Button android:id="@+id/emergencyCall" - android:text="@android:string/lockscreen_emergency_call" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1.0" - android:layout_marginBottom="8dip" - android:layout_marginLeft="8dip" - android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - /> - </LinearLayout> + android:background="#00000000" + android:keyBackground="@drawable/btn_keyboard_key_fulltrans" + /> - <!-- Not currently visible on this screen --> - <TextView - android:id="@+id/carrier" + <!-- emergency call button --> + <Button + android:id="@+id/emergencyCall" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_marginTop="6dip" - android:layout_alignParentRight="true" - android:layout_marginRight="8dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:visibility="gone" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" /> -</RelativeLayout> +</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml index a7814af..9ee8781 100644 --- a/core/res/res/layout/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout/keyguard_screen_password_portrait.xml @@ -17,142 +17,77 @@ */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" + android:layout_width="match_parent" + android:layout_height="match_parent" android:orientation="vertical" android:background="#70000000" android:gravity="center_horizontal"> - <LinearLayout android:id="@+id/topDisplayGroup" - android:layout_width="fill_parent" + <!-- "Enter PIN(Password) to unlock" --> + <TextView android:id="@+id/enter_password_label" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> - - <RelativeLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="6dip" - android:layout_marginLeft="6dip" - android:layout_alignParentTop="true" - android:layout_alignParentLeft="true"> - - <TextView android:id="@+id/timeDisplay" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="bottom" - android:singleLine="true" - android:ellipsize="none" - android:textSize="56sp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:shadowColor="#C0000000" - android:shadowDx="0" - android:shadowDy="0" - android:shadowRadius="3.0" - android:layout_marginBottom="6dip" - /> - - <TextView android:id="@+id/am_pm" - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:gravity="bottom" - android:singleLine="true" - android:ellipsize="none" - android:textSize="18sp" - android:layout_marginLeft="4dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:shadowColor="#C0000000" - android:shadowDx="0" - android:shadowDy="0" - android:shadowRadius="3.0" - /> - - </com.android.internal.widget.DigitalClock> - - <TextView android:id="@+id/carrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_marginTop="6dip" - android:layout_alignParentRight="true" - android:layout_marginRight="8dip" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - - </RelativeLayout> - - <!-- password entry --> - <LinearLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginRight="6dip" - android:layout_marginLeft="6dip" - android:gravity="center_vertical" - android:hint="@android:string/keyguard_password_enter_password_code" - android:background="@android:drawable/edit_text"> - - <!-- displays dots as user enters pin --> - <TextView android:id="@+id/pinDisplay" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:textAppearance="?android:attr/textAppearanceLargeInverse" - android:textStyle="bold" - android:inputType="textPassword" - /> - - <ImageButton android:id="@+id/backspace" - android:src="@android:drawable/ic_input_delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginRight="-3dip" - android:layout_marginBottom="-3dip" - /> - </LinearLayout> - - </LinearLayout> - - <include - android:id="@+id/keyPad" - layout="@android:layout/twelve_key_entry" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/topDisplayGroup" + android:orientation="horizontal" + android:layout_marginRight="6dip" + android:layout_marginLeft="6dip" android:layout_marginTop="10dip" + android:layout_marginBottom="10dip" + android:gravity="center" + android:text="@android:string/keyguard_password_enter_password_code" + android:textAppearance="?android:attr/textAppearanceLarge" /> - <!-- spacer below keypad --> + <!-- spacer above text entry field --> <View android:id="@+id/spacerBottom" android:layout_width="fill_parent" android:layout_height="1dip" android:layout_marginTop="6dip" - android:layout_above="@id/emergencyCall" android:background="@android:drawable/divider_horizontal_dark" /> - <!-- The emergency button should take the rest of the space and be centered vertically --> - <LinearLayout - android:layout_width="fill_parent" - android:layout_height="0dip" - android:layout_weight="1" + <!-- Password entry field --> + <EditText android:id="@+id/passwordEntry" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:textStyle="bold" + android:inputType="textPassword" android:gravity="center" - android:orientation="vertical"> + android:layout_gravity="center" + android:textSize="32sp" + android:layout_marginTop="15dip" + android:layout_marginLeft="30dip" + android:layout_marginRight="30dip" + android:textAppearance="?android:attr/textAppearanceLarge" + android:background="@drawable/password_field_default" + android:textColor="#ffffffff" + /> - <!-- emergency call button --> - <Button - android:id="@+id/emergencyCall" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@android:drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@android:string/lockscreen_emergency_call" - /> - </LinearLayout> + <View + android:layout_width="match_parent" + android:layout_height="0dip" + android:layout_weight="1" /> + + <!-- Alphanumeric keyboard --> + <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" + android:layout_alignParentBottom="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#00000000" + android:keyBackground="@drawable/btn_keyboard_key_fulltrans" + /> + <!-- emergency call button --> + <Button + android:id="@+id/emergencyCall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + android:layout_marginTop="20dip" + android:layout_marginBottom="20dip" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" + /> </LinearLayout> diff --git a/core/res/res/layout/password_keyboard_input.xml b/core/res/res/layout/password_keyboard_input.xml new file mode 100755 index 0000000..e40b69e --- /dev/null +++ b/core/res/res/layout/password_keyboard_input.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.passwordunlockdemo.LatinKeyboardView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/keyboardView" + android:layout_alignParentBottom="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#00000000" + android:keyBackground="@drawable/btn_keyboard_key_fulltrans" + /> diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml new file mode 100644 index 0000000..647a562 --- /dev/null +++ b/core/res/res/values-land/dimens.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <dimen name="password_keyboard_key_height">47dip</dimen> + <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen> +</resources>
\ No newline at end of file diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 6a3538d..5cea28d 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -4,16 +4,16 @@ ** ** Copyright 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 +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this 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. */ --> @@ -34,4 +34,8 @@ <dimen name="fastscroll_thumb_width">64dp</dimen> <!-- Height of the fastscroll thumb --> <dimen name="fastscroll_thumb_height">52dp</dimen> + <!-- Default height of a key in the password keyboard --> + <dimen name="password_keyboard_key_height">56dip</dimen> + <!-- Default correction for the space key in the password keyboard --> + <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4df570c..89cbd08 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1536,6 +1536,14 @@ <!-- Displayed on lock screen's right tab - turn sound off --> <string name="lockscreen_sound_off_label">Sound off</string> + <!-- Password keyboard strings. Used by LockScreen and Settings --><skip /> + <!-- Label for "switch to symbols" key. Must be short to fit on key! --> + <string name="password_keyboard_label_symbol_key">\?123</string> + <!-- Label for "switch to alphabetic" key. Must be short to fit on key! --> + <string name="password_keyboard_label_alpha_key">ABC</string> + <!-- Label for ALT modifier key. Must be short to fit on key! --> + <string name="password_keyboard_label_alt_key">ALT</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> diff --git a/core/res/res/xml-land/password_kbd_qwerty.xml b/core/res/res/xml-land/password_kbd_qwerty.xml new file mode 100755 index 0000000..a3d4e88 --- /dev/null +++ b/core/res/res/xml-land/password_kbd_qwerty.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="-" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="=" /> + <Key android:codes="46" android:keyLabel="." + android:keyWidth="10%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:iconPreview="@drawable/sym_keyboard_feedback_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml new file mode 100755 index 0000000..2285d91 --- /dev/null +++ b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="_" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="+" /> + <Key android:codes="46" android:keyLabel="."/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:iconPreview="@drawable/sym_keyboard_feedback_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty.xml b/core/res/res/xml-mdpi/password_kbd_qwerty.xml new file mode 100755 index 0000000..a3d4e88 --- /dev/null +++ b/core/res/res/xml-mdpi/password_kbd_qwerty.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="-" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="=" /> + <Key android:codes="46" android:keyLabel="." + android:keyWidth="10%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:iconPreview="@drawable/sym_keyboard_feedback_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml new file mode 100755 index 0000000..2285d91 --- /dev/null +++ b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="_" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="+" /> + <Key android:codes="46" android:keyLabel="."/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:iconPreview="@drawable/sym_keyboard_feedback_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml/password_kbd_extension.xml b/core/res/res/xml/password_kbd_extension.xml new file mode 100755 index 0000000..354594e --- /dev/null +++ b/core/res/res/xml/password_kbd_extension.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row android:rowEdgeFlags="top"> + <Key android:keyLabel="!" android:keyEdgeFlags="left"/> + <Key android:keyLabel="\@"/> + <Key android:keyLabel="\#"/> + <Key android:keyLabel="&"/> + <Key android:keyLabel="-"/> + <Key android:keyLabel="\'"/> + <Key android:keyLabel=":"/> + <Key android:keyLabel="""/> + <Key android:keyLabel="/"/> + <Key android:keyLabel="\?" android:keyEdgeFlags="right" + /> + </Row> + + <Row android:rowEdgeFlags="bottom"> + <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left" + /> + <Key android:codes="50" android:keyLabel="2" + /> + <Key android:codes="51" android:keyLabel="3" + /> + <Key android:codes="52" android:keyLabel="4" + /> + <Key android:codes="53" android:keyLabel="5" + /> + <Key android:codes="54" android:keyLabel="6"/> + <Key android:codes="55" android:keyLabel="7" + /> + <Key android:codes="56" android:keyLabel="8"/> + <Key android:codes="57" android:keyLabel="9"/> + <Key android:codes="48" android:keyLabel="0" + android:keyEdgeFlags="right"/> + </Row> +</Keyboard> diff --git a/core/res/res/xml/password_kbd_numeric.xml b/core/res/res/xml/password_kbd_numeric.xml new file mode 100755 index 0000000..e3f1612 --- /dev/null +++ b/core/res/res/xml/password_kbd_numeric.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="33.33%p" + android:horizontalGap="2px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1" android:keyEdgeFlags="left"/> + <Key android:codes="50" android:keyIcon="@drawable/sym_keyboard_num2"/> + <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"/> + </Row> + + <Row> + <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4" android:keyEdgeFlags="left"/> + <Key android:codes="53" android:keyIcon="@drawable/sym_keyboard_num5"/> + <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"/> + </Row> + + <Row> + <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7" android:keyEdgeFlags="left"/> + <Key android:codes="56" android:keyIcon="@drawable/sym_keyboard_num8"/> + <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"/> + </Row> + + <Row android:rowEdgeFlags="bottom"> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"/> + <Key android:codes="48" android:keyIcon="@drawable/sym_keyboard_num0_no_plus"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> diff --git a/core/res/res/xml/password_kbd_popup_template.xml b/core/res/res/xml/password_kbd_popup_template.xml new file mode 100644 index 0000000..5ddfd3e --- /dev/null +++ b/core/res/res/xml/password_kbd_popup_template.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > +</Keyboard> diff --git a/core/res/res/xml/password_kbd_qwerty.xml b/core/res/res/xml/password_kbd_qwerty.xml new file mode 100755 index 0000000..d4a454b --- /dev/null +++ b/core/res/res/xml/password_kbd_qwerty.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row android:rowEdgeFlags="top"> + <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/> + <Key android:codes="50" android:keyLabel="2"/> + <Key android:codes="51" android:keyLabel="3"/> + <Key android:codes="52" android:keyLabel="4"/> + <Key android:codes="53" android:keyLabel="5"/> + <Key android:codes="54" android:keyLabel="6"/> + <Key android:codes="55" android:keyLabel="7"/> + <Key android:codes="56" android:keyLabel="8"/> + <Key android:codes="57" android:keyLabel="9"/> + <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="-" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="=" /> + <Key android:codes="46" android:keyLabel="." + android:keyWidth="10%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok" + android:iconPreview="@drawable/sym_keyboard_feedback_ok" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml/password_kbd_qwerty_shifted.xml b/core/res/res/xml/password_kbd_qwerty_shifted.xml new file mode 100755 index 0000000..f341d9e --- /dev/null +++ b/core/res/res/xml/password_kbd_qwerty_shifted.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row android:rowEdgeFlags="top"> + <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/> + <Key android:codes="35" android:keyLabel="\#"/> + <Key android:codes="36" android:keyLabel="$"/> + <Key android:codes="37" android:keyLabel="%"/> + <Key android:codes="38" android:keyLabel="&"/> + <Key android:codes="42" android:keyLabel="*"/> + <Key android:codes="45" android:keyLabel="-"/> + <Key android:keyLabel="+"/> + <Key android:codes="40" android:keyLabel="("/> + <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y"/> + <Key android:codes="117" android:keyLabel="u"/> + <Key android:codes="105" android:keyLabel="i"/> + <Key android:codes="111" android:keyLabel="o"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left"/> + <Key android:codes="115" android:keyLabel="s"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:iconPreview="@drawable/sym_keyboard_feedback_shift" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," /> + <Key android:keyLabel="_" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:keyWidth="20%p" android:isRepeatable="true"/> + <Key android:keyLabel="+" /> + <Key android:codes="46" android:keyLabel="."/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok" + android:iconPreview="@drawable/sym_keyboard_feedback_ok" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> + +</Keyboard> + diff --git a/core/res/res/xml/password_kbd_symbols.xml b/core/res/res/xml/password_kbd_symbols.xml new file mode 100755 index 0000000..c97e6ae --- /dev/null +++ b/core/res/res/xml/password_kbd_symbols.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/> + <Key android:codes="50" android:keyLabel="2"/> + <Key android:codes="51" android:keyLabel="3"/> + <Key android:codes="52" android:keyLabel="4"/> + <Key android:codes="53" android:keyLabel="5"/> + <Key android:codes="54" android:keyLabel="6"/> + <Key android:codes="55" android:keyLabel="7"/> + <Key android:codes="56" android:keyLabel="8"/> + <Key android:codes="57" android:keyLabel="9"/> + <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/> + <Key android:codes="35" android:keyLabel="\#"/> + <Key android:codes="36" android:keyLabel="$"/> + <Key android:codes="37" android:keyLabel="%"/> + <Key android:codes="38" android:keyLabel="&"/> + <Key android:codes="42" android:keyLabel="*"/> + <Key android:codes="45" android:keyLabel="-"/> + <Key android:keyLabel="+"/> + <Key android:codes="40" android:keyLabel="(" + android:popupKeyboard="@xml/password_kbd_popup_template" + android:popupCharacters="[{<" + /> + <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right" + android:popupKeyboard="@xml/password_kbd_popup_template" + android:popupCharacters="]}>" + /> + </Row> + + <Row> + <Key android:codes="-1" android:keyLabel="@string/password_keyboard_label_alt_key" + android:keyWidth="15%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="33" android:keyLabel="!"/> + <Key android:codes="34" android:keyLabel="""/> + <Key android:codes="39" android:keyLabel="\'"/> + <Key android:codes="58" android:keyLabel=":"/> + <Key android:codes="59" android:keyLabel=";"/> + <Key android:codes="47" android:keyLabel="/" /> + <Key android:codes="63" android:keyLabel="\?"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true"/> + </Row> + + <Row android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_alpha_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="," android:keyWidth="10%p"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="40%p" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:isRepeatable="true"/> + <Key android:keyLabel="." android:keyWidth="10%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok" + android:keyWidth="20%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_ok" + /> + </Row> +</Keyboard> diff --git a/core/res/res/xml/password_kbd_symbols_shift.xml b/core/res/res/xml/password_kbd_symbols_shift.xml new file mode 100755 index 0000000..97ec3c5f --- /dev/null +++ b/core/res/res/xml/password_kbd_symbols_shift.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/password_keyboard_key_height" + > + + <Row> + <Key android:keyLabel="~" android:keyEdgeFlags="left"/> + <Key android:keyLabel="`"/> + <Key android:keyLabel="|"/> + <Key android:keyLabel="•"/> + <Key android:keyLabel="√"/> + <Key android:keyLabel="Ï€"/> + <Key android:keyLabel="÷"/> + <Key android:keyLabel="×"/> + <Key android:keyLabel="{"/> + <Key android:keyLabel="}" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:keyLabel="Â¥" android:keyEdgeFlags="left"/> + <Key android:keyLabel="£"/> + <Key android:keyLabel="¢"/> + <Key android:keyLabel="€"/> + <Key android:keyLabel="°"/> + <Key android:keyLabel="^"/> + <Key android:keyLabel="_"/> + <Key android:keyLabel="="/> + <Key android:keyLabel="["/> + <Key android:keyLabel="]" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyLabel="@string/password_keyboard_label_alt_key" + android:keyWidth="15%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:keyLabel="â„¢"/> + <Key android:keyLabel="®"/> + <Key android:keyLabel="©"/> + <Key android:keyLabel="¶"/> + <Key android:keyLabel="\\"/> + <Key android:keyLabel="<"/> + <Key android:keyLabel=">"/> + <Key android:codes="-5" + android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_delete" + android:isRepeatable="true" + /> + </Row> + + <Row android:rowEdgeFlags="bottom"> + <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_alpha_key" + android:keyWidth="20%p" android:keyEdgeFlags="left"/> + <Key android:keyLabel="„" android:keyWidth="10%p" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="40%p" + android:iconPreview="@drawable/sym_keyboard_feedback_space" + android:isRepeatable="true"/> + <Key android:keyLabel="…" android:keyWidth="10%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok" + android:keyWidth="20%p" android:keyEdgeFlags="right" + android:iconPreview="@drawable/sym_keyboard_feedback_ok" + /> + </Row> +</Keyboard> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 8d7e187..6bc6f2e 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -74,6 +74,10 @@ <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" /> <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" /> + <!-- package manager test permissions --> + <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> + <uses-permission android:name="android.permission.DELETE_PACKAGES" /> + <application android:theme="@style/Theme"> <uses-library android:name="android.test.runner" /> <activity android:name="StubTestBrowserActivity" android:label="Stubbed Test Browser"> diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java index fad4349..fb5a36f 100644 --- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java +++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java @@ -16,6 +16,7 @@ package android.database; +import dalvik.annotation.BrokenTest; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -373,7 +374,9 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe c.close(); } - @LargeTest + //@LargeTest + @BrokenTest("Consistently times out") + @Suppress public void testLoadingThread() throws Exception { mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); @@ -398,7 +401,9 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe c.close(); } - @LargeTest + //@LargeTest + @BrokenTest("Consistently times out") + @Suppress public void testLoadingThreadClose() throws Exception { mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); @@ -450,9 +455,11 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); final int count = 36799; + mDatabase.execSQL("BEGIN Transaction;"); for (int i = 0; i < count; i++) { mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");"); } + mDatabase.execSQL("COMMIT;"); Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null); assertNotNull(c); @@ -484,9 +491,11 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe // if cursor window size changed, adjust this value too final int count = 600; // more than two fillWindow needed + mDatabase.execSQL("BEGIN Transaction;"); for (int i = 0; i < count; i++) { mDatabase.execSQL(sql.toString()); } + mDatabase.execSQL("COMMIT;"); Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null); assertNotNull(c); @@ -513,6 +522,7 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe // if cursor window size changed, adjust this value too final int count = 600; + mDatabase.execSQL("BEGIN Transaction;"); for (int i = 0; i < count; i++) { StringBuilder sql = new StringBuilder(2100); sql.append("INSERT INTO test (txt, data) VALUES ('"); @@ -522,6 +532,7 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe sql.append("');"); mDatabase.execSQL(sql.toString()); } + mDatabase.execSQL("COMMIT;"); Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null); assertNotNull(c); diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java deleted file mode 100644 index ea807bd..0000000 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.database.sqlite; - -import junit.framework.TestCase; - -/** - * Tests for the SQLiteDebug - */ -public class SQLiteDebugTest extends TestCase { - private static final String TEST_DB = "test.db"; - - public void testCaptureSql() { - String rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=? and b=1", - new Object[] {"blah"}); - String expectedVal = "select * from t1 where a='blah' and b=1"; - assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal)); - - rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=?", - new Object[] {"blah"}); - expectedVal = "select * from t1 where a='blah'"; - assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal)); - - rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=1", - new Object[] {"blah"}); - assertTrue(rslt.startsWith("too many bindArgs provided.")); - - rslt = SQLiteDebug.captureSql(TEST_DB, "update t1 set a=? where b=?", - new Object[] {"blah", "foo"}); - expectedVal = "update t1 set a='blah' where b='foo'"; - assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal)); - } -} diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf Binary files differindex 61460b1..a935f16 100755..100644 --- a/data/fonts/DroidSansFallback.ttf +++ b/data/fonts/DroidSansFallback.ttf diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf Binary files differnew file mode 100644 index 0000000..61460b1 --- /dev/null +++ b/data/fonts/DroidSansFallbackLegacy.ttf diff --git a/docs/html/resources/articles/images/live_wallpapers_small.png b/docs/html/resources/articles/images/live_wallpapers_small.png Binary files differnew file mode 100644 index 0000000..3b49b24 --- /dev/null +++ b/docs/html/resources/articles/images/live_wallpapers_small.png diff --git a/docs/html/resources/articles/index.jd b/docs/html/resources/articles/index.jd index 4fda6d7..d2f0996 100644 --- a/docs/html/resources/articles/index.jd +++ b/docs/html/resources/articles/index.jd @@ -77,6 +77,11 @@ page.title=Technical Articles </dl> <dl> + <dt><a href="{@docRoot}resources/articles/live-wallpapers.html">Live Wallpapers</a></dt> + <dd>Live wallpapers are richer, animated, interactive backgrounds that users can display in their home screens. Learn how to create a live wallpaper and bundle it in an application that users can install on their devices.</dd> +</dl> + +<dl> <dt><a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a></dt> <dd>The Input Method Framework (IMF) allows users to take advantage of on-screen input methods, such as software keyboards. This article provides an overview of Input Method Editors (IMEs) and how applications interact with them.</dd> </dl> diff --git a/docs/html/resources/articles/live-wallpapers.jd b/docs/html/resources/articles/live-wallpapers.jd new file mode 100644 index 0000000..8dda879 --- /dev/null +++ b/docs/html/resources/articles/live-wallpapers.jd @@ -0,0 +1,84 @@ +page.title=Live Wallpapers +@jd:body + +<p>Starting with Android 2.1 (API Level 7), users can now enjoy <em>live +wallpapers</em> — richer, animated, interactive backgrounds — on +their home screens. A live wallpaper is very similar to a normal Android +application and has access to all the facilities of the platform: SGL (2D +drawing), OpenGL (3D drawing), GPS, accelerometers, network access, etc. The +live wallpapers included on Nexus One demonstrate the use of some of these APIs +to create fun and interesting user experiences. For instance, the Grass +wallpaper uses the phone's location to compute sunrise and sunset times in order +to display the appropriate sky.</p> + +<img src="images/live_wallpapers_small.png" style="align:center" /> + +<p>Creating your own live wallpaper is easy, especially if you have had +previous experience with <a +href="../../../reference/android/view/SurfaceView.html"><code>SurfaceView</code></a> or <a +href="../../../reference/android/graphics/Canvas.html"><code>Canvas</code></a>. +To learn how to create a live wallpaper, you should check out the <a +href="../samples/CubeLiveWallpaper/index.html">CubeLiveWallpaper sample code</a>.</p> + +<p>In terms of implementation, a live wallpaper is very similar to a regular +Android <a href="../../../reference/android/app/Service.html">service</a>. The +only difference is the addition of a new method, <a +href="../../../reference/android/service/wallpaper/WallpaperService. +html#onCreateEngine()"><code>onCreateEngine()</code></a>, whose goal is to create a <a +href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html"> +<code>WallpaperService.Engine</code></a>. The engine is responsible for +handling the lifecycle and drawing of a wallpaper. The system provides a surface +on which you can draw, just like you would with a <code>SurfaceView</code></a>. +Drawing a wallpaper can be very expensive so you should optimize your code +as much as possible to avoid using too much CPU, not only for battery life +but also to avoid slowing down the rest of the system. That is also why the +most important part of the lifecycle of a wallpaper is <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onVisibilityChanged%28boolean%29">when it becomes invisible</a>. +When invisible, such as when the user launches an application that covers +the home screen, a wallpaper must stop all activity.</p> + +<p>The engine can also implement several methods to interact with the user +or the home application. For instance, if you want your wallpaper to scroll +along when the user swipes from one home screen to another, you can use <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onOffsetsChanged%28float,%20float,%20float,%20float,%20int,%20int%29"><code>onOffsetsChanged()</code></a>. +To react to touch events, simply implement <a href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onTouchEvent%28android.view.MotionEvent%29"><code>onTouchEvent(MotionEvent)</code></a>. +Finally, applications can send arbitrary commands to the live wallpaper. +Currently, only the standard home application sends commands to the <a +href="../../../reference/android/service/wallpaper/WallpaperService.Engine.html#onCommand%28java.lang.String,%20int,%20int,%20int,%20android.os.Bundle,%20boolean%29"><code>onCommand()</code></a> +method of the live wallpaper:</p> + +<ul> +<li><code>android.wallpaper.tap</code>: When the user taps an empty space +on the workspace. This command is interpreted by the Nexus and Water live +wallpapers to make the wallpaper react to user interaction. For instance, +if you tap an empty space on the Water live wallpaper, new ripples appear +under your finger.</li> +<li><code>android.home.drop</code>: When the user drops an icon or a widget +on the workspace. This command is also interpreted by the Nexus and Water +live wallpapers.</li> +</ul> + +<p>If you are developing a live wallpaper, remember that the feature is +supported only on Android 2.1 (API level 7) and higher versions of the platform. +To ensure that your application can only be installed on devices that support +live wallpapers, remember to add the following to the application's manifest +before publishing to Android Market:</p> + +<ul> +<li><code><uses-sdk android:minSdkVersion="7" /></code>, which indicates +to Android Market and the platform that your application requires Android 2.1 or +higher. For more information, see the <a href="../../../guide/appendix/api-levels.html">API +Levels</a> and the documentation for the +<a href="../../../guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a> +element.</li> +<li><code><uses-feature android:name="android.software.live_wallpaper" /></code>, +which tells Android Market that your application includes a live wallpaper +Android Market uses this feature as a filter, when presenting users lists of +available applications. When you declaring this feature, Android Market +displays your application only to users whose devices support live wallpapers, +while hiding it from other devices on which it would not be able to run. For +more information, see the documentation for the +<a href="../../../guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a> +element.</li> +</ul> + +<p>Many great live wallpapers are already available on Android Market and +we can't wait to see more!</p> diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs index e337e38..0972029 100644 --- a/docs/html/resources/resources_toc.cs +++ b/docs/html/resources/resources_toc.cs @@ -83,6 +83,9 @@ <li><a href="<?cs var:toroot ?>resources/articles/live-folders.html"> <span class="en">Live Folders</span> </a></li> + <li><a href="<?cs var:toroot ?>resources/articles/live-wallpapers.html"> + <span class="en">Live Wallpapers</span> + </a> <span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>resources/articles/on-screen-inputs.html"> <span class="en">Onscreen Input Methods</span> </a></li> @@ -178,28 +181,34 @@ </a></li> <li><a href="<?cs var:toroot ?>resources/samples/BluetoothChat/index.html"> <span class="en">Bluetooth Chat</span> - </a> <span class="new">new!</span></li> + </a></li> <li><a href="<?cs var:toroot ?>resources/samples/BusinessCard/index.html"> <span class="en">Business Card</span> - </a> <span class="new">new!</span></li> + </a></li> <li><a href="<?cs var:toroot ?>resources/samples/ContactManager/index.html"> <span class="en">Contact Manager</span> - </a> <span class="new">new!</span></li> + </a></li> <li><a href="<?cs var:toroot ?>resources/samples/Home/index.html"> <span class="en">Home</span> </a></li> <li><a href="<?cs var:toroot ?>resources/samples/JetBoy/index.html"> <span class="en">JetBoy</span> </a></li> + <li><a href="<?cs var:toroot ?>resources/samples/CubeLiveWallpaper/index.html"> + <span class="en">Live Wallpaper</span> + </a> <span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>resources/samples/LunarLander/index.html"> <span class="en">Lunar Lander</span> </a></li> <li><a href="<?cs var:toroot ?>resources/samples/MultiResolution/index.html"> <span class="en">Multiple Resolutions</span> - </a> <span class="new">new!</span></li> + </a></li> <li><a href="<?cs var:toroot ?>resources/samples/NotePad/index.html"> <span class="en">Note Pad</span> </a></li> + <li><a href="<?cs var:toroot ?>resources/samples/SampleSyncAdapter/index.html"> + <span class="en">Sample Sync Adapter</span> + </a> <span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>resources/samples/SearchableDictionary/index.html"> <span class="en">Searchable Dictionary</span> </a></li> @@ -211,10 +220,10 @@ </a></li> <li><a href="<?cs var:toroot ?>resources/samples/Wiktionary/index.html"> <span class="en">Wiktionary</span> - </a> <span class="new">new!</span></li> + </a></li> <li><a href="<?cs var:toroot ?>resources/samples/WiktionarySimple/index.html"> <span class="en">Wiktionary (Simplified)</span> - </a> <span class="new">new!</span></li> + </a></li> </ul> </li> </ul> diff --git a/docs/html/resources/samples/images/CubeLiveWallpaper1.png b/docs/html/resources/samples/images/CubeLiveWallpaper1.png Binary files differnew file mode 100644 index 0000000..55bc1e9 --- /dev/null +++ b/docs/html/resources/samples/images/CubeLiveWallpaper1.png diff --git a/docs/html/resources/samples/images/CubeLiveWallpaper3.png b/docs/html/resources/samples/images/CubeLiveWallpaper3.png Binary files differnew file mode 100644 index 0000000..2747a88 --- /dev/null +++ b/docs/html/resources/samples/images/CubeLiveWallpaper3.png diff --git a/docs/html/resources/samples/index.jd b/docs/html/resources/samples/index.jd index 0beb781..5ebf41c 100644 --- a/docs/html/resources/samples/index.jd +++ b/docs/html/resources/samples/index.jd @@ -27,10 +27,10 @@ android update project -s -n API Demos -t <em><target_ID></em> -p <em><pat platforms) and allow you to view the source files in your browser. </p> <div class="special"> - <p>Some of the samples in this listing are not yet available in the - SDK. While we work to update the SDK, you can - <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP - archive.</p> + <p>Some of the samples in this listing may not yet be available in the + SDK. To ensure that you have the latest versions of the samples, you can + <a href="{@docRoot}shareables/latest_samples.zip">download the samples pack</a> + as a .zip archive.</p> </div> <dl> @@ -55,11 +55,15 @@ platforms) and allow you to view the source files in your browser. </p> <dt><a href="Home/index.html">Home</a></dt> <dd>A home screen replacement application.</dd> - + <dt><a href="JetBoy/index.html">JetBoy</a></dt> <dd>A game that demonstrates the SONiVOX JET interactive music technology, with {@link android.media.JetPlayer}.</dd> - + + <dt><a href="CubeLiveWallpaper/index.html">Live Wallpaper</a></dt> + <dd>An application that demonstrates how to create a live wallpaper and + bundle it in an application that users can install on their devices.</dd> + <dt><a href="LunarLander/index.html">Lunar Lander</a></dt> <dd>A classic Lunar Lander game.</dd> @@ -70,14 +74,21 @@ platforms) and allow you to view the source files in your browser. </p> <dt><a href="NotePad/index.html">Note Pad</a></dt> <dd>An application for saving notes. Similar (but not identical) to the <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd> - + + <dt><a href="SampleSyncAdapter/index.html">SampleSyncAdapter</a></dt> + <dd>Demonstrates how an application can communicate with a +cloud-based service and synchronize its data with data stored locally in a +content provider. The sample uses two related parts of the Android framework +— the account manager and the synchronization manager (through a sync +adapter).</dd> + <dt><a href="SearchableDictionary/index.html">Searchable Dictionary</a></dt> <dd>A sample application that demonstrates Android's search framework, including how to provide search suggestions for Quick Search Box.</dd> - + <dt><a href="Snake/index.html">Snake</a></dt> <dd>An implementation of the classic game "Snake."</dd> - + <dt><a href="SoftKeyboard/index.html">Soft Keyboard</a></dt> <dd>An example of writing an input method for a software keyboard.</dd> diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java index ad1bb54..1d3f82d 100644 --- a/graphics/java/android/renderscript/RSSurfaceView.java +++ b/graphics/java/android/renderscript/RSSurfaceView.java @@ -36,7 +36,7 @@ import android.view.SurfaceView; **/ public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mSurfaceHolder; - private RenderScript mRS; + private RenderScriptGL mRS; /** * Standard View constructor. In order to render something, you @@ -146,13 +146,13 @@ public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback // ---------------------------------------------------------------------- - public RenderScript createRenderScript(boolean useDepth, boolean forceSW) { + public RenderScriptGL createRenderScript(boolean useDepth, boolean forceSW) { Log.v(RenderScript.LOG_TAG, "createRenderScript"); - mRS = new RenderScript(useDepth, forceSW); + mRS = new RenderScriptGL(useDepth, forceSW); return mRS; } - public RenderScript createRenderScript(boolean useDepth) { + public RenderScriptGL createRenderScript(boolean useDepth) { return createRenderScript(useDepth, false); } diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 29361af..84b1a70 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -31,11 +31,9 @@ import android.view.Surface; **/ public class RenderScript { static final String LOG_TAG = "RenderScript_jni"; - private static final boolean DEBUG = false; + protected static final boolean DEBUG = false; @SuppressWarnings({"UnusedDeclaration", "deprecation"}) - private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - int mWidth; - int mHeight; + protected static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; @@ -44,8 +42,8 @@ public class RenderScript { * field offsets. */ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) - private static boolean sInitialized; - native private static void _nInit(); + protected static boolean sInitialized; + native protected static void _nInit(); static { @@ -64,7 +62,8 @@ public class RenderScript { native int nDeviceCreate(); native void nDeviceDestroy(int dev); native void nDeviceSetConfig(int dev, int param, int value); - native int nContextCreate(int dev, int ver, boolean useDepth); + native int nContextCreateGL(int dev, int ver, boolean useDepth); + native int nContextCreate(int dev, int ver); native void nContextDestroy(int con); native void nContextSetSurface(int w, int h, Surface sur); native void nContextSetPriority(int p); @@ -190,11 +189,10 @@ public class RenderScript { native void nAnimationAdd(float time, float[] attribs); native int nAnimationCreate(); - private int mDev; - private int mContext; + protected int mDev; + protected int mContext; @SuppressWarnings({"FieldCanBeLocal"}) - private Surface mSurface; - private MessageThread mMessageThread; + protected MessageThread mMessageThread; Element mElement_USER_U8; Element mElement_USER_I8; @@ -251,7 +249,7 @@ public class RenderScript { nContextSetPriority(p.mID); } - private static class MessageThread extends Thread { + protected static class MessageThread extends Thread { RenderScript mRS; boolean mRun = true; @@ -289,26 +287,18 @@ public class RenderScript { } } - public RenderScript(boolean useDepth, boolean forceSW) { - mSurface = null; - mWidth = 0; - mHeight = 0; - mDev = nDeviceCreate(); - if(forceSW) { - nDeviceSetConfig(mDev, 0, 1); - } - mContext = nContextCreate(mDev, 0, useDepth); - mMessageThread = new MessageThread(this); - mMessageThread.start(); - Element.initPredefined(this); + protected RenderScript() { } - public void contextSetSurface(int w, int h, Surface sur) { - mSurface = sur; - mWidth = w; - mHeight = h; - validate(); - nContextSetSurface(w, h, mSurface); + public static RenderScript create() { + RenderScript rs = new RenderScript(); + + rs.mDev = rs.nDeviceCreate(); + rs.mContext = rs.nContextCreate(rs.mDev, 0); + rs.mMessageThread = new MessageThread(rs); + rs.mMessageThread.start(); + Element.initPredefined(rs); + return rs; } public void contextDump(int bits) { @@ -332,77 +322,15 @@ public class RenderScript { return mContext != 0; } - void pause() { - validate(); - nContextPause(); - } - - void resume() { - validate(); - nContextResume(); - } - - ////////////////////////////////////////////////////////////////////////////////// - // File - - public class File extends BaseObj { - File(int id) { - super(RenderScript.this); - mID = id; - } - } - - public File fileOpen(String s) throws IllegalStateException, IllegalArgumentException - { - if(s.length() < 1) { - throw new IllegalArgumentException("fileOpen does not accept a zero length string."); - } - - try { - byte[] bytes = s.getBytes("UTF-8"); - int id = nFileOpen(bytes); - return new File(id); - } catch (java.io.UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - /////////////////////////////////////////////////////////////////////////////////// // Root state - private int safeID(BaseObj o) { + protected int safeID(BaseObj o) { if(o != null) { return o.mID; } return 0; } - - public void contextBindRootScript(Script s) { - validate(); - nContextBindRootScript(safeID(s)); - } - - public void contextBindProgramFragmentStore(ProgramStore p) { - validate(); - nContextBindProgramFragmentStore(safeID(p)); - } - - public void contextBindProgramFragment(ProgramFragment p) { - validate(); - nContextBindProgramFragment(safeID(p)); - } - - public void contextBindProgramRaster(ProgramRaster p) { - validate(); - nContextBindProgramRaster(safeID(p)); - } - - public void contextBindProgramVertex(ProgramVertex p) { - validate(); - nContextBindProgramVertex(safeID(p)); - } - } diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java new file mode 100644 index 0000000..d1df23d --- /dev/null +++ b/graphics/java/android/renderscript/RenderScriptGL.java @@ -0,0 +1,127 @@ +/* + * 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.renderscript; + +import java.lang.reflect.Field; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Config; +import android.util.Log; +import android.view.Surface; + + +/** + * @hide + * + **/ +public class RenderScriptGL extends RenderScript { + private Surface mSurface; + int mWidth; + int mHeight; + + + public RenderScriptGL(boolean useDepth, boolean forceSW) { + mSurface = null; + mWidth = 0; + mHeight = 0; + mDev = nDeviceCreate(); + if(forceSW) { + nDeviceSetConfig(mDev, 0, 1); + } + mContext = nContextCreateGL(mDev, 0, useDepth); + mMessageThread = new MessageThread(this); + mMessageThread.start(); + Element.initPredefined(this); + } + + public void contextSetSurface(int w, int h, Surface sur) { + mSurface = sur; + mWidth = w; + mHeight = h; + validate(); + nContextSetSurface(w, h, mSurface); + } + + + void pause() { + validate(); + nContextPause(); + } + + void resume() { + validate(); + nContextResume(); + } + + + public void contextBindRootScript(Script s) { + validate(); + nContextBindRootScript(safeID(s)); + } + + public void contextBindProgramFragmentStore(ProgramStore p) { + validate(); + nContextBindProgramFragmentStore(safeID(p)); + } + + public void contextBindProgramFragment(ProgramFragment p) { + validate(); + nContextBindProgramFragment(safeID(p)); + } + + public void contextBindProgramRaster(ProgramRaster p) { + validate(); + nContextBindProgramRaster(safeID(p)); + } + + public void contextBindProgramVertex(ProgramVertex p) { + validate(); + nContextBindProgramVertex(safeID(p)); + } + + + + + ////////////////////////////////////////////////////////////////////////////////// + // File + + public class File extends BaseObj { + File(int id) { + super(RenderScriptGL.this); + mID = id; + } + } + + public File fileOpen(String s) throws IllegalStateException, IllegalArgumentException + { + if(s.length() < 1) { + throw new IllegalArgumentException("fileOpen does not accept a zero length string."); + } + + try { + byte[] bytes = s.getBytes("UTF-8"); + int id = nFileOpen(bytes); + return new File(id); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + +} + + diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 7ded133..4d35c37 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -151,10 +151,17 @@ nDeviceSetConfig(JNIEnv *_env, jobject _this, jint dev, jint p, jint value) } static jint -nContextCreate(JNIEnv *_env, jobject _this, jint dev, jint ver, jboolean useDepth) +nContextCreate(JNIEnv *_env, jobject _this, jint dev, jint ver) { LOG_API("nContextCreate"); - return (jint)rsContextCreate((RsDevice)dev, ver, useDepth); + return (jint)rsContextCreate((RsDevice)dev, ver); +} + +static jint +nContextCreateGL(JNIEnv *_env, jobject _this, jint dev, jint ver, jboolean useDepth) +{ + LOG_API("nContextCreateGL"); + return (jint)rsContextCreateGL((RsDevice)dev, ver, useDepth); } static void @@ -260,7 +267,7 @@ nElementCreate2(JNIEnv *_env, jobject _this, jintArray _ids, jobjectArray _names { int fieldCount = _env->GetArrayLength(_ids); RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", con, type, kind, norm, size); + LOG_API("nElementCreate2, con(%p)", con); jint *ids = _env->GetIntArrayElements(_ids, NULL); const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *)); @@ -1089,7 +1096,7 @@ nProgramFragmentCreate(JNIEnv *_env, jobject _this, jintArray params) jint *paramPtr = _env->GetIntArrayElements(params, NULL); jint paramLen = _env->GetArrayLength(params); - LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, shaderLen, paramLen); + LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, paramLen); jint ret = (jint)rsProgramFragmentCreate(con, (uint32_t *)paramPtr, paramLen); _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); @@ -1332,7 +1339,8 @@ static JNINativeMethod methods[] = { {"nDeviceCreate", "()I", (void*)nDeviceCreate }, {"nDeviceDestroy", "(I)V", (void*)nDeviceDestroy }, {"nDeviceSetConfig", "(III)V", (void*)nDeviceSetConfig }, -{"nContextCreate", "(IIZ)I", (void*)nContextCreate }, +{"nContextCreate", "(II)I", (void*)nContextCreate }, +{"nContextCreateGL", "(IIZ)I", (void*)nContextCreateGL }, {"nContextSetPriority", "(I)V", (void*)nContextSetPriority }, {"nContextSetSurface", "(IILandroid/view/Surface;)V", (void*)nContextSetSurface }, {"nContextDestroy", "(I)V", (void*)nContextDestroy }, diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h index 113c452..022b849 100644 --- a/include/media/mediametadataretriever.h +++ b/include/media/mediametadataretriever.h @@ -54,6 +54,7 @@ enum { METADATA_KEY_VIDEO_WIDTH = 20, METADATA_KEY_WRITER = 21, METADATA_KEY_MIMETYPE = 22, + METADATA_KEY_DISC_NUMBER = 23, // Add more here... }; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 3b21468..8f423f7 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -59,6 +59,7 @@ enum { kKeyAlbumArtMIME = 'alAM', // cstring kKeyAuthor = 'auth', // cstring kKeyCDTrackNumber = 'cdtr', // cstring + kKeyDiscNumber = 'dnum', // cstring kKeyDate = 'date', // cstring kKeyWriter = 'writ', // cstring }; diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h index 2c29bfb..e328f33 100644 --- a/include/ui/CameraParameters.h +++ b/include/ui/CameraParameters.h @@ -187,6 +187,13 @@ public: // Vertical angle of view in degrees. // Example value: "42.5". Read only. static const char KEY_VERTICAL_VIEW_ANGLE[]; + // Exposure compensation. The value is multiplied by 100. -100 means -1 EV. + // 130 means +1.3 EV. + // Example value: "0" or "133". Read/write. + static const char KEY_EXPOSURE_COMPENSATION[]; + // Supported exposure compensation. + // Example value: "-100,-66,-33,0,33,66,100". Read only. + static const char KEY_SUPPORTED_EXPOSURE_COMPENSATION[]; // Values for white balance settings. diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 88521f6..98464a0 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -110,5 +110,9 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) +# include the java examples +include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\ + java \ + )) endif #simulator diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 6662333..02667d8 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -55,7 +55,8 @@ RsDevice rsDeviceCreate(); void rsDeviceDestroy(RsDevice); void rsDeviceSetConfig(RsDevice, RsDeviceParam, int32_t value); -RsContext rsContextCreate(RsDevice, uint32_t version, bool useDepth); +RsContext rsContextCreate(RsDevice, uint32_t version); +RsContext rsContextCreateGL(RsDevice, uint32_t version, bool useDepth); void rsContextDestroy(RsContext); void rsObjDestroyOOB(RsContext, void *); diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java index b80e619..7d04502 100644 --- a/libs/rs/java/Film/src/com/android/film/FilmRS.java +++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java @@ -40,7 +40,7 @@ public class FilmRS { public FilmRS() { } - public void init(RenderScript rs, Resources res, int width, int height) { + public void init(RenderScriptGL rs, Resources res, int width, int height) { mRS = rs; mRes = res; initRS(); @@ -65,7 +65,7 @@ public class FilmRS { private Resources mRes; - private RenderScript mRS; + private RenderScriptGL mRS; private Script mScriptStrip; private Script mScriptImage; private Sampler mSampler; diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java index 4a201fd..5bc2811 100644 --- a/libs/rs/java/Film/src/com/android/film/FilmView.java +++ b/libs/rs/java/Film/src/com/android/film/FilmView.java @@ -22,6 +22,7 @@ import java.util.concurrent.Semaphore; import android.renderscript.RSSurfaceView; import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; import android.content.Context; import android.content.res.Resources; @@ -45,7 +46,7 @@ public class FilmView extends RSSurfaceView { //setFocusable(true); } - private RenderScript mRS; + private RenderScriptGL mRS; private FilmRS mRender; diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java index 71f95a7..9356579 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java @@ -37,7 +37,7 @@ public class FountainRS { public FountainRS() { } - public void init(RenderScript rs, Resources res, int width, int height) { + public void init(RenderScriptGL rs, Resources res, int width, int height) { mRS = rs; mRes = res; initRS(); @@ -65,7 +65,7 @@ public class FountainRS { private Resources mRes; - private RenderScript mRS; + private RenderScriptGL mRS; private Allocation mIntAlloc; private SimpleMesh mSM; private SomeData mSD; diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java index fcb93f4..dfd6a49 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java @@ -22,6 +22,7 @@ import java.util.concurrent.Semaphore; import android.renderscript.RSSurfaceView; import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; import android.content.Context; import android.content.res.Resources; @@ -45,7 +46,7 @@ public class FountainView extends RSSurfaceView { //setFocusable(true); } - private RenderScript mRS; + private RenderScriptGL mRS; private FountainRS mRender; public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index 0ca00b3..568d3ab 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java @@ -126,14 +126,13 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - mRS.contextSetSurface(width, height, holder.getSurface()); } public void surfaceDestroyed(SurfaceHolder holder) { } private Script.Invokable createScript() { - mRS = new RenderScript(false, false); + mRS = RenderScript.create(); mRS.mMessageCallback = new FilterCallback(); mParamsType = Type.createFromClass(mRS, Params.class, 1, "Parameters"); @@ -164,7 +163,7 @@ public class ImageProcessingActivity extends Activity implements SurfaceHolder.C sb.setType(true, 2); Script.Invokable invokable = sb.addInvokable("main"); sb.setScript(getResources(), R.raw.threshold); - sb.setRoot(true); + //sb.setRoot(true); ScriptC script = sb.create(); script.bindAllocation(mParamsAllocation, 0); diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 261b827..2e47ea3 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -289,15 +289,17 @@ void * Context::threadProc(void *vrsc) LOGE("pthread_setspecific %i", status); } - rsc->mStateRaster.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setRaster(NULL); - rsc->mStateVertex.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setVertex(NULL); - rsc->mStateFragment.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setFragment(NULL); - rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setFragmentStore(NULL); - rsc->mStateVertexArray.init(rsc); + if (rsc->mIsGraphicsContext) { + rsc->mStateRaster.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); + rsc->setRaster(NULL); + rsc->mStateVertex.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); + rsc->setVertex(NULL); + rsc->mStateFragment.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); + rsc->setFragment(NULL); + rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); + rsc->setFragmentStore(NULL); + rsc->mStateVertexArray.init(rsc); + } rsc->mRunning = true; bool mDraw = true; @@ -307,7 +309,7 @@ void * Context::threadProc(void *vrsc) mDraw &= (rsc->mWndSurface != NULL); uint32_t targetTime = 0; - if (mDraw) { + if (mDraw && rsc->mIsGraphicsContext) { targetTime = rsc->runRootScript(); mDraw = targetTime && !rsc->mPaused; rsc->timerSet(RS_TIMER_CLEAR_SWAP); @@ -329,23 +331,27 @@ void * Context::threadProc(void *vrsc) } LOGV("RS Thread exiting"); - rsc->mRaster.clear(); - rsc->mFragment.clear(); - rsc->mVertex.clear(); - rsc->mFragmentStore.clear(); - rsc->mRootScript.clear(); - rsc->mStateRaster.deinit(rsc); - rsc->mStateVertex.deinit(rsc); - rsc->mStateFragment.deinit(rsc); - rsc->mStateFragmentStore.deinit(rsc); + if (rsc->mIsGraphicsContext) { + rsc->mRaster.clear(); + rsc->mFragment.clear(); + rsc->mVertex.clear(); + rsc->mFragmentStore.clear(); + rsc->mRootScript.clear(); + rsc->mStateRaster.deinit(rsc); + rsc->mStateVertex.deinit(rsc); + rsc->mStateFragment.deinit(rsc); + rsc->mStateFragmentStore.deinit(rsc); + } ObjectBase::zeroAllUserRef(rsc); rsc->mObjDestroy.mNeedToEmpty = true; rsc->objDestroyOOBRun(); - pthread_mutex_lock(&gInitMutex); - rsc->deinitEGL(); - pthread_mutex_unlock(&gInitMutex); + if (rsc->mIsGraphicsContext) { + pthread_mutex_lock(&gInitMutex); + rsc->deinitEGL(); + pthread_mutex_unlock(&gInitMutex); + } LOGV("RS Thread exited"); return NULL; @@ -371,7 +377,7 @@ void Context::setPriority(int32_t p) #endif } -Context::Context(Device *dev, bool useDepth) +Context::Context(Device *dev, bool isGraphics, bool useDepth) { pthread_mutex_lock(&gInitMutex); @@ -383,6 +389,8 @@ Context::Context(Device *dev, bool useDepth) mPaused = false; mObjHead = NULL; memset(&mEGL, 0, sizeof(mEGL)); + memset(&mGL, 0, sizeof(mGL)); + mIsGraphicsContext = isGraphics; int status; pthread_attr_t threadAttr; @@ -454,7 +462,7 @@ Context::~Context() void Context::setSurface(uint32_t w, uint32_t h, Surface *sur) { - LOGV("setSurface %i %i %p", w, h, sur); + rsAssert(mIsGraphicsContext); EGLBoolean ret; if (mEGL.mSurface != NULL) { @@ -544,21 +552,25 @@ void Context::setSurface(uint32_t w, uint32_t h, Surface *sur) void Context::pause() { + rsAssert(mIsGraphicsContext); mPaused = true; } void Context::resume() { + rsAssert(mIsGraphicsContext); mPaused = false; } void Context::setRootScript(Script *s) { + rsAssert(mIsGraphicsContext); mRootScript.set(s); } void Context::setFragmentStore(ProgramFragmentStore *pfs) { + rsAssert(mIsGraphicsContext); if (pfs == NULL) { mFragmentStore.set(mStateFragmentStore.mDefault); } else { @@ -568,6 +580,7 @@ void Context::setFragmentStore(ProgramFragmentStore *pfs) void Context::setFragment(ProgramFragment *pf) { + rsAssert(mIsGraphicsContext); if (pf == NULL) { mFragment.set(mStateFragment.mDefault); } else { @@ -577,6 +590,7 @@ void Context::setFragment(ProgramFragment *pf) void Context::setRaster(ProgramRaster *pr) { + rsAssert(mIsGraphicsContext); if (pr == NULL) { mRaster.set(mStateRaster.mDefault); } else { @@ -586,6 +600,7 @@ void Context::setRaster(ProgramRaster *pr) void Context::setVertex(ProgramVertex *pv) { + rsAssert(mIsGraphicsContext); if (pv == NULL) { mVertex.set(mStateVertex.mDefault); } else { @@ -860,10 +875,19 @@ void rsi_ContextDump(Context *rsc, int32_t bits) } -RsContext rsContextCreate(RsDevice vdev, uint32_t version, bool useDepth) +RsContext rsContextCreate(RsDevice vdev, uint32_t version) +{ + LOGV("rsContextCreate %p", vdev); + Device * dev = static_cast<Device *>(vdev); + Context *rsc = new Context(dev, false, false); + return rsc; +} + +RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, bool useDepth) { + LOGV("rsContextCreateGL %p, %i", vdev, useDepth); Device * dev = static_cast<Device *>(vdev); - Context *rsc = new Context(dev, useDepth); + Context *rsc = new Context(dev, true, useDepth); return rsc; } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 2edd16d..31d8cc8 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -51,7 +51,7 @@ namespace renderscript { class Context { public: - Context(Device *, bool useDepth); + Context(Device *, bool isGraphics, bool useDepth); ~Context(); static pthread_key_t gThreadTLSKey; @@ -201,6 +201,7 @@ protected: uint32_t mWidth; uint32_t mHeight; int32_t mThreadPriority; + bool mIsGraphicsContext; bool mRunning; bool mExit; diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp index d0c0414..6c2002d 100644 --- a/libs/rs/rsVertexArray.cpp +++ b/libs/rs/rsVertexArray.cpp @@ -191,6 +191,9 @@ void VertexArray::setupGL2(const Context *rsc, class VertexArrayState *state, Sh if (sc->isUserVertexProgram()) { slot = sc->vtxAttribSlot(ct); } else { + if (mAttribs[ct].kind == RS_KIND_USER) { + continue; + } slot = sc->vtxAttribSlot(mAttribs[ct].kind); } diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 965b7dd..2d6152e 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -39,7 +39,6 @@ #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> -#include <ui/DisplayInfo.h> #include <pixelflinger/pixelflinger.h> #include <GLES/gl.h> @@ -350,8 +349,8 @@ status_t SurfaceFlinger::readyToRun() mServerCblk->connected |= 1<<dpy; display_cblk_t* dcblk = mServerCblk->displays + dpy; memset(dcblk, 0, sizeof(display_cblk_t)); - dcblk->w = w; - dcblk->h = h; + dcblk->w = plane.getWidth(); + dcblk->h = plane.getHeight(); dcblk->format = f; dcblk->orientation = ISurfaceComposer::eOrientationDefault; dcblk->xdpi = hw.getDpiX(); @@ -621,14 +620,8 @@ void SurfaceFlinger::handleTransactionLocked( const DisplayHardware& hw(plane.displayHardware()); volatile display_cblk_t* dcblk = mServerCblk->displays + dpy; dcblk->orientation = orientation; - if (orientation & eOrientationSwapMask) { - // 90 or 270 degrees orientation - dcblk->w = hw.getHeight(); - dcblk->h = hw.getWidth(); - } else { - dcblk->w = hw.getWidth(); - dcblk->h = hw.getHeight(); - } + dcblk->w = plane.getWidth(); + dcblk->h = plane.getHeight(); mVisibleRegionsDirty = true; mDirtyRegion.set(hw.bounds()); @@ -1795,13 +1788,47 @@ bool GraphicPlane::initialized() const { return mHw ? true : false; } -void GraphicPlane::setDisplayHardware(DisplayHardware *hw) { - mHw = hw; +int GraphicPlane::getWidth() const { + return mWidth; } -void GraphicPlane::setTransform(const Transform& tr) { - mTransform = tr; - mGlobalTransform = mOrientationTransform * mTransform; +int GraphicPlane::getHeight() const { + return mHeight; +} + +void GraphicPlane::setDisplayHardware(DisplayHardware *hw) +{ + mHw = hw; + + // initialize the display orientation transform. + // it's a constant that should come from the display driver. + int displayOrientation = ISurfaceComposer::eOrientationDefault; + char property[PROPERTY_VALUE_MAX]; + if (property_get("ro.sf.hwrotation", property, NULL) > 0) { + //displayOrientation + switch (atoi(property)) { + case 90: + displayOrientation = ISurfaceComposer::eOrientation90; + break; + case 270: + displayOrientation = ISurfaceComposer::eOrientation270; + break; + } + } + + const float w = hw->getWidth(); + const float h = hw->getHeight(); + GraphicPlane::orientationToTransfrom(displayOrientation, w, h, + &mDisplayTransform); + if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) { + mDisplayWidth = h; + mDisplayHeight = w; + } else { + mDisplayWidth = w; + mDisplayHeight = h; + } + + setOrientation(ISurfaceComposer::eOrientationDefault); } status_t GraphicPlane::orientationToTransfrom( @@ -1810,8 +1837,9 @@ status_t GraphicPlane::orientationToTransfrom( float a, b, c, d, x, y; switch (orientation) { case ISurfaceComposer::eOrientationDefault: - a=1; b=0; c=0; d=1; x=0; y=0; - break; + // make sure the default orientation is optimal + tr->reset(); + return NO_ERROR; case ISurfaceComposer::eOrientation90: a=0; b=-1; c=1; d=0; x=w; y=0; break; @@ -1831,20 +1859,16 @@ status_t GraphicPlane::orientationToTransfrom( status_t GraphicPlane::setOrientation(int orientation) { - const DisplayHardware& hw(displayHardware()); - const float w = hw.getWidth(); - const float h = hw.getHeight(); - - if (orientation == ISurfaceComposer::eOrientationDefault) { - // make sure the default orientation is optimal - mOrientationTransform.reset(); - mOrientation = orientation; - mGlobalTransform = mTransform; - return NO_ERROR; - } - // If the rotation can be handled in hardware, this is where // the magic should happen. + + const DisplayHardware& hw(displayHardware()); + const float w = mDisplayWidth; + const float h = mDisplayHeight; + mWidth = int(w); + mHeight = int(h); + + Transform orientationTransform; if (UNLIKELY(orientation == 42)) { float a, b, c, d, x, y; const float r = (3.14159265f / 180.0f) * 42.0f; @@ -1853,14 +1877,18 @@ status_t GraphicPlane::setOrientation(int orientation) a=co; b=-si; c=si; d=co; x = si*(h*0.5f) + (1-co)*(w*0.5f); y =-si*(w*0.5f) + (1-co)*(h*0.5f); - mOrientationTransform.set(a, b, c, d); - mOrientationTransform.set(x, y); + orientationTransform.set(a, b, c, d); + orientationTransform.set(x, y); } else { GraphicPlane::orientationToTransfrom(orientation, w, h, - &mOrientationTransform); + &orientationTransform); + if (orientation & ISurfaceComposer::eOrientationSwapMask) { + mWidth = int(h); + mHeight = int(w); + } } mOrientation = orientation; - mGlobalTransform = mOrientationTransform * mTransform; + mGlobalTransform = mDisplayTransform * orientationTransform; return NO_ERROR; } diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index c0ab73d..2b7820c 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -116,9 +116,10 @@ public: bool initialized() const; void setDisplayHardware(DisplayHardware *); - void setTransform(const Transform& tr); status_t setOrientation(int orientation); int getOrientation() const { return mOrientation; } + int getWidth() const; + int getHeight() const; const DisplayHardware& displayHardware() const; const Transform& transform() const; @@ -129,10 +130,13 @@ private: GraphicPlane operator = (const GraphicPlane&); DisplayHardware* mHw; - Transform mTransform; - Transform mOrientationTransform; Transform mGlobalTransform; + Transform mDisplayTransform; int mOrientation; + float mDisplayWidth; + float mDisplayHeight; + int mWidth; + int mHeight; }; // --------------------------------------------------------------------------- diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp index c4958a0..493b9c1 100644 --- a/libs/ui/CameraParameters.cpp +++ b/libs/ui/CameraParameters.cpp @@ -59,6 +59,8 @@ const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values"; const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length"; const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle"; const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle"; +const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation"; +const char CameraParameters::KEY_SUPPORTED_EXPOSURE_COMPENSATION[] = "exposure-compensation-values"; // Values for white balance settings. const char CameraParameters::WHITE_BALANCE_AUTO[] = "auto"; diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 1fac07c..628cb6b7 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -81,10 +81,6 @@ interface ILocationManager void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime); void clearTestProviderStatus(String provider); - /* for installing external Location Providers */ - void installLocationProvider(String name, ILocationProvider provider); - void installGeocodeProvider(IGeocodeProvider provider); - // for NI support boolean sendNiResponse(int notifId, int userResponse); } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 6d7a23d..9027fc2 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -104,48 +104,6 @@ public class LocationManager { */ public static final String KEY_LOCATION_CHANGED = "location"; - public interface GeocodeProvider { - String getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, List<Address> addrs); - - String getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, List<Address> addrs); - } - - private static final class GeocodeProviderProxy extends IGeocodeProvider.Stub { - private GeocodeProvider mProvider; - - GeocodeProviderProxy(GeocodeProvider provider) { - mProvider = provider; - } - - /** - * This method is overridden to implement the - * {@link Geocoder#getFromLocation(double, double, int)} method. - * Classes implementing this method should not hold a reference to the params parameter. - */ - public String getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, List<Address> addrs) { - return mProvider.getFromLocation(latitude, longitude, maxResults, params, addrs); - } - - /** - * This method is overridden to implement the - * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method. - * Classes implementing this method should not hold a reference to the params parameter. - */ - public String getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, List<Address> addrs) { - return mProvider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, addrs); - } - } - // Map from LocationListeners to their associated ListenerTransport objects private HashMap<LocationListener,ListenerTransport> mListeners = new HashMap<LocationListener,ListenerTransport>(); @@ -1395,75 +1353,6 @@ public class LocationManager { return false; } } - - /** - * Installs a location provider. - * - * @param name of the location provider - * @param provider Binder interface for the location provider - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - * - * {@hide} - */ - public boolean installLocationProvider(String name, ILocationProvider provider) { - try { - mService.installLocationProvider(name, provider); - return true; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in installLocationProvider: ", e); - return false; - } - } - - /** - * Installs a location provider. - * - * @param provider implementation of the location provider - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - */ - public boolean installLocationProvider(LocationProviderImpl provider) { - return installLocationProvider(provider.getName(), provider.getInterface()); - } - - /** - * Installs a geocoder server. - * - * @param provider Binder interface for the geocoder provider - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - */ - public boolean installGeocodeProvider(GeocodeProvider provider) { - try { - mService.installGeocodeProvider(new GeocodeProviderProxy(provider)); - return true; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in setGeocodeProvider: ", e); - return false; - } - } - - /** - * Used by location providers to report new locations. - * - * @param location new Location to report - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - */ - public void reportLocation(Location location) { - try { - mService.reportLocation(location); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in reportLocation: ", e); - } - } /** * Used by NetInitiatedActivity to report user response diff --git a/location/java/android/location/LocationProviderImpl.java b/location/java/android/location/LocationProviderImpl.java deleted file mode 100644 index 7148a02..0000000 --- a/location/java/android/location/LocationProviderImpl.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.location; - -import android.net.NetworkInfo; -import android.os.Bundle; - -/** - * An abstract superclass for location providers that are implemented - * outside of the core android platform. - * A LocationProviderImpl can be installed using the - * {@link LocationManager#installLocationProvider(LocationProviderImpl)} method. - * Installing a location provider requires the - * android.permission.INSTALL_LOCATION_PROVIDER permission. - */ -public abstract class LocationProviderImpl extends LocationProvider { - - private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() { - - public boolean requiresNetwork() { - return LocationProviderImpl.this.requiresNetwork(); - } - - public boolean requiresSatellite() { - return LocationProviderImpl.this.requiresSatellite(); - } - - public boolean requiresCell() { - return LocationProviderImpl.this.requiresCell(); - } - - public boolean hasMonetaryCost() { - return LocationProviderImpl.this.hasMonetaryCost(); - } - - public boolean supportsAltitude() { - return LocationProviderImpl.this.supportsAltitude(); - } - - public boolean supportsSpeed() { - return LocationProviderImpl.this.supportsSpeed(); - } - - public boolean supportsBearing() { - return LocationProviderImpl.this.supportsBearing(); - } - - public int getPowerRequirement() { - return LocationProviderImpl.this.getPowerRequirement(); - } - - public int getAccuracy() { - return LocationProviderImpl.this.getAccuracy(); - } - - public void enable() { - LocationProviderImpl.this.enable(); - } - - public void disable() { - LocationProviderImpl.this.disable(); - } - - public int getStatus(Bundle extras) { - return LocationProviderImpl.this.getStatus(extras); - } - - public long getStatusUpdateTime() { - return LocationProviderImpl.this.getStatusUpdateTime(); - } - - public void enableLocationTracking(boolean enable) { - LocationProviderImpl.this.enableLocationTracking(enable); - } - - public void setMinTime(long minTime) { - LocationProviderImpl.this.setMinTime(minTime); - } - - public void updateNetworkState(int state, NetworkInfo info) { - LocationProviderImpl.this.updateNetworkState(state, info); - } - - public void updateLocation(Location location) { - LocationProviderImpl.this.updateLocation(location); - } - - public boolean sendExtraCommand(String command, Bundle extras) { - return LocationProviderImpl.this.sendExtraCommand(command, extras); - } - - public void addListener(int uid) { - LocationProviderImpl.this.addListener(uid); - } - - public void removeListener(int uid) { - LocationProviderImpl.this.removeListener(uid); - } - }; - - public LocationProviderImpl(String name) { - super(name); - } - - /** - * {@hide} - */ - /* package */ ILocationProvider getInterface() { - return mProvider; - } - - /** - * Enables the location provider - */ - public abstract void enable(); - - /** - * Disables the location provider - */ - public abstract void disable(); - - /** - * Returns a information on the status of this provider. - * {@link #OUT_OF_SERVICE} is returned if the provider is - * out of service, and this is not expected to change in the near - * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if - * the provider is temporarily unavailable but is expected to be - * available shortly; and {@link #AVAILABLE} is returned - * if the provider is currently available. - * - * <p> If extras is non-null, additional status information may be - * added to it in the form of provider-specific key/value pairs. - */ - public abstract int getStatus(Bundle extras); - - /** - * Returns the time at which the status was last updated. It is the - * responsibility of the provider to appropriately set this value using - * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. - * there is a status update that it wishes to broadcast to all its - * listeners. The provider should be careful not to broadcast - * the same status again. - * - * @return time of last status update in millis since last reboot - */ - public abstract long getStatusUpdateTime(); - - /** - * Notifies the location provider that clients are listening for locations. - * Called with enable set to true when the first client is added and - * called with enable set to false when the last client is removed. - * This allows the provider to prepare for receiving locations, - * and to shut down when no clients are remaining. - * - * @param enable true if location tracking should be enabled. - */ - public abstract void enableLocationTracking(boolean enable); - - /** - * Notifies the location provider of the smallest minimum time between updates amongst - * all clients that are listening for locations. This allows the provider to reduce - * the frequency of updates to match the requested frequency. - * - * @param minTime the smallest minTime value over all listeners for this provider. - */ - public abstract void setMinTime(long minTime); - - /** - * Updates the network state for the given provider. This function must - * be overwritten if {@link #requiresNetwork} returns true. The state is - * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE} - * (connected or connecting). - * - * @param state data state - */ - public abstract void updateNetworkState(int state, NetworkInfo info); - - /** - * Informs the provider when a new location has been computed by a different - * location provider. This is intended to be used as aiding data for the - * receiving provider. - * - * @param location new location from other location provider - */ - public abstract void updateLocation(Location location); - - /** - * Implements addditional location provider specific additional commands. - * - * @param command name of the command to send to the provider. - * @param extras optional arguments for the command (or null). - * The provider may optionally fill the extras Bundle with results from the command. - * - * @return true if the command succeeds. - */ - public abstract boolean sendExtraCommand(String command, Bundle extras); - - /** - * Notifies the location provider when a new client is listening for locations. - * - * @param uid user ID of the new client. - */ - public abstract void addListener(int uid); - - /** - * Notifies the location provider when a client is no longer listening for locations. - * - * @param uid user ID of the client no longer listening. - */ - public abstract void removeListener(int uid); -} - diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/java/android/location/provider/GeocodeProvider.java new file mode 100644 index 0000000..86376a7 --- /dev/null +++ b/location/java/android/location/provider/GeocodeProvider.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +import android.os.IBinder; + +import android.location.Address; +import android.location.GeocoderParams; +import android.location.IGeocodeProvider; + +import java.util.List; + +/** + * An abstract superclass for geocode providers that are implemented + * outside of the core android platform. + * Geocode providers can be implemented as services and return the result of + * {@link GeocodeProvider#getBinder()} in its getBinder() method. + * + * @hide + */ +public abstract class GeocodeProvider { + + private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() { + public String getFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + return GeocodeProvider.this.onGetFromLocation(latitude, longitude, maxResults, + params, addrs); + } + + public String getFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + return GeocodeProvider.this.onGetFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); + } + }; + + /** + * This method is overridden to implement the + * {@link Geocoder#getFromLocation(double, double, int)} method. + * Classes implementing this method should not hold a reference to the params parameter. + */ + public abstract String onGetFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs); + + /** + * This method is overridden to implement the + * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method. + * Classes implementing this method should not hold a reference to the params parameter. + */ + public abstract String onGetFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs); + + /** + * Returns the Binder interface for the geocode provider. + * This is intended to be used for the onBind() method of + * a service that implements a geocoder service. + * + * @return the IBinder instance for the provider + */ + public IBinder getBinder() { + return mProvider; + } +} + diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java new file mode 100644 index 0000000..0d028c0 --- /dev/null +++ b/location/java/android/location/provider/LocationProvider.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +import android.content.Context; +import android.net.NetworkInfo; +import android.location.ILocationManager; +import android.location.ILocationProvider; +import android.location.Location; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +/** + * An abstract superclass for location providers that are implemented + * outside of the core android platform. + * Location providers can be implemented as services and return the result of + * {@link LocationProvider#getBinder()} in its getBinder() method. + * + * @hide + */ +public abstract class LocationProvider { + + private static final String TAG = "LocationProvider"; + + private ILocationManager mLocationManager; + + private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() { + + public boolean requiresNetwork() { + return LocationProvider.this.onRequiresNetwork(); + } + + public boolean requiresSatellite() { + return LocationProvider.this.onRequiresSatellite(); + } + + public boolean requiresCell() { + return LocationProvider.this.onRequiresCell(); + } + + public boolean hasMonetaryCost() { + return LocationProvider.this.onHasMonetaryCost(); + } + + public boolean supportsAltitude() { + return LocationProvider.this.onSupportsAltitude(); + } + + public boolean supportsSpeed() { + return LocationProvider.this.onSupportsSpeed(); + } + + public boolean supportsBearing() { + return LocationProvider.this.onSupportsBearing(); + } + + public int getPowerRequirement() { + return LocationProvider.this.onGetPowerRequirement(); + } + + public int getAccuracy() { + return LocationProvider.this.onGetAccuracy(); + } + + public void enable() { + LocationProvider.this.onEnable(); + } + + public void disable() { + LocationProvider.this.onDisable(); + } + + public int getStatus(Bundle extras) { + return LocationProvider.this.onGetStatus(extras); + } + + public long getStatusUpdateTime() { + return LocationProvider.this.onGetStatusUpdateTime(); + } + + public void enableLocationTracking(boolean enable) { + LocationProvider.this.onEnableLocationTracking(enable); + } + + public void setMinTime(long minTime) { + LocationProvider.this.onSetMinTime(minTime); + } + + public void updateNetworkState(int state, NetworkInfo info) { + LocationProvider.this.onUpdateNetworkState(state, info); + } + + public void updateLocation(Location location) { + LocationProvider.this.onUpdateLocation(location); + } + + public boolean sendExtraCommand(String command, Bundle extras) { + return LocationProvider.this.onSendExtraCommand(command, extras); + } + + public void addListener(int uid) { + LocationProvider.this.onAddListener(uid); + } + + public void removeListener(int uid) { + LocationProvider.this.onRemoveListener(uid); + } + }; + + public LocationProvider() { + IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE); + mLocationManager = ILocationManager.Stub.asInterface(b); + } + + /** + * {@hide} + */ + /* package */ ILocationProvider getInterface() { + return mProvider; + } + + /** + * Returns the Binder interface for the location provider. + * This is intended to be used for the onBind() method of + * a service that implements a location provider service. + * + * @return the IBinder instance for the provider + */ + public IBinder getBinder() { + return mProvider; + } + + /** + * Used by the location provider to report new locations. + * + * @param location new Location to report + * + * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. + */ + public void reportLocation(Location location) { + try { + mLocationManager.reportLocation(location); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in reportLocation: ", e); + } + } + + /** + * Returns true if the provider requires access to a + * data network (e.g., the Internet), false otherwise. + */ + public abstract boolean onRequiresNetwork(); + + /** + * Returns true if the provider requires access to a + * satellite-based positioning system (e.g., GPS), false + * otherwise. + */ + public abstract boolean onRequiresSatellite(); + + /** + * Returns true if the provider requires access to an appropriate + * cellular network (e.g., to make use of cell tower IDs), false + * otherwise. + */ + public abstract boolean onRequiresCell(); + + /** + * Returns true if the use of this provider may result in a + * monetary charge to the user, false if use is free. It is up to + * each provider to give accurate information. + */ + public abstract boolean onHasMonetaryCost(); + + /** + * Returns true if the provider is able to provide altitude + * information, false otherwise. A provider that reports altitude + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsAltitude(); + + /** + * Returns true if the provider is able to provide speed + * information, false otherwise. A provider that reports speed + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsSpeed(); + + /** + * Returns true if the provider is able to provide bearing + * information, false otherwise. A provider that reports bearing + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsBearing(); + + /** + * Returns the power requirement for this provider. + * + * @return the power requirement for this provider, as one of the + * constants Criteria.POWER_REQUIREMENT_*. + */ + public abstract int onGetPowerRequirement(); + + /** + * Returns a constant describing horizontal accuracy of this provider. + * If the provider returns finer grain or exact location, + * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the + * location is only approximate then {@link Criteria#ACCURACY_COARSE} + * is returned. + */ + public abstract int onGetAccuracy(); + + /** + * Enables the location provider + */ + public abstract void onEnable(); + + /** + * Disables the location provider + */ + public abstract void onDisable(); + + /** + * Returns a information on the status of this provider. + * {@link #OUT_OF_SERVICE} is returned if the provider is + * out of service, and this is not expected to change in the near + * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if + * the provider is temporarily unavailable but is expected to be + * available shortly; and {@link #AVAILABLE} is returned + * if the provider is currently available. + * + * <p> If extras is non-null, additional status information may be + * added to it in the form of provider-specific key/value pairs. + */ + public abstract int onGetStatus(Bundle extras); + + /** + * Returns the time at which the status was last updated. It is the + * responsibility of the provider to appropriately set this value using + * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. + * there is a status update that it wishes to broadcast to all its + * listeners. The provider should be careful not to broadcast + * the same status again. + * + * @return time of last status update in millis since last reboot + */ + public abstract long onGetStatusUpdateTime(); + + /** + * Notifies the location provider that clients are listening for locations. + * Called with enable set to true when the first client is added and + * called with enable set to false when the last client is removed. + * This allows the provider to prepare for receiving locations, + * and to shut down when no clients are remaining. + * + * @param enable true if location tracking should be enabled. + */ + public abstract void onEnableLocationTracking(boolean enable); + + /** + * Notifies the location provider of the smallest minimum time between updates amongst + * all clients that are listening for locations. This allows the provider to reduce + * the frequency of updates to match the requested frequency. + * + * @param minTime the smallest minTime value over all listeners for this provider. + */ + public abstract void onSetMinTime(long minTime); + + /** + * Updates the network state for the given provider. This function must + * be overwritten if {@link #requiresNetwork} returns true. The state is + * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE} + * (connected or connecting). + * + * @param state data state + */ + public abstract void onUpdateNetworkState(int state, NetworkInfo info); + + /** + * Informs the provider when a new location has been computed by a different + * location provider. This is intended to be used as aiding data for the + * receiving provider. + * + * @param location new location from other location provider + */ + public abstract void onUpdateLocation(Location location); + + /** + * Implements addditional location provider specific additional commands. + * + * @param command name of the command to send to the provider. + * @param extras optional arguments for the command (or null). + * The provider may optionally fill the extras Bundle with results from the command. + * + * @return true if the command succeeds. + */ + public abstract boolean onSendExtraCommand(String command, Bundle extras); + + /** + * Notifies the location provider when a new client is listening for locations. + * + * @param uid user ID of the new client. + */ + public abstract void onAddListener(int uid); + + /** + * Notifies the location provider when a client is no longer listening for locations. + * + * @param uid user ID of the client no longer listening. + */ + public abstract void onRemoveListener(int uid); +} + diff --git a/location/java/com/android/internal/location/GeocoderProxy.java b/location/java/com/android/internal/location/GeocoderProxy.java new file mode 100644 index 0000000..b06297b --- /dev/null +++ b/location/java/com/android/internal/location/GeocoderProxy.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.location.Address; +import android.location.GeocoderParams; +import android.location.IGeocodeProvider; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.List; + +/** + * A class for proxying IGeocodeProvider implementations. + * + * {@hide} + */ +public class GeocoderProxy { + + private static final String TAG = "GeocoderProxy"; + + private final Context mContext; + private final Intent mIntent; + private final Connection mServiceConnection = new Connection(); + private IGeocodeProvider mProvider; + + public GeocoderProxy(Context context, String serviceName) { + mContext = context; + mIntent = new Intent(serviceName); + mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + + private class Connection implements ServiceConnection { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected " + className); + synchronized (this) { + mProvider = IGeocodeProvider.Stub.asInterface(service); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected " + className); + synchronized (this) { + mProvider = null; + } + } + } + + public String getFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + IGeocodeProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getFromLocation(latitude, longitude, maxResults, + params, addrs); + } catch (RemoteException e) { + Log.e(TAG, "getFromLocation failed", e); + } + } + return "Service not Available"; + } + + public String getFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + IGeocodeProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); + } catch (RemoteException e) { + Log.e(TAG, "getFromLocationName failed", e); + } + } + return "Service not Available"; + } +} diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java index 2e0be89..361104f 100644 --- a/location/java/com/android/internal/location/LocationProviderProxy.java +++ b/location/java/com/android/internal/location/LocationProviderProxy.java @@ -16,155 +16,229 @@ package com.android.internal.location; -import android.location.Address; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.location.ILocationProvider; import android.location.Location; -import android.location.LocationManager; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; -import java.util.List; - /** - * A class for proxying remote ILocationProvider implementations. + * A class for proxying ILocationProvider implementations. * * {@hide} */ -public class LocationProviderProxy implements IBinder.DeathRecipient { +public class LocationProviderProxy { private static final String TAG = "LocationProviderProxy"; + private final Context mContext; private final String mName; - private final ILocationProvider mProvider; + private ILocationProvider mProvider; + private Intent mIntent; + private Handler mHandler; + private final Connection mServiceConnection = new Connection(); + + // cached values set by the location manager private boolean mLocationTracking = false; private boolean mEnabled = false; - private long mMinTime = 0; - private boolean mDead; + private long mMinTime = -1; + private int mNetworkState; + private NetworkInfo mNetworkInfo; - public LocationProviderProxy(String name, ILocationProvider provider) { + // for caching requiresNetwork, requiresSatellite, etc. + private DummyLocationProvider mCachedAttributes; + + // constructor for proxying built-in location providers + public LocationProviderProxy(Context context, String name, ILocationProvider provider) { + mContext = context; mName = name; mProvider = provider; - try { - provider.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - Log.e(TAG, "linkToDeath failed", e); - mDead = true; - } } - public void unlinkProvider() { - if (mProvider != null) { - mProvider.asBinder().unlinkToDeath(this, 0); + // constructor for proxying location providers implemented in a separate service + public LocationProviderProxy(Context context, String name, String serviceName, + Handler handler) { + mContext = context; + mName = name; + mIntent = new Intent(serviceName); + mHandler = handler; + mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + + private class Connection implements ServiceConnection { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className); + synchronized (this) { + mProvider = ILocationProvider.Stub.asInterface(service); + if (mProvider != null) { + mHandler.post(mServiceConnectedTask); + } + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className); + synchronized (this) { + mProvider = null; + } } } + private Runnable mServiceConnectedTask = new Runnable() { + public void run() { + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + if (provider == null) { + return; + } + } + + if (mCachedAttributes == null) { + try { + mCachedAttributes = new DummyLocationProvider(mName); + mCachedAttributes.setRequiresNetwork(provider.requiresNetwork()); + mCachedAttributes.setRequiresSatellite(provider.requiresSatellite()); + mCachedAttributes.setRequiresCell(provider.requiresCell()); + mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost()); + mCachedAttributes.setSupportsAltitude(provider.supportsAltitude()); + mCachedAttributes.setSupportsSpeed(provider.supportsSpeed()); + mCachedAttributes.setSupportsBearing(provider.supportsBearing()); + mCachedAttributes.setPowerRequirement(provider.getPowerRequirement()); + mCachedAttributes.setAccuracy(provider.getAccuracy()); + } catch (RemoteException e) { + mCachedAttributes = null; + } + } + + // resend previous values from the location manager if the service has restarted + try { + if (mEnabled) { + provider.enable(); + } + if (mLocationTracking) { + provider.enableLocationTracking(true); + } + if (mMinTime >= 0) { + provider.setMinTime(mMinTime); + } + if (mNetworkInfo != null) { + provider.updateNetworkState(mNetworkState, mNetworkInfo); + } + } catch (RemoteException e) { + } + } + }; + public String getName() { return mName; } - public boolean isDead() { - return mDead; - } - public boolean requiresNetwork() { - try { - return mProvider.requiresNetwork(); - } catch (RemoteException e) { - Log.e(TAG, "requiresNetwork failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresNetwork(); + } else { return false; } } public boolean requiresSatellite() { - try { - return mProvider.requiresSatellite(); - } catch (RemoteException e) { - Log.e(TAG, "requiresSatellite failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresSatellite(); + } else { return false; } } public boolean requiresCell() { - try { - return mProvider.requiresCell(); - } catch (RemoteException e) { - Log.e(TAG, "requiresCell failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresCell(); + } else { return false; } } public boolean hasMonetaryCost() { - try { - return mProvider.hasMonetaryCost(); - } catch (RemoteException e) { - Log.e(TAG, "hasMonetaryCost failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.hasMonetaryCost(); + } else { return false; } } public boolean supportsAltitude() { - try { - return mProvider.supportsAltitude(); - } catch (RemoteException e) { - Log.e(TAG, "supportsAltitude failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsAltitude(); + } else { return false; } } public boolean supportsSpeed() { - try { - return mProvider.supportsSpeed(); - } catch (RemoteException e) { - Log.e(TAG, "supportsSpeed failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsSpeed(); + } else { return false; } } public boolean supportsBearing() { - try { - return mProvider.supportsBearing(); - } catch (RemoteException e) { - Log.e(TAG, "supportsBearing failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsBearing(); + } else { return false; } } public int getPowerRequirement() { - try { - return mProvider.getPowerRequirement(); - } catch (RemoteException e) { - Log.e(TAG, "getPowerRequirement failed", e); - return 0; + if (mCachedAttributes != null) { + return mCachedAttributes.getPowerRequirement(); + } else { + return -1; } } public int getAccuracy() { - try { - return mProvider.getAccuracy(); - } catch (RemoteException e) { - Log.e(TAG, "getAccuracy failed", e); - return 0; + if (mCachedAttributes != null) { + return mCachedAttributes.getAccuracy(); + } else { + return -1; } } public void enable() { - try { - mProvider.enable(); - mEnabled = true; - } catch (RemoteException e) { - Log.e(TAG, "enable failed", e); + mEnabled = true; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.enable(); + } catch (RemoteException e) { + } } } public void disable() { - try { - mProvider.disable(); - mEnabled = false; - } catch (RemoteException e) { - Log.e(TAG, "disable failed", e); + mEnabled = false; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.disable(); + } catch (RemoteException e) { + } } } @@ -173,22 +247,32 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { } public int getStatus(Bundle extras) { - try { - return mProvider.getStatus(extras); - } catch (RemoteException e) { - Log.e(TAG, "getStatus failed", e); - return 0; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; } + if (provider != null) { + try { + return provider.getStatus(extras); + } catch (RemoteException e) { + } + } + return 0; } public long getStatusUpdateTime() { - try { - return mProvider.getStatusUpdateTime(); - } catch (RemoteException e) { - Log.e(TAG, "getStatusUpdateTime failed", e); - return 0; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; } - } + if (provider != null) { + try { + return provider.getStatusUpdateTime(); + } catch (RemoteException e) { + } + } + return 0; + } public boolean isLocationTracking() { return mLocationTracking; @@ -196,10 +280,18 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { public void enableLocationTracking(boolean enable) { mLocationTracking = enable; - try { - mProvider.enableLocationTracking(enable); - } catch (RemoteException e) { - Log.e(TAG, "enableLocationTracking failed", e); + if (!enable) { + mMinTime = -1; + } + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.enableLocationTracking(enable); + } catch (RemoteException e) { + } } } @@ -208,58 +300,84 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { } public void setMinTime(long minTime) { - mMinTime = minTime; - try { - mProvider.setMinTime(minTime); - } catch (RemoteException e) { - Log.e(TAG, "setMinTime failed", e); + mMinTime = minTime; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.setMinTime(minTime); + } catch (RemoteException e) { + } } } public void updateNetworkState(int state, NetworkInfo info) { - try { - mProvider.updateNetworkState(state, info); - } catch (RemoteException e) { - Log.e(TAG, "updateNetworkState failed", e); + mNetworkState = state; + mNetworkInfo = info; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.updateNetworkState(state, info); + } catch (RemoteException e) { + } } } public void updateLocation(Location location) { - try { - mProvider.updateLocation(location); - } catch (RemoteException e) { - Log.e(TAG, "updateLocation failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.updateLocation(location); + } catch (RemoteException e) { + } } } public boolean sendExtraCommand(String command, Bundle extras) { - try { - return mProvider.sendExtraCommand(command, extras); - } catch (RemoteException e) { - Log.e(TAG, "sendExtraCommand failed", e); - return false; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; } + if (provider != null) { + try { + provider.sendExtraCommand(command, extras); + } catch (RemoteException e) { + } + } + return false; } public void addListener(int uid) { - try { - mProvider.addListener(uid); - } catch (RemoteException e) { - Log.e(TAG, "addListener failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.addListener(uid); + } catch (RemoteException e) { + } } } public void removeListener(int uid) { - try { - mProvider.removeListener(uid); - } catch (RemoteException e) { - Log.e(TAG, "removeListener failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.removeListener(uid); + } catch (RemoteException e) { + } } - } - - public void binderDied() { - Log.w(TAG, "Location Provider " + mName + " died"); - mDead = true; - mProvider.asBinder().unlinkToDeath(this, 0); } } diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java index d2596b8..65b67a1 100644 --- a/media/java/android/media/MediaScannerConnection.java +++ b/media/java/android/media/MediaScannerConnection.java @@ -57,17 +57,32 @@ public class MediaScannerConnection implements ServiceConnection { }; /** + * Interface for notifying clients of the result of scanning a + * requested media file. + */ + public interface ScanResultListener { + /** + * Called to notify the client when the media scanner has finished + * scanning a file. + * @param path the path to the file that has been scanned. + * @param uri the Uri for the file if the scanning operation succeeded + * and the file was added to the media database, or null if scanning failed. + */ + public void onScanCompleted(String path, Uri uri); + } + + /** * An interface for notifying clients of MediaScannerConnection * when a connection to the MediaScanner service has been established * and when the scanning of a file has completed. */ - public interface MediaScannerConnectionClient { + public interface MediaScannerConnectionClient extends ScanResultListener { /** * Called to notify the client when a connection to the * MediaScanner service has been established. */ public void onMediaScannerConnected(); - + /** * Called to notify the client when the media scanner has finished * scanning a file. @@ -136,11 +151,12 @@ public class MediaScannerConnection implements ServiceConnection { /** * Requests the media scanner to scan a file. + * Success or failure of the scanning operation cannot be determined until + * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called. + * * @param path the path to the file to be scanned. * @param mimeType an optional mimeType for the file. * If mimeType is null, then the mimeType will be inferred from the file extension. - * Success or failure of the scanning operation cannot be determined until - * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called. */ public void scanFile(String path, String mimeType) { synchronized (this) { @@ -159,7 +175,67 @@ public class MediaScannerConnection implements ServiceConnection { } } } - + + static class ClientProxy implements MediaScannerConnectionClient { + final String[] mPaths; + final String[] mMimeTypes; + final ScanResultListener mClient; + MediaScannerConnection mConnection; + int mNextPath; + + ClientProxy(String[] paths, String[] mimeTypes, ScanResultListener client) { + mPaths = paths; + mMimeTypes = mimeTypes; + mClient = client; + } + + public void onMediaScannerConnected() { + scanNextPath(); + } + + public void onScanCompleted(String path, Uri uri) { + if (mClient != null) { + mClient.onScanCompleted(path, uri); + } + scanNextPath(); + } + + void scanNextPath() { + if (mNextPath >= mPaths.length) { + mConnection.disconnect(); + return; + } + String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null; + mConnection.scanFile(mPaths[mNextPath], mimeType); + mNextPath++; + } + } + + /** + * Convenience for constructing a {@link MediaScannerConnection}, calling + * {@link #connect} on it, and calling {@link #scanFile} with the given + * <var>path</var> and <var>mimeType</var> when the connection is + * established. + * @param context The caller's Context, required for establishing a connection to + * the media scanner service. + * Success or failure of the scanning operation cannot be determined until + * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called. + * @param paths Array of paths to be scanned. + * @param mimeTypes Optional array of MIME types for each path. + * If mimeType is null, then the mimeType will be inferred from the file extension. + * @param callback Optional callback through which you can receive the + * scanned URI and MIME type; If null, the file will be scanned but + * you will not get a result back. + * @see scanFile(String, String) + */ + public static void scanFile(Context context, String[] paths, String[] mimeTypes, + ScanResultListener callback) { + ClientProxy client = new ClientProxy(paths, mimeTypes, callback); + MediaScannerConnection connection = new MediaScannerConnection(context, client); + client.mConnection = connection; + connection.connect(); + } + /** * Part of the ServiceConnection interface. Do not call. */ diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index d51ab30..12872bc 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -54,12 +54,20 @@ ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libdl endif -LOCAL_C_INCLUDES := external/tremor/Tremor \ +LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ $(TOP)/frameworks/base/media/libstagefright/include +ifeq ($(TARGET_ARCH),arm) + LOCAL_C_INCLUDES += \ + $(TOP)/external/tremolo/Tremolo +else + LOCAL_C_INCLUDES += \ + $(TOP)/external/tremor/Tremor +endif + LOCAL_MODULE:= libmediaplayerservice include $(BUILD_SHARED_LIBRARY) diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index f42d55b..1bfcf65 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -49,30 +49,11 @@ status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) { } status_t StagefrightPlayer::prepare() { - LOGV("prepare"); - - int32_t width, height; - if (mPlayer->getVideoDimensions(&width, &height) != OK) { - width = height = 0; - } - - sendEvent(MEDIA_SET_VIDEO_SIZE, width, height); - - return OK; + return mPlayer->prepare(); } status_t StagefrightPlayer::prepareAsync() { - LOGV("prepareAsync"); - - status_t err = prepare(); - - if (err != OK) { - return err; - } - - sendEvent(MEDIA_PREPARED); - - return OK; + return mPlayer->prepareAsync(); } status_t StagefrightPlayer::start() { diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index b25ce67..58eb12e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -48,8 +48,15 @@ endif LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ - $(TOP)/external/opencore/android \ + $(TOP)/external/opencore/android + +ifeq ($(TARGET_ARCH),arm) + LOCAL_C_INCLUDES += \ + $(TOP)/external/tremolo/Tremolo +else + LOCAL_C_INCLUDES += \ $(TOP)/external/tremor/Tremor +endif LOCAL_SHARED_LIBRARIES := \ libbinder \ diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index e7351dc..4926920 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -229,6 +229,8 @@ void AudioPlayer::fillBuffer(void *data, size_t size) { mInputBuffer->release(); mInputBuffer = NULL; } + + mSeeking = false; } } @@ -240,8 +242,6 @@ void AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); - mSeeking = false; - if (err != OK) { mReachedEOS = true; memset((char *)data + size_done, 0, size_remaining); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 2b403f8..a13b242 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -37,21 +37,23 @@ namespace android { struct AwesomeEvent : public TimedEventQueue::Event { - AwesomeEvent(AwesomePlayer *player, int32_t code) + AwesomeEvent( + AwesomePlayer *player, + void (AwesomePlayer::*method)()) : mPlayer(player), - mCode(code) { + mMethod(method) { } protected: virtual ~AwesomeEvent() {} virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { - mPlayer->onEvent(mCode); + (mPlayer->*mMethod)(); } private: AwesomePlayer *mPlayer; - int32_t mCode; + void (AwesomePlayer::*mMethod)(); AwesomeEvent(const AwesomeEvent &); AwesomeEvent &operator=(const AwesomeEvent &); @@ -109,19 +111,23 @@ private: AwesomePlayer::AwesomePlayer() : mTimeSource(NULL), mAudioPlayer(NULL), + mFlags(0), mLastVideoBuffer(NULL), mVideoBuffer(NULL) { CHECK_EQ(mClient.connect(), OK); DataSource::RegisterDefaultSniffers(); - mVideoEvent = new AwesomeEvent(this, 0); + mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent); mVideoEventPending = false; - mStreamDoneEvent = new AwesomeEvent(this, 1); + mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone); mStreamDoneEventPending = false; - mBufferingEvent = new AwesomeEvent(this, 2); + mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate); mBufferingEventPending = false; - mCheckAudioStatusEvent = new AwesomeEvent(this, 3); + + mCheckAudioStatusEvent = new AwesomeEvent( + this, &AwesomePlayer::onCheckAudioStatus); + mAudioStatusEventPending = false; mQueue.start(); @@ -162,23 +168,17 @@ status_t AwesomePlayer::setDataSource( reset_l(); - sp<DataSource> dataSource = DataSource::CreateFromURI(uri, headers); + mUri = uri; - if (dataSource == NULL) { - return UNKNOWN_ERROR; + if (headers) { + mUriHeaders = *headers; } - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); - - if (extractor == NULL) { - return UNKNOWN_ERROR; - } - - if (dataSource->flags() & DataSource::kWantsPrefetching) { - mPrefetcher = new Prefetcher; - } + // The actual work will be done during preparation in the call to + // ::finishSetDataSource_l to avoid blocking the calling thread in + // setDataSource for any significant time. - return setDataSource_l(extractor); + return OK; } status_t AwesomePlayer::setDataSource( @@ -237,6 +237,10 @@ void AwesomePlayer::reset() { } void AwesomePlayer::reset_l() { + while (mFlags & PREPARING) { + mPreparedCondition.wait(mLock); + } + cancelPlayerEvents(); mVideoRenderer.clear(); @@ -285,14 +289,17 @@ void AwesomePlayer::reset_l() { mSeekTimeUs = 0; mPrefetcher.clear(); + + mUri.setTo(""); + mUriHeaders.clear(); } -void AwesomePlayer::notifyListener_l(int msg, int ext1) { +void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { if (mListener != NULL) { sp<MediaPlayerBase> listener = mListener.promote(); if (listener != NULL) { - listener->sendEvent(msg, ext1); + listener->sendEvent(msg, ext1, ext2); } } } @@ -345,6 +352,14 @@ status_t AwesomePlayer::play() { return OK; } + if (!(mFlags & PREPARED)) { + status_t err = prepare_l(); + + if (err != OK) { + return err; + } + } + mFlags |= PLAYING; mFlags |= FIRST_FRAME; @@ -623,18 +638,7 @@ status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) { return mVideoSource != NULL ? OK : UNKNOWN_ERROR; } -void AwesomePlayer::onEvent(int32_t code) { - if (code == 1) { - onStreamDone(); - return; - } else if (code == 2) { - onBufferingUpdate(); - return; - } else if (code == 3) { - onCheckAudioStatus(); - return; - } - +void AwesomePlayer::onVideoEvent() { Mutex::Autolock autoLock(mLock); mVideoEventPending = false; @@ -731,7 +735,7 @@ void AwesomePlayer::onEvent(int32_t code) { if (latenessUs > 40000) { // We're more than 40ms late. - LOGI("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); + LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); mVideoBuffer->release(); mVideoBuffer = NULL; @@ -819,5 +823,131 @@ void AwesomePlayer::onCheckAudioStatus() { postCheckAudioStatusEvent_l(); } +status_t AwesomePlayer::prepare() { + Mutex::Autolock autoLock(mLock); + return prepare_l(); +} + +status_t AwesomePlayer::prepare_l() { + if (mFlags & PREPARED) { + return OK; + } + + if (mFlags & PREPARING) { + return UNKNOWN_ERROR; + } + + mIsAsyncPrepare = false; + status_t err = prepareAsync_l(); + + if (err != OK) { + return err; + } + + while (mFlags & PREPARING) { + mPreparedCondition.wait(mLock); + } + + return mPrepareResult; +} + +status_t AwesomePlayer::prepareAsync() { + Mutex::Autolock autoLock(mLock); + + if (mFlags & PREPARING) { + return UNKNOWN_ERROR; // async prepare already pending + } + + mIsAsyncPrepare = true; + return prepareAsync_l(); +} + +status_t AwesomePlayer::prepareAsync_l() { + if (mFlags & PREPARING) { + return UNKNOWN_ERROR; // async prepare already pending + } + + mFlags |= PREPARING; + mAsyncPrepareEvent = new AwesomeEvent( + this, &AwesomePlayer::onPrepareAsyncEvent); + + mQueue.postEvent(mAsyncPrepareEvent); + + return OK; +} + +status_t AwesomePlayer::finishSetDataSource_l() { + sp<DataSource> dataSource = + DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + + if (dataSource == NULL) { + return UNKNOWN_ERROR; + } + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + + if (extractor == NULL) { + return UNKNOWN_ERROR; + } + + if (dataSource->flags() & DataSource::kWantsPrefetching) { + mPrefetcher = new Prefetcher; + } + + return setDataSource_l(extractor); +} + +void AwesomePlayer::onPrepareAsyncEvent() { + { + Mutex::Autolock autoLock(mLock); + + if (mUri.size() > 0) { + status_t err = finishSetDataSource_l(); + + if (err != OK) { + if (mIsAsyncPrepare) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + } + + mPrepareResult = err; + mFlags &= ~PREPARING; + mAsyncPrepareEvent = NULL; + mPreparedCondition.broadcast(); + + return; + } + } + } + + sp<Prefetcher> prefetcher; + + { + Mutex::Autolock autoLock(mLock); + prefetcher = mPrefetcher; + } + + if (prefetcher != NULL) { + prefetcher->prepare(); + } + + Mutex::Autolock autoLock(mLock); + + if (mIsAsyncPrepare) { + if (mVideoWidth < 0 || mVideoHeight < 0) { + notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0); + } else { + notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight); + } + + notifyListener_l(MEDIA_PREPARED); + } + + mPrepareResult = OK; + mFlags &= ~PREPARING; + mFlags |= PREPARED; + mAsyncPrepareEvent = NULL; + mPreparedCondition.broadcast(); +} + } // namespace android diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index accd94d..79b7674 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -737,6 +737,7 @@ sp<MetaData> MP3Extractor::getMetaData() { { kKeyYear, "TYE", "TYER" }, { kKeyAuthor, "TXT", "TEXT" }, { kKeyCDTrackNumber, "TRK", "TRCK" }, + { kKeyDiscNumber, "TPA", "TPOS" }, }; static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 6274a6c..4458006 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1070,6 +1070,11 @@ status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) { metadataKey = kKeyGenre; break; } + case FOURCC(0xa9, 'g', 'e', 'n'): + { + metadataKey = kKeyGenre; + break; + } case FOURCC('t', 'r', 'k', 'n'): { if (size == 16 && flags == 0) { @@ -1077,11 +1082,22 @@ status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) { sprintf(tmp, "%d/%d", (int)buffer[size - 5], (int)buffer[size - 3]); - printf("track: %s\n", tmp); mFileMetaData->setCString(kKeyCDTrackNumber, tmp); } break; } + case FOURCC('d', 'i', 's', 'k'): + { + if (size == 14 && flags == 0) { + char tmp[16]; + sprintf(tmp, "%d/%d", + (int)buffer[size - 3], (int)buffer[size - 1]); + + mFileMetaData->setCString(kKeyDiscNumber, tmp); + } + break; + } + default: break; } @@ -1093,11 +1109,25 @@ status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) { buffer + 8, size - 8); } else if (metadataKey == kKeyGenre) { if (flags == 0) { - // uint8_t + // uint8_t genre code, iTunes genre codes are + // the standard id3 codes, except they start + // at 1 instead of 0 (e.g. Pop is 14, not 13) + // We use standard id3 numbering, so subtract 1. + int genrecode = (int)buffer[size - 1]; + genrecode--; + if (genrecode < 0) { + genrecode = 255; // reserved for 'unknown genre' + } char genre[10]; - sprintf(genre, "%d", (int)buffer[size - 1]); + sprintf(genre, "%d", genrecode); mFileMetaData->setCString(metadataKey, genre); + } else if (flags == 1) { + // custom genre string + buffer[size] = '\0'; + + mFileMetaData->setCString( + metadataKey, (const char *)buffer + 8); } } else { buffer[size] = '\0'; @@ -1198,7 +1228,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate)); if (prevSampleRate != sampleRate) { - LOGW("mpeg4 audio sample rate different from previous setting. " + LOGV("mpeg4 audio sample rate different from previous setting. " "was: %d, now: %d", prevSampleRate, sampleRate); } @@ -1208,7 +1238,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount)); if (prevChannelCount != numChannels) { - LOGW("mpeg4 audio channel count different from previous setting. " + LOGV("mpeg4 audio channel count different from previous setting. " "was: %d, now: %d", prevChannelCount, numChannels); } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 0355a82..c6c6f21 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -139,6 +139,7 @@ static const CodecInfo kEncoderInfo[] = { #define CODEC_LOGI(x, ...) LOGI("[%s] "x, mComponentName, ##__VA_ARGS__) #define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__) +#define CODEC_LOGE(x, ...) LOGE("[%s] "x, mComponentName, ##__VA_ARGS__) struct OMXCodecObserver : public BnOMXObserver { OMXCodecObserver() { @@ -1284,7 +1285,8 @@ void OMXCodec::on_message(const omx_message &msg) { CHECK_EQ(err, OK); buffers->removeAt(i); - } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) { + } else if (mState != ERROR + && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) { CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED); drainInputBuffer(&buffers->editItemAt(i)); } @@ -1930,10 +1932,17 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { srcLength = srcBuffer->range_length(); if (info->mSize < srcLength) { - LOGE("info->mSize = %d, srcLength = %d", + CODEC_LOGE( + "Codec's input buffers are too small to accomodate " + "buffer read from source (info->mSize = %d, srcLength = %d)", info->mSize, srcLength); + + srcBuffer->release(); + srcBuffer = NULL; + + setState(ERROR); + return; } - CHECK(info->mSize >= srcLength); memcpy(info->mData, (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(), srcLength); @@ -2250,7 +2259,7 @@ status_t OMXCodec::start(MetaData *) { } status_t OMXCodec::stop() { - CODEC_LOGV("stop"); + CODEC_LOGV("stop mState=%d", mState); Mutex::Autolock autoLock(mLock); @@ -2309,6 +2318,8 @@ status_t OMXCodec::stop() { mSource->stop(); + CODEC_LOGV("stopped"); + return OK; } diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp index 862998a..835e167 100644 --- a/media/libstagefright/Prefetcher.cpp +++ b/media/libstagefright/Prefetcher.cpp @@ -171,7 +171,7 @@ void Prefetcher::threadFunc() { } } -int64_t Prefetcher::getCachedDurationUs() { +int64_t Prefetcher::getCachedDurationUs(bool *noMoreData) { Mutex::Autolock autoLock(mLock); int64_t minCacheDurationUs = -1; @@ -197,9 +197,25 @@ int64_t Prefetcher::getCachedDurationUs() { } } + if (noMoreData) { + *noMoreData = minCacheDurationUs < 0; + } + return minCacheDurationUs < 0 ? 0 : minCacheDurationUs; } +status_t Prefetcher::prepare() { + // Buffer about 2 secs worth of data on prepare. + + int64_t duration; + bool noMoreData; + do { + duration = getCachedDurationUs(&noMoreData); + } while (!noMoreData && duration < 2000000); + + return OK; +} + //////////////////////////////////////////////////////////////////////////////// PrefetchedSource::PrefetchedSource( @@ -232,15 +248,6 @@ status_t PrefetchedSource::start(MetaData *params) { mStarted = true; - for (;;) { - // Buffer 2 secs on startup. - if (mReachedEOS || mCacheDurationUs > 2000000) { - break; - } - - mCondition.wait(mLock); - } - return OK; } diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 3451383..22f701c 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -191,6 +191,7 @@ status_t StagefrightMediaScanner::processFile( }; static const KeyMap kKeyMap[] = { { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, + { "discnumber", METADATA_KEY_DISC_NUMBER }, { "album", METADATA_KEY_ALBUM }, { "artist", METADATA_KEY_ARTIST }, { "composer", METADATA_KEY_COMPOSER }, diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index dfba74f..f617fe8 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -305,6 +305,7 @@ void StagefrightMetadataRetriever::parseMetaData() { static const Map kMap[] = { { kKeyMIMEType, METADATA_KEY_MIMETYPE }, { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER }, + { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER }, { kKeyAlbum, METADATA_KEY_ALBUM }, { kKeyArtist, METADATA_KEY_ARTIST }, { kKeyAuthor, METADATA_KEY_AUTHOR }, diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 6d64717..16a2a10 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -24,6 +24,7 @@ #include <media/stagefright/MediaDebug.h> #include <media/stagefright/Utils.h> #include <utils/String8.h> +#include <byteswap.h> namespace android { @@ -273,7 +274,9 @@ static void convertISO8859ToString8( String8 *s) { size_t utf8len = 0; for (size_t i = 0; i < size; ++i) { - if (data[i] < 0x80) { + if (data[i] == '\0') { + break; + } else if (data[i] < 0x80) { ++utf8len; } else { utf8len += 2; @@ -290,7 +293,9 @@ static void convertISO8859ToString8( char *tmp = new char[utf8len]; char *ptr = tmp; for (size_t i = 0; i < size; ++i) { - if (data[i] < 0x80) { + if (data[i] == '\0') { + break; + } else if (data[i] < 0x80) { *ptr++ = data[i]; } else if (data[i] < 0xc0) { *ptr++ = 0xc2; @@ -324,7 +329,7 @@ void ID3::Iterator::getString(String8 *id) const { return; } - id->setTo((const char *)mFrameData, mFrameSize); + convertISO8859ToString8(mFrameData, mFrameSize, id); return; } @@ -336,7 +341,26 @@ void ID3::Iterator::getString(String8 *id) const { } else { // UCS-2 // API wants number of characters, not number of bytes... - id->setTo((const char16_t *)(mFrameData + 1), n / 2); + int len = n / 2; + const char16_t *framedata = (const char16_t *) (mFrameData + 1); + char16_t *framedatacopy = NULL; + if (*framedata == 0xfffe) { + // endianness marker doesn't match host endianness, convert + framedatacopy = new char16_t[len]; + for (int i = 0; i < len; i++) { + framedatacopy[i] = bswap_16(framedata[i]); + } + framedata = framedatacopy; + } + // If the string starts with an endianness marker, skip it + if (*framedata == 0xfeff) { + framedata++; + len--; + } + id->setTo(framedata, len); + if (framedatacopy != NULL) { + delete[] framedatacopy; + } } } diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 75e71e6..a19784b 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -57,6 +57,11 @@ struct AwesomePlayer { void reset(); + status_t prepare(); + status_t prepare_l(); + status_t prepareAsync(); + status_t prepareAsync_l(); + status_t play(); status_t pause(); @@ -80,6 +85,8 @@ private: PLAYING = 1, LOOPING = 2, FIRST_FRAME = 4, + PREPARING = 8, + PREPARED = 16, }; mutable Mutex mLock; @@ -93,6 +100,9 @@ private: TimeSource *mTimeSource; + String8 mUri; + KeyedVector<String8, String8> mUriHeaders; + sp<MediaSource> mVideoSource; sp<AwesomeRenderer> mVideoRenderer; @@ -121,6 +131,11 @@ private: sp<TimedEventQueue::Event> mCheckAudioStatusEvent; bool mAudioStatusEventPending; + sp<TimedEventQueue::Event> mAsyncPrepareEvent; + Condition mPreparedCondition; + bool mIsAsyncPrepare; + status_t mPrepareResult; + void postVideoEvent_l(int64_t delayUs = -1); void postBufferingEvent_l(); void postStreamDoneEvent_l(); @@ -143,13 +158,15 @@ private: status_t setAudioSource(sp<MediaSource> source); status_t setVideoSource(sp<MediaSource> source); - void onEvent(int32_t code); void onStreamDone(); - void notifyListener_l(int msg, int ext1 = 0); + void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0); + void onVideoEvent(); void onBufferingUpdate(); void onCheckAudioStatus(); + void onPrepareAsyncEvent(); + status_t finishSetDataSource_l(); AwesomePlayer(const AwesomePlayer &); AwesomePlayer &operator=(const AwesomePlayer &); diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h index 7a97785..d227864 100644 --- a/media/libstagefright/include/Prefetcher.h +++ b/media/libstagefright/include/Prefetcher.h @@ -34,7 +34,9 @@ struct Prefetcher : public RefBase { // that will benefit from prefetching/caching the original one. sp<MediaSource> addSource(const sp<MediaSource> &source); - int64_t getCachedDurationUs(); + int64_t getCachedDurationUs(bool *noMoreData = NULL); + + status_t prepare(); protected: virtual ~Prefetcher(); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 70c6a58..2458d2a 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -129,11 +129,16 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { sendCommand(OMX_CommandStateSet, OMX_StateIdle); OMX_ERRORTYPE err; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateIdle) { + && state != OMX_StateIdle + && state != OMX_StateInvalid) { usleep(100000); } CHECK_EQ(err, OMX_ErrorNone); + if (state == OMX_StateInvalid) { + break; + } + // fall through } @@ -146,7 +151,8 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_ERRORTYPE err; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone - && state != OMX_StateLoaded) { + && state != OMX_StateLoaded + && state != OMX_StateInvalid) { LOGV("waiting for Loaded state..."); usleep(100000); } diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml index 3d72017..5ec72df 100755 --- a/packages/DefaultContainerService/AndroidManifest.xml +++ b/packages/DefaultContainerService/AndroidManifest.xml @@ -5,9 +5,9 @@ <uses-permission android:name="android.permission.ASEC_CREATE"/> <uses-permission android:name="android.permission.ASEC_DESTROY"/> <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <application android:process="def.container.service" - android:label="@string/service_name"> + <application android:label="@string/service_name"> <service android:name=".DefaultContainerService" android:enabled="true" diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index d23b7d0..c418ccb 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -3,15 +3,19 @@ package com.android.defcontainer; import com.android.internal.app.IMediaContainerService; import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Debug; +import android.os.Environment; import android.os.IBinder; -import android.os.IMountService; -import android.os.MountServiceResultCode; +import android.os.storage.IMountService; +import android.os.storage.StorageResultCode; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.app.IntentService; import android.app.Service; import android.util.Log; @@ -25,7 +29,6 @@ import java.io.OutputStream; import android.os.FileUtils; - /* * This service copies a downloaded apk to a file passed in as * a ParcelFileDescriptor or to a newly created container specified @@ -33,7 +36,7 @@ import android.os.FileUtils; * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER * permission to access apks downloaded via the download manager. */ -public class DefaultContainerService extends Service { +public class DefaultContainerService extends IntentService { private static final String TAG = "DefContainer"; private static final boolean localLOGV = false; @@ -78,6 +81,40 @@ public class DefaultContainerService extends Service { } }; + public DefaultContainerService() { + super("DefaultContainerService"); + setIntentRedelivery(true); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) { + IPackageManager pm = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + String pkg = null; + try { + while ((pkg=pm.nextPackageToClean(pkg)) != null) { + eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg)); + eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg)); + } + } catch (RemoteException e) { + } + } + } + + void eraseFiles(File path) { + if (path.isDirectory()) { + String[] files = path.list(); + if (files != null) { + for (String file : files) { + eraseFiles(new File(path, file)); + } + } + } + //Log.i(TAG, "Deleting: " + path); + path.delete(); + } + public IBinder onBind(Intent intent) { return mBinder; } @@ -158,12 +195,12 @@ public class DefaultContainerService extends Service { int rc = mountService.createSecureContainer( containerId, mbLen, "vfat", sdEncKey, ownerUid); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Container creation failed (%d)", rc)); // XXX: This destroy should not be necessary rc = mountService.destroySecureContainer(containerId); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Container creation-cleanup failed (%d)", rc)); return null; } @@ -171,7 +208,7 @@ public class DefaultContainerService extends Service { // XXX: Does this ever actually succeed? rc = mountService.createSecureContainer( containerId, mbLen, "vfat", sdEncKey, ownerUid); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Container creation retry failed (%d)", rc)); } } @@ -226,7 +263,7 @@ public class DefaultContainerService extends Service { private String mountSdDir(String containerId, String key) { try { int rc = getMountService().mountSecureContainer(containerId, key, Process.myUid()); - if (rc == MountServiceResultCode.OperationSucceeded) { + if (rc == StorageResultCode.OperationSucceeded) { return getMountService().getSecureContainerPath(containerId); } else { Log.e(TAG, String.format("Failed to mount id %s with rc %d ", containerId, rc)); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index b7eea2e..bca736a 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -133,6 +133,7 @@ public class TtsService extends Service implements OnCompletionListener { private HashMap<String, ITtsCallback> mCallbacksMap; private Boolean mIsSpeaking; + private Boolean mSynthBusy; private ArrayList<SpeechItem> mSpeechQueue; private HashMap<String, SoundResource> mEarcons; private HashMap<String, SoundResource> mUtterances; @@ -168,6 +169,7 @@ public class TtsService extends Service implements OnCompletionListener { mSelf = this; mIsSpeaking = false; + mSynthBusy = false; mEarcons = new HashMap<String, SoundResource>(); mUtterances = new HashMap<String, SoundResource>(); @@ -733,10 +735,11 @@ public class TtsService extends Service implements OnCompletionListener { try { synthAvailable = synthesizerLock.tryLock(); if (!synthAvailable) { + mSynthBusy = true; Thread.sleep(100); Thread synth = (new Thread(new SynthThread())); - //synth.setPriority(Thread.MIN_PRIORITY); synth.start(); + mSynthBusy = false; return; } int streamType = DEFAULT_STREAM_TYPE; @@ -821,10 +824,11 @@ public class TtsService extends Service implements OnCompletionListener { try { synthAvailable = synthesizerLock.tryLock(); if (!synthAvailable) { + mSynthBusy = true; Thread.sleep(100); Thread synth = (new Thread(new SynthThread())); - //synth.setPriority(Thread.MIN_PRIORITY); synth.start(); + mSynthBusy = false; return; } String language = ""; @@ -959,6 +963,12 @@ public class TtsService extends Service implements OnCompletionListener { private void processSpeechQueue() { boolean speechQueueAvailable = false; + synchronized (this) { + if (mSynthBusy){ + // There is already a synth thread waiting to run. + return; + } + } try { speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS); diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java index b56b13b..407983d 100644 --- a/services/java/com/android/server/AccessibilityManagerService.java +++ b/services/java/com/android/server/AccessibilityManagerService.java @@ -176,8 +176,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub context.registerReceiver(broadcastReceiver, packageFilter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(broadcastReceiver, sdFilter); // boot completed diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index c28cf44..44cc0bb 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -782,7 +782,7 @@ class AlarmManagerService extends IAlarmManager.Stub { mContext.registerReceiver(this, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(this, sdFilter); } @@ -791,7 +791,7 @@ class AlarmManagerService extends IAlarmManager.Stub { synchronized (mLock) { String action = intent.getAction(); String pkgList[] = null; - if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } else { Uri data = intent.getData(); diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 684f117..f336d1f 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -147,8 +147,8 @@ class AppWidgetService extends IAppWidgetService.Stub mContext.registerReceiver(mBroadcastReceiver, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mBroadcastReceiver, sdFilter); } @@ -1077,10 +1077,10 @@ class AppWidgetService extends IAppWidgetService.Stub } else { boolean added = false; String pkgList[] = null; - if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) { + if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); added = true; - } if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); added = false; } else { diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 2f845e1..27055ed 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -76,6 +76,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; @@ -226,7 +227,9 @@ class BackupManagerService extends IBackupManager.Stub { private File mEverStored; HashSet<String> mEverStoredApps = new HashSet<String>(); + static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes File mTokenFile; + Set<String> mAncestralPackages = null; long mAncestralToken = 0; long mCurrentToken = 0; @@ -500,8 +503,20 @@ class BackupManagerService extends IBackupManager.Stub { mTokenFile = new File(mBaseStateDir, "ancestral"); try { RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r"); - mAncestralToken = tf.readLong(); - mCurrentToken = tf.readLong(); + int version = tf.readInt(); + if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { + mAncestralToken = tf.readLong(); + mCurrentToken = tf.readLong(); + + int numPackages = tf.readInt(); + if (numPackages >= 0) { + mAncestralPackages = new HashSet<String>(); + for (int i = 0; i < numPackages; i++) { + String pkgName = tf.readUTF(); + mAncestralPackages.add(pkgName); + } + } + } } catch (IOException e) { Log.w(TAG, "Unable to read token file", e); } @@ -565,8 +580,8 @@ class BackupManagerService extends IBackupManager.Stub { mContext.registerReceiver(mBroadcastReceiver, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mBroadcastReceiver, sdFilter); } @@ -724,10 +739,10 @@ class BackupManagerService extends IBackupManager.Stub { } added = Intent.ACTION_PACKAGE_ADDED.equals(action); replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); - } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) { + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { added = true; pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - } else if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { added = false; pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } @@ -972,12 +987,31 @@ class BackupManagerService extends IBackupManager.Stub { } } - // Record the current and ancestral backup tokens persistently + // Persistently record the current and ancestral backup tokens as well + // as the set of packages with data [supposedly] available in the + // ancestral dataset. void writeRestoreTokens() { try { RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd"); + + // First, the version number of this record, for futureproofing + af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION); + + // Write the ancestral and current tokens af.writeLong(mAncestralToken); af.writeLong(mCurrentToken); + + // Now write the set of ancestral packages + if (mAncestralPackages == null) { + af.writeInt(-1); + } else { + af.writeInt(mAncestralPackages.size()); + if (DEBUG) Log.v(TAG, "Ancestral packages: " + mAncestralPackages.size()); + for (String pkgName : mAncestralPackages) { + af.writeUTF(pkgName); + if (DEBUG) Log.v(TAG, " " + pkgName); + } + } af.close(); } catch (IOException e) { Log.w(TAG, "Unable to write token file:", e); @@ -1502,6 +1536,7 @@ class BackupManagerService extends IBackupManager.Stub { * the user is waiting, after all. */ + PackageManagerBackupAgent pmAgent = null; int error = -1; // assume error // build the set of apps to restore @@ -1562,7 +1597,7 @@ class BackupManagerService extends IBackupManager.Stub { } // Pull the Package Manager metadata from the restore set first - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + pmAgent = new PackageManagerBackupAgent( mPackageManager, agentPackages); processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind())); @@ -1705,8 +1740,10 @@ class BackupManagerService extends IBackupManager.Stub { } // If this was a restoreAll operation, record that this was our - // ancestral dataset - if (mTargetPackage == null) { + // ancestral dataset, as well as the set of apps that are possibly + // restoreable from the dataset + if (mTargetPackage == null && pmAgent != null) { + mAncestralPackages = pmAgent.getRestoredPackages(); mAncestralToken = mToken; writeRestoreTokens(); } @@ -2436,6 +2473,12 @@ class BackupManagerService extends IBackupManager.Stub { } } + pw.println("Ancestral packages: " + + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); + for (String pkg : mAncestralPackages) { + pw.println(" " + pkg); + } + pw.println("Ever backed up: " + mEverStoredApps.size()); for (String pkg : mEverStoredApps) { pw.println(" " + pkg); diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index e4ee4ae..1625d9f 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -320,14 +320,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } tag = parser.getName(); if ("admin".equals(tag)) { - DeviceAdminInfo dai = findAdmin( - ComponentName.unflattenFromString( - parser.getAttributeValue(null, "name"))); - if (dai != null) { - ActiveAdmin ap = new ActiveAdmin(dai); - ap.readFromXml(parser); - mAdminMap.put(ap.info.getComponent(), ap); - mAdminList.add(ap); + String name = parser.getAttributeValue(null, "name"); + try { + DeviceAdminInfo dai = findAdmin( + ComponentName.unflattenFromString(name)); + if (dai != null) { + ActiveAdmin ap = new ActiveAdmin(dai); + ap.readFromXml(parser); + mAdminMap.put(ap.info.getComponent(), ap); + mAdminList.add(ap); + } + } catch (RuntimeException e) { + Log.w(TAG, "Failed loading admin " + name, e); } } else if ("failed-password-attempts".equals(tag)) { mFailedPasswordAttempts = Integer.parseInt( diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index dc942a2..5e96a2f 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -344,8 +344,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (pkg != null) { pkgList = new String[] { pkg }; } - } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) || - Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) { + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) || + Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } if (pkgList == null || pkgList.length == 0) { @@ -448,8 +448,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mContext.registerReceiver(mBroadcastReceiver, packageFilt); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE); - sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mBroadcastReceiver, sdFilter); IntentFilter screenOnOffFilt = new IntentFilter(); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 3a42b37..fff6c54 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -29,16 +29,18 @@ import java.util.Set; import android.app.PendingIntent; 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.ServiceConnection; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.database.Cursor; import android.location.Address; import android.location.GeocoderParams; -import android.location.IGeocodeProvider; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationListener; @@ -64,10 +66,11 @@ import android.provider.Settings; import android.util.Log; import android.util.PrintWriterPrinter; +import com.android.internal.location.GeocoderProxy; import com.android.internal.location.GpsLocationProvider; +import com.android.internal.location.GpsNetInitiatedHandler; import com.android.internal.location.LocationProviderProxy; import com.android.internal.location.MockProvider; -import com.android.internal.location.GpsNetInitiatedHandler; /** * The service class that manages LocationProviders and issues location @@ -105,7 +108,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private static boolean sProvidersLoaded = false; private final Context mContext; - private IGeocodeProvider mGeocodeProvider; + private GeocoderProxy mGeocodeProvider; private IGpsStatusProvider mGpsStatusProvider; private INetInitiatedListener mNetInitiatedListener; private LocationWorkerHandler mLocationHandler; @@ -415,7 +418,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private void removeProvider(LocationProviderProxy provider) { mProviders.remove(provider); - provider.unlinkProvider(); mProvidersByName.remove(provider.getName()); } @@ -446,11 +448,28 @@ public class LocationManagerService extends ILocationManager.Stub implements Run GpsLocationProvider provider = new GpsLocationProvider(mContext, this); mGpsStatusProvider = provider.getGpsStatusProvider(); mNetInitiatedListener = provider.getNetInitiatedListener(); - LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider); + LocationProviderProxy proxy = + new LocationProviderProxy(mContext, LocationManager.GPS_PROVIDER, provider); addProvider(proxy); mGpsLocationProvider = proxy; } + // initialize external network location and geocoder services + Resources resources = mContext.getResources(); + String serviceName = resources.getString( + com.android.internal.R.string.config_networkLocationProvider); + if (serviceName != null) { + mNetworkLocationProvider = + new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER, + serviceName, mLocationHandler); + addProvider(mNetworkLocationProvider); + } + + serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider); + if (serviceName != null) { + mGeocodeProvider = new GeocoderProxy(mContext, serviceName); + } + updateProvidersLocked(); } @@ -484,7 +503,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); mContext.registerReceiver(mBroadcastReceiver, intentFilter); - IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mBroadcastReceiver, sdFilter); // listen for settings changes @@ -507,45 +526,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Looper.loop(); } - public void installLocationProvider(String name, ILocationProvider provider) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); - } - - synchronized (mLock) { - // check to see if we are reinstalling a dead provider - LocationProviderProxy oldProvider = mProvidersByName.get(name); - if (oldProvider != null) { - if (oldProvider.isDead()) { - Log.d(TAG, "replacing dead provider"); - removeProvider(oldProvider); - } else { - throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); - } - } - - LocationProviderProxy proxy = new LocationProviderProxy(name, provider); - addProvider(proxy); - updateProvidersLocked(); - if (LocationManager.NETWORK_PROVIDER.equals(name)) { - mNetworkLocationProvider = proxy; - } - - // notify provider of current network state - proxy.updateNetworkState(mNetworkState, null); - } - } - - public void installGeocodeProvider(IGeocodeProvider provider) { - if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); - } - - mGeocodeProvider = provider; - } - private boolean isAllowedBySettingsLocked(String provider) { if (mEnabledProviders.contains(provider)) { return true; @@ -1313,7 +1293,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } /** - * @return null if the provider does not exits + * @return null if the provider does not exist * @throws SecurityException if the provider is not allowed to be * accessed by the caller */ @@ -1332,7 +1312,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private Bundle _getProviderInfoLocked(String provider) { LocationProviderProxy p = mProvidersByName.get(provider); - if (p == null) { + if (p == null || !p.isEnabled()) { return null; } @@ -1552,10 +1532,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) - || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) { + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { synchronized (mLock) { int uidList[] = null; - if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) { + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); } else { uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)}; @@ -1618,7 +1598,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { for (int i = mProviders.size() - 1; i >= 0; i--) { LocationProviderProxy provider = mProviders.get(i); - if (provider.requiresNetwork()) { + if (provider.isEnabled() && provider.requiresNetwork()) { provider.updateNetworkState(mNetworkState, info); } } @@ -1669,13 +1649,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public String getFromLocation(double latitude, double longitude, int maxResults, GeocoderParams params, List<Address> addrs) { if (mGeocodeProvider != null) { - try { - return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, - params, addrs); - } catch (RemoteException e) { - Log.e(TAG, "getFromLocation failed", e); - mGeocodeProvider = null; - } + return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, + params, addrs); } return null; } @@ -1687,14 +1662,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run GeocoderParams params, List<Address> addrs) { if (mGeocodeProvider != null) { - try { - return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, addrs); - } catch (RemoteException e) { - Log.e(TAG, "getFromLocationName failed", e); - mGeocodeProvider = null; - } + return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); } return null; } @@ -1737,7 +1707,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mProvidersByName.get(name) != null) { throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); } - addProvider(new LocationProviderProxy(name, provider)); + addProvider(new LocationProviderProxy(mContext, name, provider)); mMockProviders.put(name, provider); mLastKnownLocation.put(name, null); updateProvidersLocked(); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 6382646..456244a 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -23,29 +23,34 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; -import android.os.IMountService; -import android.os.IMountServiceListener; -import android.os.MountServiceResultCode; +import android.os.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.os.storage.StorageResultCode; import android.os.RemoteException; import android.os.IBinder; import android.os.Environment; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.UEventObserver; import android.os.Handler; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; +import java.util.HashSet; import java.io.File; import java.io.FileReader; /** - * MountService implements an to the mount service daemon - * @hide + * MountService implements back-end services for platform storage + * management. + * @hide - Applications should use android.os.storage.StorageManager + * to access the MountService. */ class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks { + private static final boolean LOCAL_LOGD = false; private static final String TAG = "MountService"; @@ -109,45 +114,40 @@ class MountService extends IMountService.Stub private PackageManagerService mPms; private boolean mUmsEnabling; private ArrayList<MountServiceBinderListener> mListeners; + private boolean mBooted; + private boolean mReady; /** - * Constructs a new MountService instance - * - * @param context Binder context for this service + * Private hash of currently mounted secure containers. */ - public MountService(Context context) { - mContext = context; - - // XXX: This will go away soon in favor of IMountServiceObserver - mPms = (PackageManagerService) ServiceManager.getService("package"); - - // Register a BOOT_COMPLETED handler so that we can start - // our NativeDaemonConnector. We defer the startup so that we don't - // start processing events before we ought-to - mContext.registerReceiver(mBroadcastReceiver, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + private HashSet<String> mAsecMountSet = new HashSet<String>(); - mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector"); - mListeners = new ArrayList<MountServiceBinderListener>(); + private void waitForReady() { + while (mReady == false) { + for (int retries = 5; retries > 0; retries--) { + if (mReady) { + return; + } + SystemClock.sleep(1000); + } + Log.w(TAG, "Waiting too long for mReady!"); + } } - BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { - /* - * Vold does not run in the simulator, so fake out a mounted - * event to trigger MediaScanner - */ - if ("simulator".equals(SystemProperties.get("ro.product.device"))) { - updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED); - return; - } + mBooted = true; - Thread thread = new Thread( - mConnector, NativeDaemonConnector.class.getName()); - thread.start(); + String path = Environment.getExternalStorageDirectory().getPath(); + if (getVolumeState(path).equals(Environment.MEDIA_UNMOUNTED)) { + int rc = doMountVolume(path); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, String.format("Boot-time mount failed (%d)", rc)); + } + } } } }; @@ -161,7 +161,7 @@ class MountService extends IMountService.Stub } public void binderDied() { - Log.d(TAG, "An IMountServiceListener has died!"); + if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!"); synchronized(mListeners) { mListeners.remove(this); mListener.asBinder().unlinkToDeath(this, 0); @@ -169,7 +169,7 @@ class MountService extends IMountService.Stub } } - int doShareUnshareVolume(String path, String method, boolean enable) { + private int doShareUnshareVolume(String path, String method, boolean enable) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); // TODO: Add support for multiple share methods @@ -182,9 +182,13 @@ class MountService extends IMountService.Stub */ String vs = getVolumeState(path); if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { - mUmsEnabling = enable; // Supress unmounted events - unmountVolume(path); - mUmsEnabling = false; // Unsupress unmounted events + mUmsEnabling = enable; // Override for isUsbMassStorageEnabled() + int rc = doUnmountVolume(path); + mUmsEnabling = false; // Clear override + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc)); + return rc; + } } try { @@ -192,14 +196,14 @@ class MountService extends IMountService.Stub "volume %sshare %s %s", (enable ? "" : "un"), path, method)); } catch (NativeDaemonConnectorException e) { Log.e(TAG, "Failed to share/unshare", e); - return MountServiceResultCode.OperationFailedInternalError; + return StorageResultCode.OperationFailedInternalError; } /* * If we disabled UMS then mount the volume */ if (!enable) { - if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) { + if (doMountVolume(path) != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format( "Failed to remount %s after disabling share method %s", path, method)); /* @@ -210,15 +214,19 @@ class MountService extends IMountService.Stub } } - return MountServiceResultCode.OperationSucceeded; + return StorageResultCode.OperationSucceeded; } - void updatePublicVolumeState(String path, String state) { + private void updatePublicVolumeState(String path, String state) { if (!path.equals(Environment.getExternalStorageDirectory().getPath())) { Log.w(TAG, "Multiple volumes not currently supported"); return; } - Log.i(TAG, "State for {" + path + "} = {" + state + "}"); + + if (mLegacyState.equals(state)) { + Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state)); + return; + } String oldState = mLegacyState; mLegacyState = state; @@ -227,7 +235,7 @@ class MountService extends IMountService.Stub for (int i = mListeners.size() -1; i >= 0; i--) { MountServiceBinderListener bl = mListeners.get(i); try { - bl.mListener.onVolumeStateChanged("", path, oldState, state); + bl.mListener.onStorageStateChanged(path, oldState, state); } catch (RemoteException rex) { Log.e(TAG, "Listener dead"); mListeners.remove(i); @@ -270,11 +278,7 @@ class MountService extends IMountService.Stub if (st == VolumeState.NoMedia) { state = Environment.MEDIA_REMOVED; } else if (st == VolumeState.Idle) { - state = null; - int rc = mountVolume(path); - if (rc != MountServiceResultCode.OperationSucceeded) { - Log.e(TAG, String.format("Connection-mount failed (%d)", rc)); - } + state = Environment.MEDIA_UNMOUNTED; } else if (st == VolumeState.Mounted) { state = Environment.MEDIA_MOUNTED; Log.i(TAG, "Media already mounted on daemon connection"); @@ -294,23 +298,26 @@ class MountService extends IMountService.Stub } try { - boolean avail = getShareMethodAvailable("ums"); + boolean avail = doGetShareMethodAvailable("ums"); notifyShareAvailabilityChange("ums", avail); } catch (Exception ex) { Log.w(TAG, "Failed to get share availability"); } + /* + * Now that we've done our initialization, release + * the hounds! + */ + mReady = true; } }.start(); } /** - * * Callback from NativeDaemonConnector */ public boolean onEvent(int code, String raw, String[] cooked) { Intent in = null; - // Log.d(TAG, "event {" + raw + "}"); if (code == VoldResponseCode.VolumeStateChange) { /* * One of the volumes we're managing has changed state. @@ -347,34 +354,12 @@ class MountService extends IMountService.Stub Log.e(TAG, "Failed to parse major/minor", ex); } - synchronized (mListeners) { - for (int i = mListeners.size() -1; i >= 0; i--) { - MountServiceBinderListener bl = mListeners.get(i); - try { - if (code == VoldResponseCode.VolumeDiskInserted) { - bl.mListener.onMediaInserted(label, path, major, minor); - } else if (code == VoldResponseCode.VolumeDiskRemoved) { - bl.mListener.onMediaRemoved(label, path, major, minor, true); - } else if (code == VoldResponseCode.VolumeBadRemoval) { - bl.mListener.onMediaRemoved(label, path, major, minor, false); - } else { - Log.e(TAG, String.format("Unknown code {%d}", code)); - } - } catch (RemoteException rex) { - Log.e(TAG, "Listener dead"); - mListeners.remove(i); - } catch (Exception ex) { - Log.e(TAG, "Listener failed", ex); - } - } - } - if (code == VoldResponseCode.VolumeDiskInserted) { new Thread() { public void run() { try { int rc; - if ((rc = mountVolume(path)) != MountServiceResultCode.OperationSucceeded) { + if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { Log.w(TAG, String.format("Insertion mount failed (%d)", rc)); } } catch (Exception ex) { @@ -417,7 +402,7 @@ class MountService extends IMountService.Stub return true; } - void notifyVolumeStateChange(String label, String path, int oldState, int newState) { + private void notifyVolumeStateChange(String label, String path, int oldState, int newState) { String vs = getVolumeState(path); Intent in = null; @@ -471,7 +456,144 @@ class MountService extends IMountService.Stub } } - void notifyShareAvailabilityChange(String method, final boolean avail) { + private boolean doGetShareMethodAvailable(String method) { + ArrayList<String> rsp = mConnector.doCommand("share status " + method); + + for (String line : rsp) { + String []tok = line.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Log.e(TAG, String.format("Error parsing code %s", tok[0])); + return false; + } + if (code == VoldResponseCode.ShareStatusResult) { + if (tok[2].equals("available")) + return true; + return false; + } else { + Log.e(TAG, String.format("Unexpected response code %d", code)); + return false; + } + } + Log.e(TAG, "Got an empty response"); + return false; + } + + private int doMountVolume(String path) { + int rc = StorageResultCode.OperationSucceeded; + + try { + mConnector.doCommand(String.format("volume mount %s", path)); + } catch (NativeDaemonConnectorException e) { + /* + * Mount failed for some reason + */ + Intent in = null; + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedNoMedia) { + /* + * Attempt to mount but no media inserted + */ + rc = StorageResultCode.OperationFailedNoMedia; + } else if (code == VoldResponseCode.OpFailedMediaBlank) { + /* + * Media is blank or does not contain a supported filesystem + */ + updatePublicVolumeState(path, Environment.MEDIA_NOFS); + in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path)); + rc = StorageResultCode.OperationFailedMediaBlank; + } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { + /* + * Volume consistency check failed + */ + updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); + in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); + rc = StorageResultCode.OperationFailedMediaCorrupt; + } else { + rc = StorageResultCode.OperationFailedInternalError; + } + + /* + * Send broadcast intent (if required for the failure) + */ + if (in != null) { + mContext.sendBroadcast(in); + } + } + + return rc; + } + + private int doUnmountVolume(String path) { + if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) { + return VoldResponseCode.OpFailedVolNotMounted; + } + + // Notify PackageManager of potential media removal and deal with + // return code later on. The caller of this api should be aware or have been + // notified that the applications installed on the media will be killed. + mPms.updateExternalMediaStatus(false); + try { + mConnector.doCommand(String.format("volume unmount %s", path)); + return StorageResultCode.OperationSucceeded; + } catch (NativeDaemonConnectorException e) { + // Don't worry about mismatch in PackageManager since the + // call back will handle the status changes any way. + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedVolNotMounted) { + return StorageResultCode.OperationFailedVolumeNotMounted; + } else { + return StorageResultCode.OperationFailedInternalError; + } + } + } + + private int doFormatVolume(String path) { + try { + String cmd = String.format("volume format %s", path); + mConnector.doCommand(cmd); + return StorageResultCode.OperationSucceeded; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedNoMedia) { + return StorageResultCode.OperationFailedNoMedia; + } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { + return StorageResultCode.OperationFailedMediaCorrupt; + } else { + return StorageResultCode.OperationFailedInternalError; + } + } + } + + private boolean doGetVolumeShared(String path, String method) { + String cmd = String.format("volume shared %s %s", path, method); + ArrayList<String> rsp = mConnector.doCommand(cmd); + + for (String line : rsp) { + String []tok = line.split(" "); + int code; + try { + code = Integer.parseInt(tok[0]); + } catch (NumberFormatException nfe) { + Log.e(TAG, String.format("Error parsing code %s", tok[0])); + return false; + } + if (code == VoldResponseCode.ShareEnabledResult) { + if (tok[2].equals("enabled")) + return true; + return false; + } else { + Log.e(TAG, String.format("Unexpected response code %d", code)); + return false; + } + } + Log.e(TAG, "Got an empty response"); + return false; + } + + private void notifyShareAvailabilityChange(String method, final boolean avail) { if (!method.equals("ums")) { Log.w(TAG, "Ignoring unsupported share method {" + method + "}"); return; @@ -481,7 +603,7 @@ class MountService extends IMountService.Stub for (int i = mListeners.size() -1; i >= 0; i--) { MountServiceBinderListener bl = mListeners.get(i); try { - bl.mListener.onShareAvailabilityChanged(method, avail); + bl.mListener.onUsbMassStorageConnectionChanged(avail); } catch (RemoteException rex) { Log.e(TAG, "Listener dead"); mListeners.remove(i); @@ -491,22 +613,55 @@ class MountService extends IMountService.Stub } } - Intent intent; - if (avail) { - intent = new Intent(Intent.ACTION_UMS_CONNECTED); - } else { - intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); + if (mBooted == true) { + Intent intent; + if (avail) { + intent = new Intent(Intent.ACTION_UMS_CONNECTED); + } else { + intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); + } + mContext.sendBroadcast(intent); } - mContext.sendBroadcast(intent); } - void validatePermission(String perm) { + private void validatePermission(String perm) { if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(String.format("Requires %s permission", perm)); } } /** + * Constructs a new MountService instance + * + * @param context Binder context for this service + */ + public MountService(Context context) { + mContext = context; + + /* + * Vold does not run in the simulator, so fake out a mounted + * event to trigger MediaScanner + */ + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { + updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED); + return; + } + + // XXX: This will go away soon in favor of IMountServiceObserver + mPms = (PackageManagerService) ServiceManager.getService("package"); + + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + + mListeners = new ArrayList<MountServiceBinderListener>(); + + mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector"); + mReady = false; + Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); + thread.start(); + } + + /** * Exposed API calls below here */ @@ -549,7 +704,7 @@ class MountService extends IMountService.Stub * the UMS host could have dirty FAT cache entries * yet to flush. */ - if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) { + if (setUsbMassStorageEnabled(false) != StorageResultCode.OperationSucceeded) { Log.e(TAG, "UMS disable on shutdown failed"); } } else if (state.equals(Environment.MEDIA_CHECKING)) { @@ -577,75 +732,30 @@ class MountService extends IMountService.Stub /* * If the media is mounted, then gracefully unmount it. */ - if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) { + if (doUnmountVolume(path) != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to unmount media for shutdown"); } } } - public String[] getShareMethodList() { - String[] rdata = new String[1]; - rdata[0] = "ums"; - return rdata; - } + public boolean isUsbMassStorageConnected() { + waitForReady(); - public boolean getShareMethodAvailable(String method) { - ArrayList<String> rsp = mConnector.doCommand("share status " + method); - - for (String line : rsp) { - String []tok = line.split(" "); - int code; - try { - code = Integer.parseInt(tok[0]); - } catch (NumberFormatException nfe) { - Log.e(TAG, String.format("Error parsing code %s", tok[0])); - return false; - } - if (code == VoldResponseCode.ShareStatusResult) { - if (tok[2].equals("available")) - return true; - return false; - } else { - Log.e(TAG, String.format("Unexpected response code %d", code)); - return false; - } + if (mUmsEnabling) { + return true; } - Log.e(TAG, "Got an empty response"); - return false; + return doGetShareMethodAvailable("ums"); } - public int shareVolume(String path, String method) { - return doShareUnshareVolume(path, method, true); - } + public int setUsbMassStorageEnabled(boolean enable) { + waitForReady(); - public int unshareVolume(String path, String method) { - return doShareUnshareVolume(path, method, false); + return doShareUnshareVolume(Environment.getExternalStorageDirectory().getPath(), "ums", enable); } - public boolean getVolumeShared(String path, String method) { - String cmd = String.format("volume shared %s %s", path, method); - ArrayList<String> rsp = mConnector.doCommand(cmd); - - for (String line : rsp) { - String []tok = line.split(" "); - int code; - try { - code = Integer.parseInt(tok[0]); - } catch (NumberFormatException nfe) { - Log.e(TAG, String.format("Error parsing code %s", tok[0])); - return false; - } - if (code == VoldResponseCode.ShareEnabledResult) { - if (tok[2].equals("enabled")) - return true; - return false; - } else { - Log.e(TAG, String.format("Unexpected response code %d", code)); - return false; - } - } - Log.e(TAG, "Got an empty response"); - return false; + public boolean isUsbMassStorageEnabled() { + waitForReady(); + return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums"); } /** @@ -664,103 +774,38 @@ class MountService extends IMountService.Stub return mLegacyState; } - - /** - * Attempt to mount external media - */ public int mountVolume(String path) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - int rc = MountServiceResultCode.OperationSucceeded; - - try { - mConnector.doCommand(String.format("volume mount %s", path)); - } catch (NativeDaemonConnectorException e) { - /* - * Mount failed for some reason - */ - Intent in = null; - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedNoMedia) { - /* - * Attempt to mount but no media inserted - */ - rc = MountServiceResultCode.OperationFailedNoMedia; - } else if (code == VoldResponseCode.OpFailedMediaBlank) { - /* - * Media is blank or does not contain a supported filesystem - */ - updatePublicVolumeState(path, Environment.MEDIA_NOFS); - in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path)); - rc = MountServiceResultCode.OperationFailedMediaBlank; - } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { - /* - * Volume consistency check failed - */ - updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); - rc = MountServiceResultCode.OperationFailedMediaCorrupt; - } else { - rc = MountServiceResultCode.OperationFailedInternalError; - } - - /* - * Send broadcast intent (if required for the failure) - */ - if (in != null) { - mContext.sendBroadcast(in); - } - } - - return rc; + waitForReady(); + return doMountVolume(path); } - /** - * Attempt to unmount external media to prepare for eject - */ public int unmountVolume(String path) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); - try { - mConnector.doCommand(String.format("volume unmount %s", path)); - return MountServiceResultCode.OperationSucceeded; - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedVolNotMounted) { - return MountServiceResultCode.OperationFailedVolumeNotMounted; - } else { - return MountServiceResultCode.OperationFailedInternalError; - } - } + return doUnmountVolume(path); } - /** - * Synchronously formats a volume - * - * @param path The volume path to format - * @return Error code from MountServiceResultCode - */ public int formatVolume(String path) { validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); + waitForReady(); - try { - String cmd = String.format("volume format %s", path); - mConnector.doCommand(cmd); - return MountServiceResultCode.OperationSucceeded; - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedNoMedia) { - return MountServiceResultCode.OperationFailedNoMedia; - } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { - return MountServiceResultCode.OperationFailedMediaCorrupt; - } else { - return MountServiceResultCode.OperationFailedInternalError; - } + return doFormatVolume(path); + } + + private void warnOnNotMounted() { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + Log.w(TAG, "getSecureContainerList() called when storage not mounted"); } } public String[] getSecureContainerList() { validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + try { return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult); } catch (NativeDaemonConnectorException e) { @@ -771,82 +816,124 @@ class MountService extends IMountService.Stub public int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid) { validatePermission(android.Manifest.permission.ASEC_CREATE); + waitForReady(); + warnOnNotMounted(); - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid); try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; } return rc; } public int finalizeSecureContainer(String id) { validatePermission(android.Manifest.permission.ASEC_CREATE); + warnOnNotMounted(); - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; try { mConnector.doCommand(String.format("asec finalize %s", id)); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; } return rc; } public int destroySecureContainer(String id) { validatePermission(android.Manifest.permission.ASEC_DESTROY); + waitForReady(); + warnOnNotMounted(); - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; try { mConnector.doCommand(String.format("asec destroy %s", id)); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; } return rc; } public int mountSecureContainer(String id, String key, int ownerUid) { validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); + waitForReady(); + warnOnNotMounted(); - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; String cmd = String.format("asec mount %s %s %d", id, key, ownerUid); try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; + } + + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + mAsecMountSet.add(id); + } } return rc; } public int unmountSecureContainer(String id) { validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); + waitForReady(); + warnOnNotMounted(); + + synchronized (mAsecMountSet) { + if (!mAsecMountSet.contains(id)) { + return StorageResultCode.OperationFailedVolumeNotMounted; + } + } - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; String cmd = String.format("asec unmount %s", id); try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; + } + + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + mAsecMountSet.remove(id); + } } return rc; } + public boolean isSecureContainerMounted(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + + synchronized (mAsecMountSet) { + return mAsecMountSet.contains(id); + } + } + public int renameSecureContainer(String oldId, String newId) { validatePermission(android.Manifest.permission.ASEC_RENAME); + waitForReady(); + warnOnNotMounted(); - int rc = MountServiceResultCode.OperationSucceeded; + int rc = StorageResultCode.OperationSucceeded; String cmd = String.format("asec rename %s %s", oldId, newId); try { mConnector.doCommand(cmd); } catch (NativeDaemonConnectorException e) { - rc = MountServiceResultCode.OperationFailedInternalError; + rc = StorageResultCode.OperationFailedInternalError; } return rc; } public String getSecureContainerPath(String id) { validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + ArrayList<String> rsp = mConnector.doCommand("asec path " + id); for (String line : rsp) { diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index 9a066d3..cf083c0 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -21,7 +21,6 @@ import android.net.LocalSocket; import android.os.Environment; import android.os.SystemClock; import android.os.SystemProperties; -import android.util.Config; import android.util.Log; import java.io.IOException; @@ -41,6 +40,7 @@ import java.util.concurrent.LinkedBlockingQueue; * protocol. */ final class NativeDaemonConnector implements Runnable { + private static final boolean LOCAL_LOGD = false; private BlockingQueue<String> mResponseQueue; private OutputStream mOutputStream; @@ -110,7 +110,7 @@ final class NativeDaemonConnector implements Runnable { for (int i = 0; i < count; i++) { if (buffer[i] == 0) { String event = new String(buffer, start, i - start); -// Log.d(TAG, "Got packet {" + event + "}"); + if (LOCAL_LOGD) Log.d(TAG, String.format("RCV <- {%s}", event)); String[] tokens = event.split(" "); try { @@ -177,7 +177,7 @@ final class NativeDaemonConnector implements Runnable { */ private void sendCommand(String command, String argument) { synchronized (this) { - Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}"); + if (LOCAL_LOGD) Log.d(TAG, String.format("SND -> {%s} {%s}", command, argument)); if (mOutputStream == null) { Log.e(TAG, "No connection to daemon", new IllegalStateException()); } else { @@ -210,7 +210,7 @@ final class NativeDaemonConnector implements Runnable { while (!complete) { try { String line = mResponseQueue.take(); - Log.d(TAG, String.format("RSP -> {%s}", line)); + if (LOCAL_LOGD) Log.d(TAG, String.format("RSP <- {%s}", line)); String[] tokens = line.split(" "); try { code = Integer.parseInt(tokens[0]); @@ -250,13 +250,18 @@ final class NativeDaemonConnector implements Runnable { String[] rdata = new String[rsp.size()-1]; int idx = 0; - for (String line : rsp) { + for (int i = 0; i < rsp.size(); i++) { + String line = rsp.get(i); try { String[] tok = line.split(" "); int code = Integer.parseInt(tok[0]); if (code == expectedResponseCode) { rdata[idx++] = line.substring(tok[0].length() + 1); } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + if (LOCAL_LOGD) Log.d(TAG, String.format("List terminated with {%s}", line)); + if (i != rsp.size()) { + Log.w(TAG, String.format("Recv'd %d lines after list term", (rsp.size()-i))); + } return rdata; } else { throw new NativeDaemonConnectorException( diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 02a0401..ad8ab84 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -50,6 +50,7 @@ import android.os.Message; import android.os.Power; import android.os.Process; import android.os.RemoteException; +import android.os.storage.StorageManager; import android.os.SystemProperties; import android.os.Vibrator; import android.provider.Settings; @@ -330,9 +331,9 @@ class NotificationManagerService extends INotificationManager.Stub updateAdbNotification(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) - || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) { + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { String pkgList[] = null; - if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) { + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } else { Uri uri = intent.getData(); @@ -406,6 +407,10 @@ class NotificationManagerService extends INotificationManager.Stub mSound.setUsesWakeLock(context); mToastQueue = new ArrayList<ToastRecord>(); mHandler = new WorkerHandler(); + + StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + sm.registerListener(new com.android.internal.app.StorageNotification(context)); + mStatusBarService = statusBar; statusBar.setNotificationCallbacks(mNotificationCallbacks); @@ -440,7 +445,7 @@ class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver(mIntentReceiver, filter); - IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mIntentReceiver, sdFilter); SettingsObserver observer = new SettingsObserver(mHandler); diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index dbd1826..9551db5 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Set; /** * We back up the signatures of each package so that during a system restore, @@ -100,6 +101,20 @@ public class PackageManagerBackupAgent extends BackupAgent { return mRestoredSignatures.get(packageName); } + + public Set<String> getRestoredPackages() { + if (mRestoredSignatures == null) { + Log.w(TAG, "getRestoredPackages() before metadata read!"); + return null; + } + + // This is technically the set of packages on the originating handset + // that had backup agents at all, not limited to the set of packages + // that had actually contributed a restore dataset, but it's a + // close enough approximation for our purposes and does not require any + // additional involvement by the transport to obtain. + return mRestoredSignatures.keySet(); + } // The backed up data is the signature block for each app, keyed by // the package name. diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 387f139..10bf851 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -20,6 +20,7 @@ import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.common.FastXmlSerializer; import com.android.common.XmlUtils; +import com.android.server.JournaledFile; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -74,7 +75,7 @@ import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; -import android.os.MountServiceResultCode; +import android.os.storage.StorageResultCode; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ServiceManager; @@ -148,6 +149,10 @@ class PackageManagerService extends IPackageManager.Stub { static final int SCAN_NEW_INSTALL = 1<<4; static final int SCAN_NO_PATHS = 1<<5; + static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( + "com.android.defcontainer", + "com.android.defcontainer.DefaultContainerService"); + final HandlerThread mHandlerThread = new HandlerThread("PackageManager", Process.THREAD_PRIORITY_BACKGROUND); final PackageHandler mHandler; @@ -298,6 +303,7 @@ class PackageManagerService extends IPackageManager.Stub { static final int END_COPY = 4; static final int INIT_COPY = 5; static final int MCS_UNBIND = 6; + static final int START_CLEANING_PACKAGE = 7; // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; private ServiceConnection mDefContainerConn = new ServiceConnection() { @@ -328,9 +334,7 @@ class PackageManagerService extends IPackageManager.Stub { case INIT_COPY: { InstallArgs args = (InstallArgs) msg.obj; args.createCopyFile(); - Intent service = new Intent().setComponent(new ComponentName( - "com.android.defcontainer", - "com.android.defcontainer.DefaultContainerService")); + Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); if (mContainerService != null) { // No need to add to pending list. Use remote stub directly handleStartCopy(args); @@ -405,6 +409,15 @@ class PackageManagerService extends IPackageManager.Stub { } break; } + case START_CLEANING_PACKAGE: { + String packageName = (String)msg.obj; + synchronized (mPackages) { + if (!mSettings.mPackagesToBeCleaned.contains(packageName)) { + mSettings.mPackagesToBeCleaned.add(packageName); + } + } + startCleaningPackages(); + } break; } } @@ -2111,6 +2124,12 @@ class PackageManagerService extends IPackageManager.Stub { File file = new File(dir, files[i]); PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode); + // Don't mess around with apps in system partition. + if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0) { + // Delete the apk + Log.w(TAG, "Cleaning up failed install of " + file); + file.delete(); + } } } @@ -2353,6 +2372,13 @@ class PackageManagerService extends IPackageManager.Stub { private PackageParser.Package scanPackageLI( PackageParser.Package pkg, int parseFlags, int scanMode) { File scanFile = new File(pkg.mScanPath); + if (scanFile == null || pkg.applicationInfo.sourceDir == null || + pkg.applicationInfo.publicSourceDir == null) { + // Bail out. The resource and code paths haven't been set. + Log.w(TAG, " Code and resource paths haven't been set correctly"); + mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK; + return null; + } mScanningPath = scanFile; if (pkg == null) { mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; @@ -2810,6 +2836,9 @@ class PackageManagerService extends IPackageManager.Stub { mSettings.insertPackageSettingLP(pkgSetting, pkg); // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); + // Make sure we don't accidentally delete its data. + mSettings.mPackagesToBeCleaned.remove(pkgName); + int N = pkg.providers.size(); StringBuilder r = null; int i; @@ -4005,7 +4034,47 @@ class PackageManagerService extends IPackageManager.Stub { } } } + + public String nextPackageToClean(String lastPackage) { + synchronized (mPackages) { + if (!mMediaMounted) { + // If the external storage is no longer mounted at this point, + // the caller may not have been able to delete all of this + // packages files and can not delete any more. Bail. + return null; + } + if (lastPackage != null) { + mSettings.mPackagesToBeCleaned.remove(lastPackage); + } + return mSettings.mPackagesToBeCleaned.size() > 0 + ? mSettings.mPackagesToBeCleaned.get(0) : null; + } + } + void schedulePackageCleaning(String packageName) { + mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName)); + } + + void startCleaningPackages() { + synchronized (mPackages) { + if (!mMediaMounted) { + return; + } + if (mSettings.mPackagesToBeCleaned.size() <= 0) { + return; + } + } + Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE); + intent.setComponent(DEFAULT_CONTAINER_COMPONENT); + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + try { + am.startService(null, intent, null); + } catch (RemoteException e) { + } + } + } + private final class AppDirObserver extends FileObserver { public AppDirObserver(String path, int mask, boolean isrom) { super(path, mask); @@ -4959,69 +5028,82 @@ class PackageManagerService extends IPackageManager.Stub { File tmpPackageFile = new File(args.getCodePath()); boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0); - boolean replacingExistingPackage = false; + boolean replace = false; int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE | (newInstall ? SCAN_NEW_INSTALL : 0); // Result object to be returned res.returnCode = PackageManager.INSTALL_SUCCEEDED; - main_flow: try { - // Retrieve PackageSettings and parse package - int parseFlags = PackageParser.PARSE_CHATTY | - (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | - (onSd ? PackageParser.PARSE_ON_SDCARD : 0); - parseFlags |= mDefParseFlags; - PackageParser pp = new PackageParser(tmpPackageFile.getPath()); - pp.setSeparateProcesses(mSeparateProcesses); - final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, - null, mMetrics, parseFlags); - if (pkg == null) { - res.returnCode = pp.getParseError(); - break main_flow; - } - String pkgName = res.name = pkg.packageName; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { - if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { - res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; - break main_flow; - } + // Retrieve PackageSettings and parse package + int parseFlags = PackageParser.PARSE_CHATTY | + (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | + (onSd ? PackageParser.PARSE_ON_SDCARD : 0); + parseFlags |= mDefParseFlags; + PackageParser pp = new PackageParser(tmpPackageFile.getPath()); + pp.setSeparateProcesses(mSeparateProcesses); + final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, + null, mMetrics, parseFlags); + if (pkg == null) { + res.returnCode = pp.getParseError(); + return; + } + String pkgName = res.name = pkg.packageName; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { + if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { + res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY; + return; } - if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { - res.returnCode = pp.getParseError(); - break main_flow; + } + if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { + res.returnCode = pp.getParseError(); + return; + } + // Some preinstall checks + if (forwardLocked && onSd) { + // Make sure forward locked apps can only be installed + // on internal storage + Log.w(TAG, "Cannot install protected apps on sdcard"); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + return; + } + // Get rid of all references to package scan path via parser. + pp = null; + String oldCodePath = null; + boolean systemApp = false; + synchronized (mPackages) { + // Check if installing already existing package + if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0 + && mPackages.containsKey(pkgName)) { + replace = true; } - - // Get rid of all references to package scan path via parser. - pp = null; - String oldCodePath = null; - synchronized (mPackages) { - //check if installing already existing package - if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0 - && mPackages.containsKey(pkgName)) { - replacingExistingPackage = true; - } - PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - oldCodePath = mSettings.mPackages.get(pkgName).codePathString; + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + oldCodePath = mSettings.mPackages.get(pkgName).codePathString; + if (ps.pkg != null && ps.pkg.applicationInfo != null) { + systemApp = (ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SYSTEM) != 0; } } + } - if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { - res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - break main_flow; - } - // Set application objects path explicitly after the rename - setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); - if(replacingExistingPackage) { - replacePackageLI(pkg, parseFlags, scanMode, - installerPackageName, res); - } else { - installNewPackageLI(pkg, parseFlags, scanMode, - installerPackageName,res); - } - } finally { - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - } + if (systemApp && onSd) { + // Disable updates to system apps on sdcard + Log.w(TAG, "Cannot install updates to system apps on sdcard"); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + return; + } + if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return; + } + // Set application objects path explicitly after the rename + setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); + if(replace) { + replacePackageLI(pkg, parseFlags, scanMode, + installerPackageName, res); + } else { + installNewPackageLI(pkg, parseFlags, scanMode, + installerPackageName,res); } } @@ -5198,7 +5280,6 @@ class PackageManagerService extends IPackageManager.Stub { * persisting settings for later use * sending a broadcast if necessary */ - private boolean deletePackageX(String packageName, boolean sendBroadCast, boolean deleteCodeAndResources, int flags) { PackageRemovedInfo info = new PackageRemovedInfo(); @@ -5290,6 +5371,7 @@ class PackageManagerService extends IPackageManager.Stub { File dataDir = new File(pkg.applicationInfo.dataDir); dataDir.delete(); } + schedulePackageCleaning(packageName); synchronized (mPackages) { if (outInfo != null) { outInfo.removedUid = mSettings.removePackageLP(packageName); @@ -5302,7 +5384,7 @@ class PackageManagerService extends IPackageManager.Stub { mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids); } // Save settings now - mSettings.writeLP (); + mSettings.writeLP(); } } @@ -6745,6 +6827,7 @@ class PackageManagerService extends IPackageManager.Stub { private static final class Settings { private final File mSettingsFilename; private final File mBackupSettingsFilename; + private final File mPackageListFilename; private final HashMap<String, PackageSetting> mPackages = new HashMap<String, PackageSetting>(); // List of replaced system applications @@ -6791,6 +6874,10 @@ class PackageManagerService extends IPackageManager.Stub { final HashMap<String, BasePermission> mPermissionTrees = new HashMap<String, BasePermission>(); + // Packages that have been uninstalled and still need their external + // storage data deleted. + final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); + private final StringBuilder mReadMessages = new StringBuilder(); private static final class PendingPackage extends PackageSettingBase { @@ -6822,6 +6909,7 @@ class PackageManagerService extends IPackageManager.Stub { -1, -1); mSettingsFilename = new File(systemDir, "packages.xml"); mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); + mPackageListFilename = new File(systemDir, "packages.list"); } PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, @@ -7354,6 +7442,14 @@ class PackageManagerService extends IPackageManager.Stub { serializer.endTag(null, "shared-user"); } + if (mPackagesToBeCleaned.size() > 0) { + for (int i=0; i<mPackagesToBeCleaned.size(); i++) { + serializer.startTag(null, "cleaning-package"); + serializer.attribute(null, "name", mPackagesToBeCleaned.get(i)); + serializer.endTag(null, "cleaning-package"); + } + } + serializer.endTag(null, "packages"); serializer.endDocument(); @@ -7369,6 +7465,61 @@ class PackageManagerService extends IPackageManager.Stub { |FileUtils.S_IRGRP|FileUtils.S_IWGRP |FileUtils.S_IROTH, -1, -1); + + // Write package list file now, use a JournaledFile. + // + File tempFile = new File(mPackageListFilename.toString() + ".tmp"); + JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); + + str = new FileOutputStream(journal.chooseForWrite()); + try { + StringBuilder sb = new StringBuilder(); + for (PackageSetting pkg : mPackages.values()) { + ApplicationInfo ai = pkg.pkg.applicationInfo; + String dataPath = ai.dataDir; + boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + + // Avoid any application that has a space in its path + // or that is handled by the system. + if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID) + continue; + + // we store on each line the following information for now: + // + // pkgName - package name + // userId - application-specific user id + // debugFlag - 0 or 1 if the package is debuggable. + // dataPath - path to package's data path + // + // NOTE: We prefer not to expose all ApplicationInfo flags for now. + // + // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS + // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: + // system/core/run-as/run-as.c + // + sb.setLength(0); + sb.append(ai.packageName); + sb.append(" "); + sb.append((int)ai.uid); + sb.append(isDebug ? " 1 " : " 0 "); + sb.append(dataPath); + sb.append("\n"); + str.write(sb.toString().getBytes()); + } + str.flush(); + str.close(); + journal.commit(); + } + catch (Exception e) { + journal.rollback(); + } + + FileUtils.setPermissions(mPackageListFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + return; } catch(XmlPullParserException e) { @@ -7376,7 +7527,7 @@ class PackageManagerService extends IPackageManager.Stub { } catch(java.io.IOException e) { Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); } - // Clean up partially written file + // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename); @@ -7386,7 +7537,7 @@ class PackageManagerService extends IPackageManager.Stub { } void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { + throws java.io.IOException { serializer.startTag(null, "updated-package"); serializer.attribute(null, "name", pkg.name); serializer.attribute(null, "codePath", pkg.codePathString); @@ -7424,7 +7575,7 @@ class PackageManagerService extends IPackageManager.Stub { } void writePackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { + throws java.io.IOException { serializer.startTag(null, "package"); serializer.attribute(null, "name", pkg.name); serializer.attribute(null, "codePath", pkg.codePathString); @@ -7614,6 +7765,11 @@ class PackageManagerService extends IPackageManager.Stub { readPreferredActivitiesLP(parser); } else if(tagName.equals("updated-package")) { readDisabledSysPackageLP(parser); + } else if (tagName.equals("cleaning-package")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + mPackagesToBeCleaned.add(name); + } } else { Log.w(TAG, "Unknown element under <packages>: " + parser.getName()); @@ -7739,7 +7895,7 @@ class PackageManagerService extends IPackageManager.Stub { } private void readDisabledSysPackageLP(XmlPullParser parser) - throws XmlPullParserException, IOException { + throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); String codePathStr = parser.getAttributeValue(null, "codePath"); String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); @@ -8269,17 +8425,17 @@ class PackageManagerService extends IPackageManager.Stub { int rc = mountService.createSecureContainer( pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Failed to create container (%d)", rc)); rc = mountService.destroySecureContainer(pkgName); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Failed to cleanup container (%d)", rc)); return null; } rc = mountService.createSecureContainer( pkgName, mbLen, "vfat", sdEncKey, Process.SYSTEM_UID); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Failed to create container (2nd try) (%d)", rc)); return null; } @@ -8299,7 +8455,7 @@ class PackageManagerService extends IPackageManager.Stub { int rc = getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " rc : " + rc); return null; } @@ -8310,7 +8466,7 @@ class PackageManagerService extends IPackageManager.Stub { private boolean unMountSdDir(String pkgName) { // STOPSHIP unmount directory int rc = getMountService().unmountSecureContainer(pkgName); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to unmount : " + pkgName + " with rc " + rc); return false; } @@ -8334,7 +8490,7 @@ class PackageManagerService extends IPackageManager.Stub { private boolean finalizeSdDir(String pkgName) { int rc = getMountService().finalizeSecureContainer(pkgName); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.i(TAG, "Failed to finalize container for pkg : " + pkgName); return false; } @@ -8343,7 +8499,7 @@ class PackageManagerService extends IPackageManager.Stub { private boolean destroySdDir(String pkgName) { int rc = getMountService().destroySecureContainer(pkgName); - if (rc != MountServiceResultCode.OperationSucceeded) { + if (rc != StorageResultCode.OperationSucceeded) { Log.i(TAG, "Failed to destroy container for pkg : " + pkgName); return false; } @@ -8409,19 +8565,21 @@ class PackageManagerService extends IPackageManager.Stub { } public void updateExternalMediaStatus(final boolean mediaStatus) { - final boolean DEBUG = true; - if (DEBUG) Log.i(TAG, "updateExterMediaStatus::"); - if (mediaStatus == mMediaMounted) { - return; - } - mMediaMounted = mediaStatus; - // Queue up an async operation since the package installation may take a little while. - mHandler.post(new Runnable() { - public void run() { - mHandler.removeCallbacks(this); - updateExternalMediaStatusInner(mediaStatus); + synchronized (mPackages) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + + mediaStatus+", mMediaMounted=" + mMediaMounted); + if (mediaStatus == mMediaMounted) { + return; } - }); + mMediaMounted = mediaStatus; + // Queue up an async operation since the package installation may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + updateExternalMediaStatusInner(mediaStatus); + } + }); + } } void updateExternalMediaStatusInner(boolean mediaStatus) { @@ -8432,9 +8590,6 @@ class PackageManagerService extends IPackageManager.Stub { HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); int uidList[] = new int[list.length]; int num = 0; - for (int i = 0; i < uidList.length; i++) { - uidList[i] = Process.LAST_APPLICATION_UID; - } synchronized (mPackages) { Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD); for (String cid : list) { @@ -8456,24 +8611,35 @@ class PackageManagerService extends IPackageManager.Stub { processCids.put(args, ps.codePathString); int uid = ps.userId; if (uid != -1) { - int idx = Arrays.binarySearch(uidList, uid); - if (idx < 0) { - uidList[-idx] = uid; - num++; - } + uidList[num++] = uid; } } } - int uidArr[] = uidList; - if ((num > 0) && (num < uidList.length)) { + int uidArr[] = null; + if (num > 0) { + // Sort uid list + Arrays.sort(uidList, 0, num); + // Throw away duplicates uidArr = new int[num]; - for (int i = 0; i < num; i++) { - uidArr[i] = uidList[i]; + uidArr[0] = uidList[0]; + int di = 0; + for (int i = 1; i < num; i++) { + if (uidList[i-1] != uidList[i]) { + uidArr[di++] = uidList[i]; + } + } + if (true) { + for (int j = 0; j < num; j++) { + Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]); + } } } if (mediaStatus) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); loadMediaPackages(processCids, uidArr); + startCleaningPackages(); } else { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); unloadMediaPackages(processCids, uidArr); } } @@ -8486,9 +8652,11 @@ class PackageManagerService extends IPackageManager.Stub { Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList.toArray(new String[size])); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr); - String action = mediaStatus ? Intent.ACTION_MEDIA_RESOURCES_AVAILABLE - : Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE; + if (uidArr != null) { + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr); + } + String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE + : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; sendPackageBroadcast(action, null, extras); } } @@ -8497,10 +8665,11 @@ class PackageManagerService extends IPackageManager.Stub { ArrayList<String> pkgList = new ArrayList<String>(); Set<SdInstallArgs> keys = processCids.keySet(); for (SdInstallArgs args : keys) { - String cid = args.cid; String codePath = processCids.get(args); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install pkg : " + + args.cid + " from " + args.cachePath); if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) { - Log.i(TAG, "Failed to install package: " + codePath + " from sdcard"); + Log.e(TAG, "Failed to install package: " + codePath + " from sdcard"); continue; } // Parse package @@ -8511,7 +8680,8 @@ class PackageManagerService extends IPackageManager.Stub { final PackageParser.Package pkg = pp.parsePackage(new File(codePath), codePath, mMetrics, parseFlags); if (pkg == null) { - Log.w(TAG, "Failed to install package : " + cid + " from sd card"); + Log.e(TAG, "Trying to install pkg : " + + args.cid + " from " + args.cachePath); continue; } setApplicationInfoPaths(pkg, codePath, codePath); @@ -8532,22 +8702,24 @@ class PackageManagerService extends IPackageManager.Stub { } } args.doPostInstall(retCode); - pkgList.add(pkg.packageName); } // Send broadcasts first - sendResourcesChangedBroadcast(true, pkgList, uidArr); - Runtime.getRuntime().gc(); - // If something failed do we clean up here or next install? + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(true, pkgList, uidArr); + Runtime.getRuntime().gc(); + // If something failed do we clean up here or next install? + } } void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages"); ArrayList<String> pkgList = new ArrayList<String>(); + ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); Set<SdInstallArgs> keys = processCids.keySet(); for (SdInstallArgs args : keys) { String cid = args.cid; String pkgName = args.getPackageName(); - // STOPSHIP Send broadcast to apps to remove references - // STOPSHIP Unmount package + if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName); // Delete package internally PackageRemovedInfo outInfo = new PackageRemovedInfo(); synchronized (mInstallLock) { @@ -8557,14 +8729,17 @@ class PackageManagerService extends IPackageManager.Stub { pkgList.add(pkgName); } else { Log.e(TAG, "Failed to delete pkg from sdcard : " + pkgName); + failedList.add(args); } } } // Send broadcasts - sendResourcesChangedBroadcast(false, pkgList, uidArr); - Runtime.getRuntime().gc(); + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(false, pkgList, uidArr); + Runtime.getRuntime().gc(); + } // Do clean up. Just unmount - for (SdInstallArgs args : keys) { + for (SdInstallArgs args : failedList) { synchronized (mInstallLock) { args.doPostDeleteLI(false); } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 1e7dd99..1ccae86 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -41,7 +41,7 @@ import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.IMountService; +import android.os.storage.IMountService; import android.os.IPowerManager; import android.os.LocalPowerManager; import android.os.Power; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ee1cc8d..6e9c21b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -273,19 +273,22 @@ class ServerThread extends Thread { } try { - Log.i(TAG, "Notification Manager"); - notification = new NotificationManagerService(context, statusBar, lights); - ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); + /* + * NotificationManagerService is dependant on MountService, + * (for media / usb notifications) so we must start MountService first. + */ + Log.i(TAG, "Mount Service"); + ServiceManager.addService("mount", new MountService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting Notification Manager", e); + Log.e(TAG, "Failure starting Mount Service", e); } try { - // MountService must start after NotificationManagerService - Log.i(TAG, "Mount Service"); - ServiceManager.addService("mount", new MountService(context)); + Log.i(TAG, "Notification Manager"); + notification = new NotificationManagerService(context, statusBar, lights); + ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); } catch (Throwable e) { - Log.e(TAG, "Failure starting Mount Service", e); + Log.e(TAG, "Failure starting Notification Manager", e); } try { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 88aadbd..b57e543 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5465,7 +5465,6 @@ public class WindowManagerService extends IWindowManager.Stub curFocus = mCurrentFocus; // cache the paused state at ctor time as well if (theFocus == null || theFocus.mToken == null) { - Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!"); focusPaused = false; } else { focusPaused = theFocus.mToken.paused; @@ -5478,7 +5477,7 @@ public class WindowManagerService extends IWindowManager.Stub + " fin=" + finished + " gfw=" + gotFirstWindow + " ed=" + eventDispatching + " tts=" + timeToSwitch + " wf=" + wasFrozen + " fp=" + focusPaused - + " mcf=" + mCurrentFocus + "}}"; + + " mcf=" + curFocus + "}}"; } }; private DispatchState mDispatchState = null; @@ -5651,10 +5650,11 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (this) { Log.w(TAG, "Key dispatching timed out sending to " + (targetWin != null ? targetWin.mAttrs.getTitle() - : "<null>")); + : "<null>: no window ready for key dispatch")); // NOSHIP debugging - Log.w(TAG, "Dispatch state: " + mDispatchState); - Log.w(TAG, "Current state: " + new DispatchState(nextKey, targetWin)); + Log.w(TAG, "Previous dispatch state: " + mDispatchState); + Log.w(TAG, "Current dispatch state: " + + new DispatchState(nextKey, targetWin)); // END NOSHIP //dump(); if (targetWin != null) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 5e6881c..98ded37 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -11941,7 +11941,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent.getAction()); if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) || intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) - || Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction()) + || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction()) || uidRemoved) { if (checkComponentPermission( android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, @@ -11960,7 +11960,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else { // If resources are unvailble just force stop all // those packages and flush the attribute cache as well. - if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) { + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); if (list != null && (list.length > 0)) { for (String pkg : list) { @@ -12146,7 +12146,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen skipPackages = new String[] { pkgName }; } } - } else if (intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(intent.getAction())) { + } else if (intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) { skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } if (skipPackages != null && (skipPackages.length > 0)) { diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 55041fb..c7c3335 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -1813,14 +1813,14 @@ public class StatusBarService extends IStatusBar.Stub filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addDataScheme("package"); mContext.registerReceiver(this, filter); - IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(this, sdFilter); } @Override public void onReceive(Context context, Intent intent) { String pkgList[] = null; - if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) { + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } else { Uri data = intent.getData(); diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java index e8d72d9..7d418f0 100644 --- a/test-runner/android/test/SyncBaseInstrumentation.java +++ b/test-runner/android/test/SyncBaseInstrumentation.java @@ -46,7 +46,7 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase { */ protected void syncProvider(Uri uri, String accountName, String authority) throws Exception { Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); Account account = new Account(accountName, "com.google"); ContentResolver.requestSync(account, authority, extras); diff --git a/test-runner/android/test/TouchUtils.java b/test-runner/android/test/TouchUtils.java index 962b2f9..69c6d2d 100644 --- a/test-runner/android/test/TouchUtils.java +++ b/test-runner/android/test/TouchUtils.java @@ -773,7 +773,7 @@ public class TouchUtils { float xStep = (toX - fromX) / stepCount; MotionEvent event = MotionEvent.obtain(downTime, eventTime, - MotionEvent.ACTION_DOWN, fromX, y, 0); + MotionEvent.ACTION_DOWN, x, y, 0); inst.sendPointerSync(event); inst.waitForIdleSync(); @@ -787,7 +787,7 @@ public class TouchUtils { } eventTime = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, fromX, y, 0); + event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); inst.sendPointerSync(event); inst.waitForIdleSync(); } diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index 57b22f8..ffd757c 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -158,11 +158,21 @@ public class MockContext extends Context { } @Override + public File getExternalFilesDir(String type) { + throw new UnsupportedOperationException(); + } + + @Override public File getCacheDir() { throw new UnsupportedOperationException(); } @Override + public File getExternalCacheDir() { + throw new UnsupportedOperationException(); + } + + @Override public File getDir(String name, int mode) { throw new UnsupportedOperationException(); } diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index 70d0e78..2b6b81e 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -28,6 +28,7 @@ <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> + <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" /> <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" /> diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index 163ddd5..3a4d38c 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -16,6 +16,8 @@ package com.android.unit_tests; +import android.os.storage.IMountService.Stub; + import android.net.Uri; import android.os.FileUtils; @@ -34,7 +36,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstallObserver; -import android.content.pm.IPackageStatsObserver; +import android.content.pm.IPackageDeleteObserver; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageStats; @@ -51,15 +53,27 @@ import android.util.DisplayMetrics; import android.util.Log; import android.os.Environment; import android.os.Handler; +import android.os.IBinder; +import android.os.storage.IMountService; +import android.os.storage.StorageResultCode; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; public class PackageManagerTests extends AndroidTestCase { - private static final boolean localLOGV = false; + private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; - public final long MAX_WAIT_TIME=60*1000; - public final long WAIT_TIME_INCR=10*1000; + public final long MAX_WAIT_TIME=120*1000; + public final long WAIT_TIME_INCR=20*1000; + + void failStr(String errMsg) { + Log.w(TAG, "errMsg="+errMsg); + fail(errMsg); + } + void failStr(Exception e) { + Log.w(TAG, "e.getMessage="+e.getMessage()); + Log.w(TAG, "e="+e); + } @Override protected void setUp() throws Exception { @@ -146,6 +160,7 @@ public class PackageManagerTests extends AndroidTestCase { try { // Wait on observer synchronized(observer) { + synchronized (receiver) { getPm().installPackage(packageURI, observer, flags, null); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { @@ -158,7 +173,6 @@ public class PackageManagerTests extends AndroidTestCase { if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) { return false; } - synchronized (receiver) { // Verify we received the broadcast waitTime = 0; while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { @@ -262,6 +276,7 @@ public class PackageManagerTests extends AndroidTestCase { PackageParser.Package pkg = parsePackage(packageURI); assertNotNull(pkg); InstallReceiver receiver = new InstallReceiver(pkg.packageName); + InstallParams ip = null; try { try { assertTrue(invokeInstallPackage(packageURI, flags, @@ -271,13 +286,11 @@ public class PackageManagerTests extends AndroidTestCase { } // Verify installed information assertInstall(pkg.packageName, flags); - return new InstallParams(pkg, outFileName, packageURI); + ip = new InstallParams(pkg, outFileName, packageURI); + return ip; } finally { if (cleanUp) { - getPm().deletePackage(pkg.packageName, null, 0); - if (outFile != null && outFile.exists()) { - outFile.delete(); - } + cleanUpInstall(ip); } } } @@ -300,28 +313,52 @@ public class PackageManagerTests extends AndroidTestCase { /* ------------------------- Test replacing packages --------------*/ class ReplaceReceiver extends GenericReceiver { String pkgName; - boolean removed = false; + final static int INVALID = -1; + final static int REMOVED = 1; + final static int ADDED = 2; + final static int REPLACED = 3; + int removed = INVALID; + // for updated system apps only + boolean update = false; ReplaceReceiver(String pkgName) { this.pkgName = pkgName; filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_REPLACED); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + if (update) { + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); + } + filter.addDataScheme("package"); super.setFilter(filter); } public boolean notifyNow(Intent intent) { String action = intent.getAction(); - if (Intent.ACTION_PACKAGE_REMOVED.equals(action) || - Intent.ACTION_PACKAGE_REPLACED.equals(action)) { - Uri data = intent.getData(); - String installedPkg = data.getEncodedSchemeSpecificPart(); - if (pkgName.equals(installedPkg)) { - if (removed) { - return true; - } else { - removed = true; - } + Uri data = intent.getData(); + String installedPkg = data.getEncodedSchemeSpecificPart(); + if (pkgName == null || !pkgName.equals(installedPkg)) { + return false; + } + if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + removed = REMOVED; + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + if (removed != REMOVED) { + return false; + } + boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (!replacing) { + return false; + } + removed = ADDED; + if (!update) { + return true; } + } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) { + if (removed != ADDED) { + return false; + } + removed = REPLACED; + return true; } return false; } @@ -335,29 +372,26 @@ public class PackageManagerTests extends AndroidTestCase { */ public void replaceFromRawResource(int flags) { InstallParams ip = installFromRawResource(flags, false); - boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0); + boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0); GenericReceiver receiver; - if (result) { + if (replace) { receiver = new ReplaceReceiver(ip.pkg.packageName); + Log.i(TAG, "Creating replaceReceiver"); } else { receiver = new InstallReceiver(ip.pkg.packageName); } try { try { assertEquals(invokeInstallPackage(ip.packageURI, flags, - ip.pkg.packageName, receiver), result); - if (result) { + ip.pkg.packageName, receiver), replace); + if (replace) { assertInstall(ip.pkg.packageName, flags); } } catch (Exception e) { failStr("Failed with exception : " + e); } } finally { - getPm().deletePackage(ip.pkg.packageName, null, 0); - File outFile = new File(ip.outFileName); - if (outFile != null && outFile.exists()) { - outFile.delete(); - } + cleanUpInstall(ip); } } @@ -376,12 +410,362 @@ public class PackageManagerTests extends AndroidTestCase { replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD); } - void failStr(String errMsg) { - Log.w(TAG, "errMsg="+errMsg); - fail(errMsg); + @MediumTest + public void testReplaceNormalInternal() { + replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING); } - void failStr(Exception e) { - Log.w(TAG, "e.getMessage="+e.getMessage()); - Log.w(TAG, "e="+e); + + @MediumTest + public void testReplaceFwdLockedInternal() { + replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING | + PackageManager.INSTALL_FORWARD_LOCK); + } + + @MediumTest + public void testReplaceSdcard() { + replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING | + PackageManager.INSTALL_ON_SDCARD); + } + + /* -------------- Delete tests ---*/ + class DeleteObserver extends IPackageDeleteObserver.Stub { + + public boolean succeeded; + private boolean doneFlag = false; + + public boolean isDone() { + return doneFlag; + } + + public void packageDeleted(boolean succeeded) throws RemoteException { + synchronized(this) { + this.succeeded = succeeded; + doneFlag = true; + notifyAll(); + } + } + } + + class DeleteReceiver extends GenericReceiver { + String pkgName; + + DeleteReceiver(String pkgName) { + this.pkgName = pkgName; + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + super.setFilter(filter); + } + + public boolean notifyNow(Intent intent) { + String action = intent.getAction(); + if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + return false; + } + Uri data = intent.getData(); + String installedPkg = data.getEncodedSchemeSpecificPart(); + if (pkgName.equals(installedPkg)) { + return true; + } + return false; + } + } + + public boolean invokeDeletePackage(Uri packageURI, int flags, + final String pkgName, GenericReceiver receiver) throws Exception { + DeleteObserver observer = new DeleteObserver(); + final boolean received = false; + mContext.registerReceiver(receiver, receiver.filter); + try { + // Wait on observer + synchronized(observer) { + synchronized (receiver) { + getPm().deletePackage(pkgName, observer, flags); + long waitTime = 0; + while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!observer.isDone()) { + throw new Exception("Timed out waiting for packageInstalled callback"); + } + // Verify we received the broadcast + waitTime = 0; + while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + receiver.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!receiver.isDone()) { + throw new Exception("Timed out waiting for PACKAGE_ADDED notification"); + } + return receiver.received; + } + } + } finally { + mContext.unregisterReceiver(receiver); + } + } + + public void deleteFromRawResource(int iFlags, int dFlags) { + InstallParams ip = installFromRawResource(iFlags, false); + boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0); + GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName); + DeleteObserver observer = new DeleteObserver(); + try { + assertTrue(invokeDeletePackage(ip.packageURI, dFlags, + ip.pkg.packageName, receiver)); + ApplicationInfo info = null; + Log.i(TAG, "okay4"); + try { + info = getPm().getApplicationInfo(ip.pkg.packageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException e) { + info = null; + } + if (retainData) { + assertNotNull(info); + assertEquals(info.packageName, ip.pkg.packageName); + File file = new File(info.dataDir); + assertTrue(file.exists()); + } else { + assertNull(info); + } + } catch (Exception e) { + failStr(e); + } finally { + cleanUpInstall(ip); + } + } + + @MediumTest + public void testDeleteNormalInternal() { + deleteFromRawResource(0, 0); + } + + @MediumTest + public void testDeleteFwdLockedInternal() { + deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, 0); + } + + @MediumTest + public void testDeleteSdcard() { + deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, 0); + } + + @MediumTest + public void testDeleteNormalInternalRetainData() { + deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA); + } + + @MediumTest + public void testDeleteFwdLockedInternalRetainData() { + deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA); + } + + @MediumTest + public void testDeleteSdcardRetainData() { + deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, PackageManager.DONT_DELETE_DATA); } + + /* sdcard mount/unmount tests ******/ + + class SdMountReceiver extends GenericReceiver { + String pkgNames[]; + boolean status = true; + + SdMountReceiver(String[] pkgNames) { + this.pkgNames = pkgNames; + IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + super.setFilter(filter); + } + + public boolean notifyNow(Intent intent) { + Log.i(TAG, "okay 1"); + String action = intent.getAction(); + if (!Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + return false; + } + String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + for (String pkg : pkgNames) { + boolean found = false; + for (String rpkg : rpkgList) { + if (rpkg.equals(pkg)) { + found = true; + break; + } + } + if (!found) { + status = false; + return true; + } + } + return true; + } + } + + class SdUnMountReceiver extends GenericReceiver { + String pkgNames[]; + boolean status = true; + + SdUnMountReceiver(String[] pkgNames) { + this.pkgNames = pkgNames; + IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + super.setFilter(filter); + } + + public boolean notifyNow(Intent intent) { + String action = intent.getAction(); + if (!Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + return false; + } + String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + for (String pkg : pkgNames) { + boolean found = false; + for (String rpkg : rpkgList) { + if (rpkg.equals(pkg)) { + found = true; + break; + } + } + if (!found) { + status = false; + return true; + } + } + return true; + } + } + + IMountService getMs() { + IBinder service = ServiceManager.getService("mount"); + if (service != null) { + return IMountService.Stub.asInterface(service); + } else { + Log.e(TAG, "Can't get mount service"); + } + return null; + } + + boolean getMediaState() { + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + String state = getMs().getVolumeState(mPath); + return Environment.MEDIA_MOUNTED.equals(state); + } catch (RemoteException e) { + return false; + } + } + + boolean mountMedia() { + if (getMediaState()) { + return true; + } + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + int ret = getMs().mountVolume(mPath); + return ret == StorageResultCode.OperationSucceeded; + } catch (RemoteException e) { + return false; + } + } + + private boolean unmountMedia() { + if (!getMediaState()) { + return true; + } + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + int ret = getMs().unmountVolume(mPath); + return ret == StorageResultCode.OperationSucceeded; + } catch (RemoteException e) { + return true; + } + } + + /* + * Install package on sdcard. Unmount and then mount the media. + * (Use PackageManagerService private api for now) + * Make sure the installed package is available. + * STOPSHIP will uncomment when MountService api's to mount/unmount + * are made asynchronous. + */ + public void xxxtestMountSdNormalInternal() { + assertTrue(mountFromRawResource()); + } + + private boolean mountFromRawResource() { + // Install pkg on sdcard + InstallParams ip = installFromRawResource(PackageManager.INSTALL_ON_SDCARD | + PackageManager.INSTALL_REPLACE_EXISTING, false); + if (localLOGV) Log.i(TAG, "Installed pkg on sdcard"); + boolean origState = getMediaState(); + SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName}); + try { + if (localLOGV) Log.i(TAG, "Unmounting media"); + // Unmount media + assertTrue(unmountMedia()); + if (localLOGV) Log.i(TAG, "Unmounted media"); + try { + if (localLOGV) Log.i(TAG, "Sleeping for 10 second"); + Thread.sleep(10*1000); + } catch (InterruptedException e) { + failStr(e); + } + // Register receiver here + PackageManager pm = getPm(); + mContext.registerReceiver(receiver, receiver.filter); + + // Wait on receiver + synchronized (receiver) { + if (localLOGV) Log.i(TAG, "Mounting media"); + // Mount media again + assertTrue(mountMedia()); + if (localLOGV) Log.i(TAG, "Mounted media"); + if (localLOGV) Log.i(TAG, "Waiting for notification"); + long waitTime = 0; + // Verify we received the broadcast + waitTime = 0; + while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + receiver.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!receiver.isDone()) { + failStr("Timed out waiting for EXTERNAL_APPLICATIONS notification"); + } + return receiver.received; + } + } catch (InterruptedException e) { + failStr(e); + return false; + } finally { + mContext.unregisterReceiver(receiver); + // Restore original media state + if (origState) { + mountMedia(); + } else { + unmountMedia(); + } + if (localLOGV) Log.i(TAG, "Cleaning up install"); + cleanUpInstall(ip); + } + } + + void cleanUpInstall(InstallParams ip) { + if (ip == null) { + return; + } + Runtime.getRuntime().gc(); + Log.i(TAG, "Deleting package : " + ip.pkg.packageName); + getPm().deletePackage(ip.pkg.packageName, null, 0); + File outFile = new File(ip.outFileName); + if (outFile != null && outFile.exists()) { + outFile.delete(); + } + } + /* + * TODO's + * check version numbers for upgrades + * check permissions of installed packages + * how to do tests on updated system apps? + * verify updates to system apps cannot be installed on the sdcard. + */ } diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index cbb5203..558b587 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -39,7 +39,7 @@ public: mRequireLocalization(false), mPseudolocalize(false), mUTF8(false), mEncodingSpecified(false), mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL), - mAssetSourceDir(NULL), mProguardFile(NULL), + mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), @@ -90,6 +90,8 @@ public: void setOutputAPKFile(const char* val) { mOutputAPKFile = val; } const char* getManifestPackageNameOverride() const { return mManifestPackageNameOverride; } void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; } + bool getAutoAddOverlay() { return mAutoAddOverlay; } + void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; } /* * Input options. @@ -181,6 +183,7 @@ private: bool mJunkPath; const char* mOutputAPKFile; const char* mManifestPackageNameOverride; + bool mAutoAddOverlay; const char* mAssetSourceDir; const char* mProguardFile; const char* mAndroidManifestFile; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 6675ac2..6d0a351 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -60,6 +60,7 @@ void usage(void) " [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n" " [--max-sdk-version VAL] [--app-version VAL] \\\n" " [--app-version-name TEXT] [--custom-package VAL] [--utf16] \\\n" + " [--auto-add-overlay] \\\n" " [-I base-package [-I base-package ...]] \\\n" " [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " @@ -136,6 +137,8 @@ void usage(void) " inserts android:versionName in to manifest.\n" " --custom-package\n" " generates R.java into a different package.\n" + " --auto-add-overlay\n" + " Automatically add resources that are only in overlays.\n" " --utf16\n" " changes default encoding for resources to UTF-16. Only useful when API\n" " level is set to 7 or higher where the default encoding is UTF-8.\n"); @@ -445,6 +448,8 @@ int main(int argc, char* const argv[]) goto bail; } bundle.setManifestPackageNameOverride(argv[0]); + } else if (strcmp(cp, "-auto-add-overlay") == 0) { + bundle.setAutoAddOverlay(true); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index a9cbd11..b682702 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -1591,7 +1591,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - if (overlay && !hasBagOrEntry(package, type, name)) { + if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) { bool canAdd = false; sp<Package> p = mPackages.valueFor(package); if (p != NULL) { @@ -3279,12 +3279,13 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, const SourcePos& sourcePos, const ResTable_config* config, bool doSetIndex, - bool overlay) + bool overlay, + bool autoAddOverlay) { int pos = -1; sp<ConfigList> c = mConfigs.valueFor(entry); if (c == NULL) { - if (overlay == true && mCanAddEntries.indexOf(entry) < 0) { + if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) { sourcePos.error("Resource at %s appears in overlay but not" " in the base package; use <add-resource> to add.\n", String8(entry).string()); @@ -3596,7 +3597,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package, if (t == NULL) { return NULL; } - return t->getEntry(name, sourcePos, config, doSetIndex, overlay); + return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay()); } sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID, diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index cfa75a71..60d0901 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -425,7 +425,8 @@ public: const SourcePos& pos, const ResTable_config* config = NULL, bool doSetIndex = false, - bool overlay = false); + bool overlay = false, + bool autoAddOverlay = false); const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; } 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 b670eee..57b5d4e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -928,6 +928,12 @@ public final class BridgeContext extends Context { } @Override + public File getExternalCacheDir() { + // TODO Auto-generated method stub + return null; + } + + @Override public ContentResolver getContentResolver() { if (mContentResolver == null) { mContentResolver = new BridgeContentResolver(this); @@ -960,6 +966,12 @@ public final class BridgeContext extends Context { } @Override + public File getExternalFilesDir(String type) { + // TODO Auto-generated method stub + return null; + } + + @Override public String getPackageCodePath() { // TODO Auto-generated method stub return null; |