Merge "fixes for [2474091] Saw Poor behaviour playing a video."
diff --git a/Android.mk b/Android.mk
index 8a8678d..35ee3fd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -93,9 +93,9 @@
core/java/android/app/IWallpaperManager.aidl \
core/java/android/app/IWallpaperManagerCallback.aidl \
core/java/android/app/admin/IDevicePolicyManager.aidl \
- core/java/android/backup/IBackupManager.aidl \
- core/java/android/backup/IRestoreObserver.aidl \
- core/java/android/backup/IRestoreSession.aidl \
+ core/java/android/app/backup/IBackupManager.aidl \
+ core/java/android/app/backup/IRestoreObserver.aidl \
+ core/java/android/app/backup/IRestoreSession.aidl \
core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothCallback.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..b6b8640
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,6 @@
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app)
diff --git a/api/current.xml b/api/current.xml
index 28a7168..2e434af 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19916,83 +19916,6 @@
>
</method>
</class>
-<class name="BackupAgent"
- extends="android.content.ContextWrapper"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BackupAgent"
- type="android.app.BackupAgent"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onBackup"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="onCreate"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onDestroy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onRestore"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInput">
-</parameter>
-<parameter name="appVersionCode" type="int">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-</class>
<class name="DatePickerDialog"
extends="android.app.AlertDialog"
abstract="false"
@@ -24220,6 +24143,17 @@
visibility="public"
>
</field>
+<field name="CURSOR_EXTRA_KEY_IN_PROGRESS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""in_progress""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_DATA_KEY"
type="java.lang.String"
transient="false"
@@ -24473,6 +24407,17 @@
visibility="public"
>
</field>
+<field name="SUGGEST_COLUMN_TEXT_2_URL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""suggest_text_2_url""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_MIME_TYPE"
type="java.lang.String"
transient="false"
@@ -25542,6 +25487,17 @@
visibility="public"
>
</method>
+<method name="getCurrentModeType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNightMode"
return="int"
abstract="false"
@@ -25566,29 +25522,58 @@
<parameter name="mode" type="int">
</parameter>
</method>
-<field name="MODE_AUTO"
+<field name="ACTION_ENTER_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_ENTER_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NIGHT_AUTO"
type="int"
transient="false"
volatile="false"
- value="3"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="MODE_NIGHT"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MODE_NOTNIGHT"
+<field name="MODE_NIGHT_NO"
type="int"
transient="false"
volatile="false"
@@ -25599,6 +25584,17 @@
visibility="public"
>
</field>
+<field name="MODE_NIGHT_YES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="WallpaperInfo"
extends="java.lang.Object"
@@ -26901,6 +26897,692 @@
</field>
</class>
</package>
+<package name="android.app.backup"
+>
+<class name="AbsoluteFileBackupHelper"
+ extends="android.app.backup.FileBackupHelperBase"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.app.backup.BackupHelper">
+</implements>
+<constructor name="AbsoluteFileBackupHelper"
+ type="android.app.backup.AbsoluteFileBackupHelper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="files" type="java.lang.String...">
+</parameter>
+</constructor>
+<method name="performBackup"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+<method name="restoreEntity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInputStream">
+</parameter>
+</method>
+</class>
+<class name="BackupAgent"
+ extends="android.content.ContextWrapper"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BackupAgent"
+ type="android.app.backup.BackupAgent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onBackup"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="onCreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDestroy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRestore"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInput">
+</parameter>
+<parameter name="appVersionCode" type="int">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BackupDataInput"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getDataSize"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getKey"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readEntityData"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="size" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="readNextHeader"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="skipEntityData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BackupDataInputStream"
+ extends="java.io.InputStream"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getKey"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="read"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="size"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="BackupDataOutput"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="setKeyPrefix"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyPrefix" type="java.lang.String">
+</parameter>
+</method>
+<method name="writeEntityData"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="size" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeEntityHeader"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="dataSize" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="OP_DELETE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OP_UPDATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="BackupHelper"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="performBackup"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+<method name="restoreEntity"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInputStream">
+</parameter>
+</method>
+<method name="writeNewStateDescription"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+</interface>
+<class name="BackupHelperAgent"
+ extends="android.app.backup.BackupAgent"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BackupHelperAgent"
+ type="android.app.backup.BackupHelperAgent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addHelper"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyPrefix" type="java.lang.String">
+</parameter>
+<parameter name="helper" type="android.app.backup.BackupHelper">
+</parameter>
+</method>
+<method name="onBackup"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="onRestore"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInput">
+</parameter>
+<parameter name="appVersionCode" type="int">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BackupManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BackupManager"
+ type="android.app.backup.BackupManager"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="beginRestoreSession"
+ return="android.app.backup.RestoreSession"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
+</class>
+<class name="FileBackupHelper"
+ extends="android.app.backup.FileBackupHelperBase"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.app.backup.BackupHelper">
+</implements>
+<constructor name="FileBackupHelper"
+ type="android.app.backup.FileBackupHelper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="files" type="java.lang.String...">
+</parameter>
+</constructor>
+<method name="performBackup"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+<method name="restoreEntity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInputStream">
+</parameter>
+</method>
+</class>
+<class name="FileBackupHelperBase"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility=""
+>
+<method name="writeNewStateDescription"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+</class>
+<class name="RestoreObserver"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="RestoreObserver"
+ type="android.app.backup.RestoreObserver"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<class name="RestoreSession"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="endRestoreSession"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="restorePackage"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="observer" type="android.app.backup.RestoreObserver">
+</parameter>
+</method>
+</class>
+<class name="SharedPreferencesBackupHelper"
+ extends="android.app.backup.FileBackupHelperBase"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.app.backup.BackupHelper">
+</implements>
+<constructor name="SharedPreferencesBackupHelper"
+ type="android.app.backup.SharedPreferencesBackupHelper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="prefGroups" type="java.lang.String...">
+</parameter>
+</constructor>
+<method name="performBackup"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldState" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="data" type="android.app.backup.BackupDataOutput">
+</parameter>
+<parameter name="newState" type="android.os.ParcelFileDescriptor">
+</parameter>
+</method>
+<method name="restoreEntity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.app.backup.BackupDataInputStream">
+</parameter>
+</method>
+</class>
+</package>
<package name="android.appwidget"
>
<class name="AppWidgetHost"
@@ -27646,615 +28328,6 @@
</field>
</class>
</package>
-<package name="android.backup"
->
-<class name="AbsoluteFileBackupHelper"
- extends="android.backup.FileBackupHelperBase"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.backup.BackupHelper">
-</implements>
-<constructor name="AbsoluteFileBackupHelper"
- type="android.backup.AbsoluteFileBackupHelper"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="files" type="java.lang.String...">
-</parameter>
-</constructor>
-<method name="performBackup"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-<method name="restoreEntity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInputStream">
-</parameter>
-</method>
-</class>
-<class name="BackupDataInput"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getDataSize"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getKey"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="readEntityData"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="byte[]">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-<parameter name="size" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="readNextHeader"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="skipEntityData"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-</class>
-<class name="BackupDataInputStream"
- extends="java.io.InputStream"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getKey"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="read"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="size"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<class name="BackupDataOutput"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="setKeyPrefix"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="keyPrefix" type="java.lang.String">
-</parameter>
-</method>
-<method name="writeEntityData"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="byte[]">
-</parameter>
-<parameter name="size" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="writeEntityHeader"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="java.lang.String">
-</parameter>
-<parameter name="dataSize" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<field name="OP_DELETE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OP_UPDATE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="BackupHelper"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="performBackup"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-<method name="restoreEntity"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInputStream">
-</parameter>
-</method>
-<method name="writeNewStateDescription"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fd" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-</interface>
-<class name="BackupHelperAgent"
- extends="android.app.BackupAgent"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BackupHelperAgent"
- type="android.backup.BackupHelperAgent"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="addHelper"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="keyPrefix" type="java.lang.String">
-</parameter>
-<parameter name="helper" type="android.backup.BackupHelper">
-</parameter>
-</method>
-<method name="onBackup"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="onRestore"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInput">
-</parameter>
-<parameter name="appVersionCode" type="int">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-</class>
-<class name="BackupManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BackupManager"
- type="android.backup.BackupManager"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</constructor>
-<method name="beginRestoreSession"
- return="android.backup.RestoreSession"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="dataChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="dataChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-</method>
-</class>
-<class name="FileBackupHelper"
- extends="android.backup.FileBackupHelperBase"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.backup.BackupHelper">
-</implements>
-<constructor name="FileBackupHelper"
- type="android.backup.FileBackupHelper"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="files" type="java.lang.String...">
-</parameter>
-</constructor>
-<method name="performBackup"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-<method name="restoreEntity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInputStream">
-</parameter>
-</method>
-</class>
-<class name="FileBackupHelperBase"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility=""
->
-<method name="writeNewStateDescription"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fd" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-</class>
-<class name="RestoreObserver"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="RestoreObserver"
- type="android.backup.RestoreObserver"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-</class>
-<class name="RestoreSession"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="endRestoreSession"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="restorePackage"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-<parameter name="observer" type="android.backup.RestoreObserver">
-</parameter>
-</method>
-</class>
-<class name="SharedPreferencesBackupHelper"
- extends="android.backup.FileBackupHelperBase"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.backup.BackupHelper">
-</implements>
-<constructor name="SharedPreferencesBackupHelper"
- type="android.backup.SharedPreferencesBackupHelper"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="prefGroups" type="java.lang.String...">
-</parameter>
-</constructor>
-<method name="performBackup"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-<method name="restoreEntity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.backup.BackupDataInputStream">
-</parameter>
-</method>
-</class>
-</package>
<package name="android.bluetooth"
>
<class name="BluetoothAdapter"
@@ -39046,17 +39119,6 @@
visibility="public"
>
</field>
-<field name="EXTRA_CAR_MODE_ENABLED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.extra.CAR_MODE_ENABLED""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="EXTRA_CC"
type="java.lang.String"
transient="false"
@@ -39211,17 +39273,6 @@
visibility="public"
>
</field>
-<field name="EXTRA_PHYSICAL_DOCK_STATE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.extra.PHYSICAL_DOCK_STATE""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="EXTRA_REMOTE_INTENT_TOKEN"
type="java.lang.String"
transient="false"
@@ -47889,6 +47940,17 @@
type="int"
transient="false"
volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UI_MODE_TYPE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
value="2"
static="true"
final="true"
@@ -71459,6 +71521,19 @@
visibility="public"
>
</method>
+<method name="reconnect"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
<method name="release"
return="void"
abstract="false"
@@ -72346,6 +72421,19 @@
<parameter name="longitude" type="double">
</parameter>
</method>
+<method name="setGpsProcessingMethod"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="processing_method" type="java.lang.String">
+</parameter>
+</method>
<method name="setGpsTimestamp"
return="void"
abstract="false"
@@ -78061,75 +78149,6 @@
</exception>
</method>
</class>
-<class name="GeocoderParams"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getClientPackage"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocale"
- return="java.util.Locale"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parcel" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
<class name="GpsSatellite"
extends="java.lang.Object"
abstract="false"
@@ -82184,6 +82203,210 @@
</parameter>
</method>
</interface>
+<class name="CamcorderProfile"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="get"
+ return="android.media.CamcorderProfile"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="quality" type="android.media.CamcorderProfile.Quality">
+</parameter>
+</method>
+<method name="getMmsRecordingDurationInSeconds"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="mAudioBitRate"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mAudioChannels"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mAudioCodec"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mAudioSampleRate"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mFileFormat"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mQuality"
+ type="android.media.CamcorderProfile.Quality"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVideoBitRate"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVideoCodec"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVideoFrameHeight"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVideoFrameRate"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mVideoFrameWidth"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="CamcorderProfile.Quality"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.media.CamcorderProfile.Quality"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.media.CamcorderProfile.Quality[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="CameraProfile"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="CameraProfile"
+ type="android.media.CameraProfile"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getImageEncodingQualityLevels"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
<class name="ExifInterface"
extends="java.lang.Object"
abstract="false"
@@ -83890,6 +84113,19 @@
visibility="public"
>
</method>
+<method name="setAudioChannels"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="numChannels" type="int">
+</parameter>
+</method>
<method name="setAudioEncoder"
return="void"
abstract="false"
@@ -83905,6 +84141,32 @@
<exception name="IllegalStateException" type="java.lang.IllegalStateException">
</exception>
</method>
+<method name="setAudioEncodingBitRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bitRate" type="int">
+</parameter>
+</method>
+<method name="setAudioSamplingRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="samplingRate" type="int">
+</parameter>
+</method>
<method name="setAudioSource"
return="void"
abstract="false"
@@ -84062,6 +84324,19 @@
<exception name="IllegalStateException" type="java.lang.IllegalStateException">
</exception>
</method>
+<method name="setVideoEncodingBitRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bitRate" type="int">
+</parameter>
+</method>
<method name="setVideoFrameRate"
return="void"
abstract="false"
@@ -85543,7 +85818,7 @@
type="int"
transient="false"
volatile="false"
- value="4"
+ value="2"
static="true"
final="true"
deprecated="not deprecated"
@@ -85561,7 +85836,7 @@
visibility="public"
>
</field>
-<field name="TARGET_SIZE_NORMAL_THUMBNAIL"
+<field name="TARGET_SIZE_MINI_THUMBNAIL"
type="int"
transient="false"
volatile="false"
@@ -114026,6 +114301,23 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="dumpService"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+</method>
<method name="enableEmulatorTraceOutput"
return="void"
abstract="false"
@@ -175191,6 +175483,17 @@
visibility="public"
>
</method>
+<method name="getOverscrollMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPaddingBottom"
return="int"
abstract="false"
@@ -177356,6 +177659,19 @@
<parameter name="l" type="android.view.View.OnTouchListener">
</parameter>
</method>
+<method name="setOverscrollMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overscrollMode" type="int">
+</parameter>
+</method>
<method name="setPadding"
return="void"
abstract="false"
@@ -177978,6 +178294,39 @@
visibility="public"
>
</field>
+<field name="OVERSCROLL_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_IF_CONTENT_SCROLLS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
type="int[]"
transient="false"
@@ -203732,6 +204081,17 @@
synchronized="false"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemIds"
+ return="long[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
diff --git a/calendar/Android.mk b/calendar/Android.mk
deleted file mode 100644
index 7f9ee0a..0000000
--- a/calendar/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2009 Google, Inc.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := calendar
-LOCAL_SRC_FILES := \
- ../core/java/android/provider/Calendar.java \
- ../core/java/android/pim/EventRecurrence.java \
- ../core/java/android/pim/ICalendar.java \
- ../core/java/android/pim/RecurrenceSet.java \
- ../core/java/android/pim/ContactsAsyncHelper.java \
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Include this library in the build server's output directory
-$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):calendar.jar)
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 7e81e90..00bd54e 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -1291,8 +1291,26 @@
client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
}
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 60000;
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
status_t CameraService::dump(int fd, const Vector<String16>& args)
{
+ static const char* kDeadlockedString = "CameraService may be deadlocked\n";
+
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
@@ -1304,7 +1322,13 @@
result.append(buffer);
write(fd, result.string(), result.size());
} else {
- AutoMutex lock(&mServiceLock);
+ bool locked = tryLock(mServiceLock);
+ // failed to lock - CameraService is probably deadlocked
+ if (!locked) {
+ String8 result(kDeadlockedString);
+ write(fd, result.string(), result.size());
+ }
+
if (mClient != 0) {
sp<Client> currentClient = mClient.promote();
sprintf(buffer, "Client (%p) PID: %d\n",
@@ -1317,6 +1341,8 @@
result.append("No camera client yet.\n");
write(fd, result.string(), result.size());
}
+
+ if (locked) mServiceLock.unlock();
}
return NO_ERROR;
}
diff --git a/cleanspec.mk b/cleanspec.mk
deleted file mode 100644
index 683e303..0000000
--- a/cleanspec.mk
+++ /dev/null
@@ -1 +0,0 @@
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java)
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 7e9fd61..d040b44 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -16,10 +16,10 @@
package com.android.commands.bmgr;
-import android.backup.IBackupManager;
-import android.backup.IRestoreObserver;
-import android.backup.IRestoreSession;
-import android.backup.RestoreSet;
+import android.app.backup.RestoreSet;
+import android.app.backup.IBackupManager;
+import android.app.backup.IRestoreObserver;
+import android.app.backup.IRestoreSession;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -311,7 +311,7 @@
doRestorePackage(arg);
} else {
try {
- long token = Long.parseLong(nextArg(), 16);
+ long token = Long.parseLong(arg, 16);
doRestoreAll(token);
} catch (NumberFormatException e) {
showUsage();
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index adec5a4..eaa34c8 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -110,6 +110,9 @@
dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+ run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
+ run_command("MOUNTED FILESYSTEMS", 10, "df", NULL);
+
run_command("PROCESSES", 10, "ps", "-P", NULL);
run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
run_command("LIBRANK", 10, "librank", NULL);
@@ -208,7 +211,7 @@
}
/* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
+ gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT };
setgroups(sizeof(groups)/sizeof(groups[0]), groups);
setuid(AID_SHELL);
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index a447f53..c8d1397 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -674,14 +674,19 @@
return 0;
}
-void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid)
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+ struct stat* statbuf)
{
while (path[basepos] != 0) {
if (path[basepos] == '/') {
path[basepos] = 0;
- LOGI("Making directory: %s\n", path);
- if (mkdir(path, mode) == 0) {
- chown(path, uid, gid);
+ if (lstat(path, statbuf) < 0) {
+ LOGI("Making directory: %s\n", path);
+ if (mkdir(path, mode) == 0) {
+ chown(path, uid, gid);
+ } else {
+ LOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+ }
}
path[basepos] = '/';
basepos++;
@@ -690,8 +695,8 @@
}
}
-int movefileordir(char* srcpath, char* dstpath, int dstuid, int dstgid,
- struct stat* statbuf)
+int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
+ int dstuid, int dstgid, struct stat* statbuf)
{
DIR *d;
struct dirent *de;
@@ -706,8 +711,9 @@
}
if ((statbuf->st_mode&S_IFDIR) == 0) {
+ mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH,
+ dstuid, dstgid, statbuf);
LOGI("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid);
- mkinnerdirs(dstpath, dstend-1, S_IRWXU|S_IRWXG|S_IXOTH, dstuid, dstgid);
if (rename(srcpath, dstpath) >= 0) {
if (chown(dstpath, dstuid, dstgid) < 0) {
LOGE("cannot chown %s: %s\n", dstpath, strerror(errno));
@@ -752,7 +758,7 @@
strcpy(srcpath+srcend+1, name);
strcpy(dstpath+dstend+1, name);
- if (movefileordir(srcpath, dstpath, dstuid, dstgid, statbuf) != 0) {
+ if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) {
res = 1;
}
@@ -834,7 +840,9 @@
LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) &&
!create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) {
- movefileordir(srcpath, dstpath, dstuid, dstgid, &s);
+ movefileordir(srcpath, dstpath,
+ strlen(dstpath)-strlen(buf+bufp),
+ dstuid, dstgid, &s);
}
}
} else {
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
index 0e7e1ae..8330f8e 100644
--- a/cmds/keystore/keystore_get.h
+++ b/cmds/keystore/keystore_get.h
@@ -19,7 +19,6 @@
#include <stdio.h>
#include <stdint.h>
-#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -28,18 +27,21 @@
#define KEYSTORE_MESSAGE_SIZE 65535
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* This function is provided for native components to get values from keystore.
* Users are required to link against libcutils. The lengths of keys and values
* are limited to KEYSTORE_MESSAGE_SIZE. This function returns the length of
* the requested value or -1 if something goes wrong. */
-static int keystore_get(const char *key, char *value)
+static int keystore_get(const char *key, int length, char *value)
{
- int length = strlen(key);
uint8_t bytes[2] = {length >> 8, length};
uint8_t code = 'g';
int sock;
- if (length > KEYSTORE_MESSAGE_SIZE) {
+ if (length < 0 || length > KEYSTORE_MESSAGE_SIZE) {
return -1;
}
sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -66,4 +68,8 @@
return length;
}
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index d6c76a2..d2b3bc7 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -19,6 +19,7 @@
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.XmlSerializerAndParser;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.Context;
import android.util.AttributeSet;
@@ -47,8 +48,9 @@
AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
}
- public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
- TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+ public AuthenticatorDescription parseServiceAttributes(Resources res,
+ String packageName, AttributeSet attrs) {
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AccountAuthenticator);
try {
final String accountType =
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index e2263fc..1bb1d0f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -704,6 +704,15 @@
throws OperationCanceledException, IOException, AuthenticatorException {
Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
null /* handler */).getResult();
+ if (bundle == null) {
+ // This should never happen, but it does, occasionally. If it does return null to
+ // signify that we were not able to get the authtoken.
+ // TODO: remove this when the bug is found that sometimes causes a null bundle to be
+ // returned
+ Log.e(TAG, "blockingGetAuthToken: null was returned from getResult() for "
+ + account + ", authTokenType " + authTokenType);
+ return null;
+ }
return bundle.getString(KEY_AUTHTOKEN);
}
@@ -1166,6 +1175,16 @@
return this;
}
+ protected void set(Bundle bundle) {
+ // TODO: somehow a null is being set as the result of the Future. Log this
+ // case to help debug where this is occurring. When this bug is fixed this
+ // condition statement should be removed.
+ if (bundle == null) {
+ Log.e(TAG, "the bundle must not be null", new Exception());
+ }
+ super.set(bundle);
+ }
+
public abstract void doWork() throws RemoteException;
private Bundle internalGetResult(Long timeout, TimeUnit unit)
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0756c71..7c49bb7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,7 @@
package android.app;
+import android.app.backup.BackupAgent;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
import android.content.ComponentName;
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
index db198ad..dcfa63f 100644
--- a/core/java/android/app/FullBackupAgent.java
+++ b/core/java/android/app/FullBackupAgent.java
@@ -1,8 +1,9 @@
package android.app;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
-import android.backup.FileBackupHelper;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FileBackupHelper;
import android.os.ParcelFileDescriptor;
import android.util.Log;
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 0de6ad9..fed2bc5 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -16,13 +16,13 @@
package android.app;
-import android.backup.IBackupManager;
+import android.app.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
/**
* Interface presented by applications being asked to participate in the
- * backup & restore mechanism. End user code does not typically implement
- * this interface; they subclass BackupAgent instead.
+ * backup & restore mechanism. End user code will not typically implement
+ * this interface directly; they subclass BackupAgent instead.
*
* {@hide}
*/
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 6ac8a2a..bd637b7 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -33,6 +33,11 @@
void disableCarMode();
/**
+ * Return the current running mode.
+ */
+ int getCurrentModeType();
+
+ /**
* Sets the night mode.
* The mode can be one of:
* 1 - notnight mode
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index af68689..3e426f5 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -464,15 +464,14 @@
}
}
- /**
- * Update the text in the search button. Note: This is deprecated functionality, for
- * 1.0 compatibility only.
- */
- private void updateSearchButton() {
+ private void updateSearchButton() {
String textLabel = null;
Drawable iconLabel = null;
int textId = mSearchable.getSearchButtonText();
- if (textId != 0) {
+ if (isBrowserSearch()){
+ iconLabel = getContext().getResources()
+ .getDrawable(com.android.internal.R.drawable.ic_btn_search_play);
+ } else if (textId != 0) {
textLabel = mActivityContext.getResources().getString(textId);
} else {
iconLabel = getContext().getResources().
@@ -483,10 +482,6 @@
}
private void updateSearchAppIcon() {
- // In Donut, we special-case the case of the browser to hide the app icon as if it were
- // global search, for extra space for url entry.
- //
- // TODO: Remove this special case once the issue has been reconciled in Eclair.
if (isBrowserSearch()) {
mAppIcon.setImageResource(0);
mAppIcon.setVisibility(View.GONE);
@@ -581,10 +576,16 @@
}
mVoiceButton.setVisibility(visibility);
}
-
+
+ /** Called by SuggestionsAdapter when the cursor contents changed. */
+ void onDataSetChanged() {
+ if (mSearchAutoComplete != null && mSuggestionsAdapter != null) {
+ mSearchAutoComplete.onFilterComplete(mSuggestionsAdapter.getCount());
+ }
+ }
+
/**
- * Hack to determine whether this is the browser, so we can remove the browser icon
- * to the left of the search field.
+ * Hack to determine whether this is the browser, so we can adjust the UI.
*/
private boolean isBrowserSearch() {
return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
@@ -667,7 +668,7 @@
// The user changed the query, remember it.
mUserQuery = s == null ? "" : s.toString();
}
- updateVoiceButton(TextUtils.isEmpty(s));
+ updateVoiceButton(mSearchAutoComplete.isEmpty());
}
public void afterTextChanged(Editable s) {
@@ -690,13 +691,30 @@
};
/**
- * Enable/Disable the cancel button based on edit text state (any text?)
+ * Enable/Disable the go button based on edit text state (any text?)
*/
private void updateWidgetState() {
// enable the button if we have one or more non-space characters
boolean enabled = !mSearchAutoComplete.isEmpty();
- mGoButton.setEnabled(enabled);
- mGoButton.setFocusable(enabled);
+ if (isBrowserSearch()) {
+ // In the browser, we hide the search button when there is no text
+ if (enabled) {
+ mSearchAutoComplete.setBackgroundResource(
+ com.android.internal.R.drawable.textfield_search);
+ mGoButton.setVisibility(View.VISIBLE);
+ // Just to be sure
+ mGoButton.setEnabled(true);
+ mGoButton.setFocusable(true);
+ } else {
+ mSearchAutoComplete.setBackgroundResource(
+ com.android.internal.R.drawable.textfield_search_empty);
+ mGoButton.setVisibility(View.GONE);
+ }
+ } else {
+ // Elsewhere we just disable the button
+ mGoButton.setEnabled(enabled);
+ mGoButton.setFocusable(enabled);
+ }
}
/**
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index b54e53d..0ed572a 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1300,6 +1300,14 @@
public final static String EXTRA_SELECT_QUERY = "select_query";
/**
+ * Boolean extra data key for a suggestion provider to return in {@link Cursor#getExtras} to
+ * indicate that the search is not complete yet. This can be used by the search UI
+ * to indicate that a search is in progress. The suggestion provider can return partial results
+ * this way and send a change notification on the cursor when more results are available.
+ */
+ public final static String CURSOR_EXTRA_KEY_IN_PROGRESS = "in_progress";
+
+ /**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
* to obtain the action message that was defined for a particular search action key and/or
@@ -1355,6 +1363,16 @@
* a much smaller appearance.
*/
public final static String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> This is a URL that will be shown
+ * as the second line of text instead of {@link #SUGGEST_COLUMN_TEXT_2}. This is a separate
+ * column so that the search UI knows to display the text as a URL, e.g. by using a different
+ * color. If this column is absent, or has the value {@code null},
+ * {@link #SUGGEST_COLUMN_TEXT_2} will be used instead.
+ */
+ public final static String SUGGEST_COLUMN_TEXT_2_URL = "suggest_text_2_url";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
* then all suggestions will be provided in a format that includes space for two small icons,
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 57795d1..cb947b1 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -16,6 +16,8 @@
package android.app;
+import com.android.internal.R;
+
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -23,16 +25,21 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
-import android.text.Html;
+import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
import android.util.Log;
import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
@@ -64,14 +71,17 @@
private SparseArray<Drawable.ConstantState> mBackgroundsCache;
private boolean mClosed = false;
+ // URL color
+ private ColorStateList mUrlColor;
+
// Cached column indexes, updated when the cursor changes.
- private int mFormatCol;
private int mText1Col;
private int mText2Col;
+ private int mText2UrlCol;
private int mIconName1Col;
private int mIconName2Col;
private int mBackgroundColorCol;
-
+
static final int NONE = -1;
private final Runnable mStartSpinnerRunnable;
@@ -112,7 +122,7 @@
mSearchDialog.setWorking(false);
}
};
-
+
// delay 500ms when deleting
getFilter().setDelayer(new Filter.Delayer() {
@@ -120,7 +130,7 @@
public long getPostingDelay(CharSequence constraint) {
if (constraint == null) return 0;
-
+
long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
mPreviousLength = constraint.length();
return delay;
@@ -150,19 +160,23 @@
* for in app search we show the progress spinner until the cursor is returned with
* the results.
*/
+ Cursor cursor = null;
mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
try {
- final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
+ cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
// trigger fill window so the spinner stays up until the results are copied over and
// closer to being ready
- if (cursor != null) cursor.getCount();
- return cursor;
+ if (cursor != null) {
+ cursor.getCount();
+ return cursor;
+ }
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
- return null;
- } finally {
- mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
}
+ // If cursor is null or an exception was thrown, stop the spinner and return null.
+ // changeCursor doesn't get called if cursor is null
+ mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
+ return null;
}
public void close() {
@@ -171,6 +185,42 @@
mClosed = true;
}
+ @Override
+ public void notifyDataSetChanged() {
+ if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
+ super.notifyDataSetChanged();
+
+ mSearchDialog.onDataSetChanged();
+
+ updateSpinnerState(getCursor());
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
+ super.notifyDataSetInvalidated();
+
+ updateSpinnerState(getCursor());
+ }
+
+ private void updateSpinnerState(Cursor cursor) {
+ Bundle extras = cursor != null ? cursor.getExtras() : null;
+ if (DBG) {
+ Log.d(LOG_TAG, "updateSpinnerState - extra = "
+ + (extras != null
+ ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+ : null));
+ }
+ // Check if the Cursor indicates that the query is not complete and show the spinner
+ if (extras != null
+ && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+ mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
+ return;
+ }
+ // If cursor is null or is done, stop the spinner
+ mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
+ }
+
/**
* Cache columns.
*/
@@ -188,12 +238,13 @@
super.changeCursor(c);
if (c != null) {
- mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
+ mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
- mBackgroundColorCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
+ mBackgroundColorCol =
+ c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
}
} catch (Exception e) {
Log.e(LOG_TAG, "error changing cursor and caching columns", e);
@@ -239,9 +290,20 @@
Drawable background = getItemBackground(backgroundColor);
view.setBackgroundDrawable(background);
- final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
- setViewText(cursor, views.mText1, mText1Col, isHtml);
- setViewText(cursor, views.mText2, mText2Col, isHtml);
+ if (views.mText1 != null) {
+ String text1 = getStringOrNull(cursor, mText1Col);
+ setViewText(views.mText1, text1);
+ }
+ if (views.mText2 != null) {
+ // First check TEXT_2_URL
+ CharSequence text2 = getStringOrNull(cursor, mText2UrlCol);
+ if (text2 != null) {
+ text2 = formatUrl(text2);
+ } else {
+ text2 = getStringOrNull(cursor, mText2Col);
+ }
+ setViewText(views.mText2, text2);
+ }
if (views.mIcon1 != null) {
setViewDrawable(views.mIcon1, getIcon1(cursor));
@@ -251,6 +313,21 @@
}
}
+ private CharSequence formatUrl(CharSequence url) {
+ if (mUrlColor == null) {
+ // Lazily get the URL color from the current theme.
+ TypedValue colorValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
+ mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId);
+ }
+
+ SpannableString text = new SpannableString(url);
+ text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null),
+ 0, url.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return text;
+ }
+
/**
* Gets a drawable with no color when selected or pressed, and the given color when
* neither selected nor pressed.
@@ -278,19 +355,7 @@
}
}
- private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) {
- if (v == null) {
- return;
- }
- CharSequence text = null;
- if (textCol >= 0) {
- String str = cursor.getString(textCol);
- if (isHtml && looksLikeHtml(str)) {
- text = Html.fromHtml(str);
- } else {
- text = str;
- }
- }
+ private void setViewText(TextView v, CharSequence text) {
// Set the text even if it's null, since we need to clear any previous text.
v.setText(text);
@@ -301,15 +366,6 @@
}
}
- private static boolean looksLikeHtml(String str) {
- if (TextUtils.isEmpty(str)) return false;
- for (int i = str.length() - 1; i >= 0; i--) {
- char c = str.charAt(i);
- if (c == '<' || c == '&') return true;
- }
- return false;
- }
-
private Drawable getIcon1(Cursor cursor) {
if (mIconName1Col < 0) {
return null;
@@ -617,6 +673,10 @@
*/
public static String getColumnString(Cursor cursor, String columnName) {
int col = cursor.getColumnIndex(columnName);
+ return getStringOrNull(cursor, col);
+ }
+
+ private static String getStringOrNull(Cursor cursor, int col) {
if (col == NONE) {
return null;
}
@@ -629,5 +689,4 @@
return null;
}
}
-
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index aca8ab4..defe421 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -1,5 +1,7 @@
package android.app;
+import android.content.Context;
+import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -9,6 +11,22 @@
* allow applications to control UI modes of the device.
* It provides functionality to disable the car mode and it gives access to the
* night mode settings.
+ *
+ * <p>These facilities are built on top of the underlying
+ * {@link android.content.Intent#ACTION_DOCK_EVENT} broadcasts that are sent when the user
+ * physical places the device into and out of a dock. When that happens,
+ * the UiModeManager switches the system {@link android.content.res.Configuration}
+ * to the appropriate UI mode, sends broadcasts about the mode switch, and
+ * starts the corresponding mode activity if appropriate. See the
+ * broadcasts {@link #ACTION_ENTER_CAR_MODE} and
+ * {@link #ACTION_ENTER_DESK_MODE} for more information.
+ *
+ * <p>In addition, the user may manually switch the system to car mode without
+ * physically being in a dock. While in car mode -- whether by manual action
+ * from the user or being physically placed in a dock -- a notification is
+ * displayed allowing the user to exit dock mode. Thus the dock mode
+ * represented here may be different than the current state of the underlying
+ * dock event broadcast.
*
* <p>You do not instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService
@@ -17,19 +35,72 @@
public class UiModeManager {
private static final String TAG = "UiModeManager";
- public static final int MODE_NOTNIGHT = 1;
- public static final int MODE_NIGHT = 2;
- public static final int MODE_AUTO = 3;
+ /**
+ * Broadcast sent when the device's UI has switched to car mode, either
+ * by being placed in a car dock or explicit action of the user. After
+ * sending the broadcast, the system will start the intent
+ * {@link android.content.Intent#ACTION_MAIN} with category
+ * {@link android.content.Intent#CATEGORY_CAR_DOCK}
+ * to display the car UI, which typically what an application would
+ * implement to provide their own interface. However, applications can
+ * also monitor this Intent in order to be informed of mode changes or
+ * prevent the normal car UI from being displayed by setting the result
+ * of the broadcast to {@link Activity#RESULT_CANCELED}.
+ */
+ public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switch away from car mode back
+ * to normal mode. Typically used by a car mode app, to dismiss itself
+ * when the user exits car mode.
+ */
+ public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switched to desk mode,
+ * by being placed in a desk dock. After
+ * sending the broadcast, the system will start the intent
+ * {@link android.content.Intent#ACTION_MAIN} with category
+ * {@link android.content.Intent#CATEGORY_DESK_DOCK}
+ * to display the desk UI, which typically what an application would
+ * implement to provide their own interface. However, applications can
+ * also monitor this Intent in order to be informed of mode changes or
+ * prevent the normal desk UI from being displayed by setting the result
+ * of the broadcast to {@link Activity#RESULT_CANCELED}.
+ */
+ public static String ACTION_ENTER_DESK_MODE = "android.app.action.ENTER_DESK_MODE";
+
+ /**
+ * Broadcast sent when the device's UI has switch away from car mode back
+ * to normal mode. Typically used by a car mode app, to dismiss itself
+ * when the user exits car mode.
+ */
+ public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * automatically switch night mode on and off based on the time.
+ */
+ public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * never run in night mode.
+ */
+ public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
+
+ /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ * always run in night mode.
+ */
+ public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
private IUiModeManager mService;
/*package*/ UiModeManager() {
mService = IUiModeManager.Stub.asInterface(
- ServiceManager.getService("uimode"));
+ ServiceManager.getService(Context.UI_MODE_SERVICE));
}
/**
- * Disables the car mode.
+ * Turn off special mode if currently in car mode.
*/
public void disableCarMode() {
if (mService != null) {
@@ -42,17 +113,35 @@
}
/**
+ * Return the current running mode type. May be one of
+ * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
+ * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
+ * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+ */
+ public int getCurrentModeType() {
+ if (mService != null) {
+ try {
+ return mService.getCurrentModeType();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getCurrentModeType: RemoteException", e);
+ }
+ }
+ return Configuration.UI_MODE_TYPE_NORMAL;
+ }
+
+ /**
* Sets the night mode. Changes to the night mode are only effective when
- * the car mode is enabled on a device.
+ * the car or desk mode is enabled on a device.
*
* <p>The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NOTNIGHT}<em> - sets the device into notnight
+ * <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
* mode.</li>
- * <li><em>{@link #MODE_NIGHT}</em> - sets the device into night mode.
+ * <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
* </li>
- * <li><em>{@link #MODE_AUTO}</em> - automatic night/notnight switching
+ * <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
* depending on the location and certain other sensors.</li>
+ * </ul>
*/
public void setNightMode(int mode) {
if (mService != null) {
@@ -67,8 +156,8 @@
/**
* Returns the currently configured night mode.
*
- * @return {@link #MODE_NOTNIGHT}, {@link #MODE_NIGHT} or {@link #MODE_AUTO}
- * When an error occurred -1 is returned.
+ * @return {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, or
+ * {@link #MODE_NIGHT_AUTO}. When an error occurred -1 is returned.
*/
public int getNightMode() {
if (mService != null) {
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 5ca3fb54..7db9fa8 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -25,7 +25,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -96,6 +98,8 @@
+ WallpaperService.SERVICE_META_DATA + " meta-data");
}
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
@@ -109,7 +113,7 @@
"Meta-data does not start with wallpaper tag");
}
- TypedArray sa = context.getResources().obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.Wallpaper);
settingsActivityComponent = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsActivity);
@@ -125,6 +129,9 @@
-1);
sa.recycle();
+ } catch (NameNotFoundException e) {
+ throw new XmlPullParserException(
+ "Unable to create context for: " + si.packageName);
} finally {
if (parser != null) parser.close();
}
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index c4de812..0bcd65c 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -26,6 +26,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.content.res.Resources.NotFoundException;
@@ -181,6 +183,8 @@
+ DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data");
}
+ Resources res = pm.getResourcesForApplication(ai.applicationInfo);
+
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
@@ -194,7 +198,7 @@
"Meta-data does not start with device-admin tag");
}
- TypedArray sa = context.getResources().obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.DeviceAdmin);
mVisible = sa.getBoolean(
@@ -227,6 +231,9 @@
}
}
}
+ } catch (NameNotFoundException e) {
+ throw new XmlPullParserException(
+ "Unable to create context for: " + ai.packageName);
} finally {
if (parser != null) parser.close();
}
diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/app/backup/AbsoluteFileBackupHelper.java
similarity index 98%
rename from core/java/android/backup/AbsoluteFileBackupHelper.java
rename to core/java/android/app/backup/AbsoluteFileBackupHelper.java
index 5a8034b..677224c 100644
--- a/core/java/android/backup/AbsoluteFileBackupHelper.java
+++ b/core/java/android/app/backup/AbsoluteFileBackupHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
similarity index 96%
rename from core/java/android/app/BackupAgent.java
rename to core/java/android/app/backup/BackupAgent.java
index 4695c21..d0cb148 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package android.app;
+package android.app.backup;
import android.app.IBackupAgent;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
-import android.backup.IBackupManager;
+import android.app.backup.IBackupManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Binder;
@@ -39,7 +37,7 @@
* respectively.
* <p>
* A backup agent based on convenient helper classes is available in
- * {@link android.backup.BackupHelperAgent} for less complex implementation
+ * {@link android.app.backup.BackupHelperAgent} for less complex implementation
* requirements.
* <p>
* STOPSHIP write more documentation about the backup process here.
@@ -87,7 +85,7 @@
* The application is being restored from backup and should replace any
* existing data with the contents of the backup. The backup data is
* provided in the file descriptor pointed to by the
- * {@link android.backup.BackupDataInput} instance <code>data</code>. Once
+ * {@link android.app.backup.BackupDataInput} instance <code>data</code>. Once
* the restore is finished, the application should write a representation of
* the final state to the <code>newState</code> file descriptor.
* <p>
diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/app/backup/BackupDataInput.java
similarity index 98%
rename from core/java/android/backup/BackupDataInput.java
rename to core/java/android/app/backup/BackupDataInput.java
index a08ee75..2da0c11 100644
--- a/core/java/android/backup/BackupDataInput.java
+++ b/core/java/android/app/backup/BackupDataInput.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -131,7 +131,7 @@
/**
* Consume the current record's data without actually reading it into a buffer
- * for further processing. This allows a {@link android.app.BackupAgent} to
+ * for further processing. This allows a {@link android.app.backup.BackupAgent} to
* efficiently discard obsolete or otherwise uninteresting records during the
* restore operation.
*
diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/app/backup/BackupDataInputStream.java
similarity index 97%
rename from core/java/android/backup/BackupDataInputStream.java
rename to core/java/android/app/backup/BackupDataInputStream.java
index 503b3c1..a7f4ba6 100644
--- a/core/java/android/backup/BackupDataInputStream.java
+++ b/core/java/android/app/backup/BackupDataInputStream.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import java.io.InputStream;
import java.io.IOException;
diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java
similarity index 98%
rename from core/java/android/backup/BackupDataOutput.java
rename to core/java/android/app/backup/BackupDataOutput.java
index 34879d8..53b1d46 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/app/backup/BackupDataOutput.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java
similarity index 93%
rename from core/java/android/backup/BackupHelper.java
rename to core/java/android/app/backup/BackupHelper.java
index 7eedd01..dca23881 100644
--- a/core/java/android/backup/BackupHelper.java
+++ b/core/java/android/app/backup/BackupHelper.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.os.ParcelFileDescriptor;
/**
* A convenient interface to be used with the
- * {@link android.backup.BackupHelperAgent} to implement backup and restore of
+ * {@link android.app.backup.BackupHelperAgent} to implement backup and restore of
* arbitrary data types.
* <p>
* STOPSHOP: document!
diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/app/backup/BackupHelperAgent.java
similarity index 97%
rename from core/java/android/backup/BackupHelperAgent.java
rename to core/java/android/app/backup/BackupHelperAgent.java
index 7fb44f4..6f4a031 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/app/backup/BackupHelperAgent.java
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
-import android.app.BackupAgent;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/app/backup/BackupHelperDispatcher.java
similarity index 99%
rename from core/java/android/backup/BackupHelperDispatcher.java
rename to core/java/android/app/backup/BackupHelperDispatcher.java
index 68076db..5466db5 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/app/backup/BackupHelperDispatcher.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.os.ParcelFileDescriptor;
import android.util.Log;
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
similarity index 88%
rename from core/java/android/backup/BackupManager.java
rename to core/java/android/app/backup/BackupManager.java
index 2dff975..12d4daf 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
-import android.backup.RestoreSession;
+import android.app.backup.RestoreSession;
+import android.app.backup.IBackupManager;
+import android.app.backup.IRestoreSession;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -34,12 +36,12 @@
* operation actually occurs.
* <p>
* The backup operation itself begins with the system launching the
- * {@link android.app.BackupAgent} subclass declared in your manifest. See the
- * documentation for {@link android.app.BackupAgent} for a detailed description
+ * {@link android.app.backup.BackupAgent} subclass declared in your manifest. See the
+ * documentation for {@link android.app.backup.BackupAgent} for a detailed description
* of how the backup then proceeds.
* <p>
* A simple implementation of a BackupAgent useful for backing up Preferences
- * and files is available by using {@link android.backup.BackupHelperAgent}.
+ * and files is available by using {@link android.app.backup.BackupHelperAgent}.
* <p>
* STOPSHIP: more documentation!
* <p>
@@ -82,7 +84,7 @@
/**
* Notifies the Android backup system that your application wishes to back up
* new changes to its data. A backup operation using your application's
- * {@link android.app.BackupAgent} subclass will be scheduled when you call this method.
+ * {@link android.app.backup.BackupAgent} subclass will be scheduled when you call this method.
*/
public void dataChanged() {
checkServiceBinder();
@@ -116,7 +118,7 @@
/**
* Begin the process of restoring data from backup. See the
- * {@link android.backup.RestoreSession} class for documentation on that process.
+ * {@link android.app.backup.RestoreSession} class for documentation on that process.
*/
public RestoreSession beginRestoreSession() {
RestoreSession session = null;
diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/app/backup/FileBackupHelper.java
similarity index 92%
rename from core/java/android/backup/FileBackupHelper.java
rename to core/java/android/app/backup/FileBackupHelper.java
index cc859e2..b42049e 100644
--- a/core/java/android/backup/FileBackupHelper.java
+++ b/core/java/android/app/backup/FileBackupHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
@@ -24,7 +24,7 @@
/**
* A helper class which can be used in conjunction with
- * {@link android.backup.BackupHelperAgent} to manage the backup of a set of
+ * {@link android.app.backup.BackupHelperAgent} to manage the backup of a set of
* files. Whenever backup is performed, all files changed since the last backup
* will be saved in their entirety. During the first time the backup happens,
* all the files in the list will be backed up. Note that this should only be
@@ -69,9 +69,9 @@
* now. When <code>oldState</code> is <code>null</code>, all the files will
* be backed up.
* <p>
- * This should be called from {@link android.backup.BackupHelperAgent}
+ * This should be called from {@link android.app.backup.BackupHelperAgent}
* directly. See
- * {@link android.app.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+ * {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
* for a description of parameter meanings.
*/
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/app/backup/FileBackupHelperBase.java
similarity index 97%
rename from core/java/android/backup/FileBackupHelperBase.java
rename to core/java/android/app/backup/FileBackupHelperBase.java
index 7cb1ccc..1e3158f 100644
--- a/core/java/android/backup/FileBackupHelperBase.java
+++ b/core/java/android/app/backup/FileBackupHelperBase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.content.Context;
import android.os.ParcelFileDescriptor;
@@ -24,7 +24,7 @@
import java.io.FileDescriptor;
/**
- * Base class for the {@link android.backup.FileBackupHelper} implementation.
+ * Base class for the {@link android.app.backup.FileBackupHelper} implementation.
*/
class FileBackupHelperBase {
private static final String TAG = "FileBackupHelperBase";
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
similarity index 97%
rename from core/java/android/backup/IBackupManager.aidl
rename to core/java/android/app/backup/IBackupManager.aidl
index d94b066..23d6351 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
-import android.backup.IRestoreSession;
+import android.app.backup.IRestoreSession;
/**
* Direct interface to the Backup Manager Service that applications invoke on. The only
* operation currently needed is a simple notification that the app has made changes to
* data it wishes to back up, so the system should run a backup pass.
*
- * Apps will use the {@link android.backup.BackupManager} class rather than going through
+ * Apps will use the {@link android.app.backup.BackupManager} class rather than going through
* this Binder interface directly.
*
* {@hide}
diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/app/backup/IRestoreObserver.aidl
similarity index 98%
rename from core/java/android/backup/IRestoreObserver.aidl
rename to core/java/android/app/backup/IRestoreObserver.aidl
index 59e59fc..75d0d17 100644
--- a/core/java/android/backup/IRestoreObserver.aidl
+++ b/core/java/android/app/backup/IRestoreObserver.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
/**
* Callback class for receiving progress reports during a restore operation.
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl
similarity index 96%
rename from core/java/android/backup/IRestoreSession.aidl
rename to core/java/android/app/backup/IRestoreSession.aidl
index bead395..58c21fe 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/app/backup/IRestoreSession.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
-import android.backup.RestoreSet;
-import android.backup.IRestoreObserver;
+import android.app.backup.RestoreSet;
+import android.app.backup.IRestoreObserver;
/**
* Binder interface used by clients who wish to manage a restore operation. Every
diff --git a/core/java/android/backup/RestoreObserver.java b/core/java/android/app/backup/RestoreObserver.java
similarity index 98%
rename from core/java/android/backup/RestoreObserver.java
rename to core/java/android/app/backup/RestoreObserver.java
index e4182750..7a5e10b 100644
--- a/core/java/android/backup/RestoreObserver.java
+++ b/core/java/android/app/backup/RestoreObserver.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
/**
* Callback class for receiving progress reports during a restore operation. These
diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java
similarity index 97%
rename from core/java/android/backup/RestoreSession.java
rename to core/java/android/app/backup/RestoreSession.java
index bc410c4..730a21f 100644
--- a/core/java/android/backup/RestoreSession.java
+++ b/core/java/android/app/backup/RestoreSession.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
-import android.backup.IRestoreSession;
-import android.backup.RestoreObserver;
-import android.backup.RestoreSet;
+import android.app.backup.RestoreObserver;
+import android.app.backup.RestoreSet;
+import android.app.backup.IRestoreObserver;
+import android.app.backup.IRestoreSession;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
diff --git a/core/java/android/backup/RestoreSet.aidl b/core/java/android/app/backup/RestoreSet.aidl
similarity index 95%
rename from core/java/android/backup/RestoreSet.aidl
rename to core/java/android/app/backup/RestoreSet.aidl
index 42e77bf..ab1b125 100644
--- a/core/java/android/backup/RestoreSet.aidl
+++ b/core/java/android/app/backup/RestoreSet.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
parcelable RestoreSet;
\ No newline at end of file
diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/app/backup/RestoreSet.java
similarity index 98%
rename from core/java/android/backup/RestoreSet.java
rename to core/java/android/app/backup/RestoreSet.java
index eeca148..0431977 100644
--- a/core/java/android/backup/RestoreSet.java
+++ b/core/java/android/app/backup/RestoreSet.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
similarity index 96%
rename from core/java/android/backup/SharedPreferencesBackupHelper.java
rename to core/java/android/app/backup/SharedPreferencesBackupHelper.java
index 7ba80db..d35b10c 100644
--- a/core/java/android/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.backup;
+package android.app.backup;
import android.content.Context;
import android.content.SharedPreferences;
@@ -25,7 +25,7 @@
/**
* A helper class which can be used in conjunction with
- * {@link android.backup.BackupHelperAgent} to manage the backup of
+ * {@link android.app.backup.BackupHelperAgent} to manage the backup of
* {@link android.content.SharedPreferences}. Whenever backup is performed it
* will back up all named shared preferences which have changed since the last
* backup.
diff --git a/core/java/android/backup/package.html b/core/java/android/app/backup/package.html
similarity index 65%
rename from core/java/android/backup/package.html
rename to core/java/android/app/backup/package.html
index 13c0eb8..8d58e0c 100644
--- a/core/java/android/backup/package.html
+++ b/core/java/android/app/backup/package.html
@@ -2,12 +2,12 @@
<BODY>
<p>Package containing the backup and restore functionality available to
applications. All backup management is controlled through
-{@link android.backup.BackupManager}. Individual backup functionality is
-implemented through a subclass {@link android.app.BackupAgent} and a
+{@link android.app.backup.BackupManager}. Individual backup functionality is
+implemented through a subclass {@link android.app.backup.BackupAgent} and a
simple and easy-to-use implementation is provided in
-{@link android.backup.BackupHelperAgent}.</p>
+{@link android.app.backup.BackupHelperAgent}.</p>
-<p>STOPSHIP: add more documenation and remove Dev Guide link if not written!</p>
+<p>STOPSHIP: add more documentation and remove Dev Guide link if not written!</p>
<p>The backup APIs let applications:</p>
<ul>
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 91b1c4e..5fb2aae 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -29,6 +29,7 @@
import android.database.SQLException;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -217,6 +218,13 @@
return ContentProvider.this.openAssetFile(uri, mode);
}
+ /**
+ * @hide
+ */
+ public Bundle call(String method, String request, Bundle args) {
+ return ContentProvider.this.call(method, request, args);
+ }
+
private void enforceReadPermission(Uri uri) {
final int uid = Binder.getCallingUid();
if (uid == mMyUid) {
@@ -748,4 +756,18 @@
}
return results;
}
-}
\ No newline at end of file
+
+ /**
+ * @hide -- until interface has proven itself
+ *
+ * Call an provider-defined method. This can be used to implement
+ * interfaces that are cheaper than using a Cursor.
+ *
+ * @param method Method name to call. Opaque to framework.
+ * @param request Nullable String argument passed to method.
+ * @param args Nullable Bundle argument passed to method.
+ */
+ public Bundle call(String method, String request, Bundle args) {
+ return null;
+ }
+}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index bacb684..81b8055 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -26,6 +26,7 @@
import android.database.IContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
@@ -222,6 +223,21 @@
}
return true;
}
+
+ case CALL_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+
+ String method = data.readString();
+ String stringArg = data.readString();
+ Bundle args = data.readBundle();
+
+ Bundle responseBundle = call(method, stringArg, args);
+
+ reply.writeNoException();
+ reply.writeBundle(responseBundle);
+ return true;
+ }
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -485,6 +501,22 @@
return fd;
}
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ data.writeString(method);
+ data.writeString(request);
+ data.writeBundle(args);
+
+ mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ return reply.readBundle();
+ }
+
private IBinder mRemote;
}
-
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 1b0ef34..bcef75e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -736,7 +736,7 @@
* @hide
*/
public final IContentProvider acquireProvider(String name) {
- if(name == null) {
+ if (name == null) {
return null;
}
return acquireProvider(mContext, name);
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index b5a78fa..0477d6d 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -32,7 +32,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
/**
@@ -104,7 +103,7 @@
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
synchronized (mRootNode) {
- mRootNode.addObserver(uri, observer, notifyForDescendents);
+ mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri +
" with notifyForDescendents " + notifyForDescendents);
}
@@ -115,7 +114,7 @@
throw new IllegalArgumentException("You must pass a valid observer");
}
synchronized (mRootNode) {
- mRootNode.removeObserver(observer);
+ mRootNode.removeObserverLocked(observer);
if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
}
}
@@ -132,7 +131,7 @@
try {
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
synchronized (mRootNode) {
- mRootNode.collectObservers(uri, 0, observer, observerWantsSelfNotifications,
+ mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
calls);
}
final int numCalls = calls.size();
@@ -470,10 +469,12 @@
*/
public static final class ObserverNode {
private class ObserverEntry implements IBinder.DeathRecipient {
- public IContentObserver observer;
- public boolean notifyForDescendents;
+ public final IContentObserver observer;
+ public final boolean notifyForDescendents;
+ private final Object observersLock;
- public ObserverEntry(IContentObserver o, boolean n) {
+ public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
+ this.observersLock = observersLock;
observer = o;
notifyForDescendents = n;
try {
@@ -484,7 +485,9 @@
}
public void binderDied() {
- removeObserver(observer);
+ synchronized (observersLock) {
+ removeObserverLocked(observer);
+ }
}
}
@@ -519,16 +522,16 @@
return uri.getPathSegments().size() + 1;
}
- public void addObserver(Uri uri, IContentObserver observer, boolean notifyForDescendents) {
- addObserver(uri, 0, observer, notifyForDescendents);
+ public void addObserverLocked(Uri uri, IContentObserver observer,
+ boolean notifyForDescendents, Object observersLock) {
+ addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
}
- private void addObserver(Uri uri, int index, IContentObserver observer,
- boolean notifyForDescendents) {
-
+ private void addObserverLocked(Uri uri, int index, IContentObserver observer,
+ boolean notifyForDescendents, Object observersLock) {
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
- mObservers.add(new ObserverEntry(observer, notifyForDescendents));
+ mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
return;
}
@@ -538,7 +541,7 @@
for (int i = 0; i < N; i++) {
ObserverNode node = mChildren.get(i);
if (node.mName.equals(segment)) {
- node.addObserver(uri, index + 1, observer, notifyForDescendents);
+ node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
return;
}
}
@@ -546,13 +549,13 @@
// No child found, create one
ObserverNode node = new ObserverNode(segment);
mChildren.add(node);
- node.addObserver(uri, index + 1, observer, notifyForDescendents);
+ node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
}
- public boolean removeObserver(IContentObserver observer) {
+ public boolean removeObserverLocked(IContentObserver observer) {
int size = mChildren.size();
for (int i = 0; i < size; i++) {
- boolean empty = mChildren.get(i).removeObserver(observer);
+ boolean empty = mChildren.get(i).removeObserverLocked(observer);
if (empty) {
mChildren.remove(i);
i--;
@@ -578,10 +581,8 @@
return false;
}
- private void collectMyObservers(Uri uri,
- boolean leaf, IContentObserver observer, boolean selfNotify,
- ArrayList<ObserverCall> calls)
- {
+ private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
+ boolean selfNotify, ArrayList<ObserverCall> calls) {
int N = mObservers.size();
IBinder observerBinder = observer == null ? null : observer.asBinder();
for (int i = 0; i < N; i++) {
@@ -600,17 +601,17 @@
}
}
- public void collectObservers(Uri uri, int index, IContentObserver observer,
+ public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
boolean selfNotify, ArrayList<ObserverCall> calls) {
String segment = null;
int segmentCount = countUriSegments(uri);
if (index >= segmentCount) {
// This is the leaf node, notify all observers
- collectMyObservers(uri, true, observer, selfNotify, calls);
+ collectMyObserversLocked(true, observer, selfNotify, calls);
} else if (index < segmentCount){
segment = getUriSegment(uri, index);
// Notify any observers at this level who are interested in descendents
- collectMyObservers(uri, false, observer, selfNotify, calls);
+ collectMyObserversLocked(false, observer, selfNotify, calls);
}
int N = mChildren.size();
@@ -618,7 +619,7 @@
ObserverNode node = mChildren.get(i);
if (segment == null || node.mName.equals(segment)) {
// We found the child,
- node.collectObservers(uri, index + 1, observer, selfNotify, calls);
+ node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
if (segment != null) {
break;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8d136f1..2b0e7e7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1489,7 +1489,7 @@
/**
* Use with {@link #getSystemService} to retrieve an
- * {@link android.backup.IBackupManager IBackupManager} for communicating
+ * {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
* @hide
*
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 1b0ca58..67e7581 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -22,10 +22,11 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
-import android.os.RemoteException;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -58,6 +59,17 @@
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
+ /**
+ * @hide -- until interface has proven itself
+ *
+ * Call an provider-defined method. This can be used to implement
+ * interfaces that are cheaper than using a Cursor.
+ *
+ * @param method Method name to call. Opaque to framework.
+ * @param request Nullable String argument passed to method.
+ * @param args Nullable Bundle argument passed to method.
+ */
+ public Bundle call(String method, String request, Bundle args) throws RemoteException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
@@ -71,4 +83,5 @@
static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
+ static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 90e8c14..fd5591d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1423,7 +1423,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE =
- "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABILE";
+ "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
/**
* Broadcast Action: The current system wallpaper has changed. See
@@ -1632,12 +1632,20 @@
/**
* Broadcast Action: External media is unmounted because it is being shared via USB mass storage.
- * The path to the mount point for the removed media is contained in the Intent.mData field.
+ * The path to the mount point for the shared media is contained in the Intent.mData field.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED";
/**
+ * Broadcast Action: External media is no longer being shared via USB mass storage.
+ * The path to the mount point for the previously shared media is contained in the Intent.mData field.
+ *
+ * @hide
+ */
+ public static final String ACTION_MEDIA_UNSHARED = "android.intent.action.MEDIA_UNSHARED";
+
+ /**
* Broadcast Action: External media was removed from SD card slot, but mount point was not unmounted.
* The path to the mount point for the removed media is contained in the Intent.mData field.
*/
@@ -1830,21 +1838,17 @@
"android.intent.action.REBOOT";
/**
- * Broadcast Action: A sticky broadcast indicating the phone was docked
- * or undocked.
+ * Broadcast Action: A sticky broadcast for changes in the physical
+ * docking state of the device.
*
* <p>The intent will have the following extra values:
* <ul>
* <li><em>{@link #EXTRA_DOCK_STATE}</em> - the current dock
- * state, which depends on the state of the car mode.</li>
- * <li><em>{@link #EXTRA_PHYSICAL_DOCK_STATE}</em> - the physical dock
- * state.</li>
- * <li><em>{@link #EXTRA_CAR_MODE_ENABLED}</em> - a boolean indicating the
- * state of the car mode.</li>
+ * state, indicating which dock the device is physically in.</li>
* </ul>
- * <p>This is intended for monitoring the current dock state.
- * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK}
- * or {@link #CATEGORY_DESK_DOCK} instead.
+ * <p>This is intended for monitoring the current physical dock state.
+ * See {@link android.app.UiModeManager} for the normal API dealing with
+ * dock mode changes.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DOCK_EVENT =
@@ -2006,15 +2010,15 @@
"android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST";
/**
* An activity to run when device is inserted into a car dock.
- * Used with {@link #ACTION_MAIN} to launch an activity.
- * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+ * Used with {@link #ACTION_MAIN} to launch an activity. For more
+ * information, see {@link android.app.UiModeManager}.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
/**
* An activity to run when device is inserted into a car dock.
- * Used with {@link #ACTION_MAIN} to launch an activity.
- * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+ * Used with {@link #ACTION_MAIN} to launch an activity. For more
+ * information, see {@link android.app.UiModeManager}.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
@@ -2183,22 +2187,6 @@
public static final int EXTRA_DOCK_STATE_CAR = 2;
/**
- * Used as an int extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
- * intents to request the physical dock state. Possible values are
- * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
- * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
- * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
- */
- public static final String EXTRA_PHYSICAL_DOCK_STATE =
- "android.intent.extra.PHYSICAL_DOCK_STATE";
-
- /**
- * Used as an boolean extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
- * intents to indicate that the car mode is enabled or not.
- */
- public static final String EXTRA_CAR_MODE_ENABLED = "android.intent.extra.CAR_MODE_ENABLED";
-
- /**
* Boolean that can be supplied as meta-data with a dock activity, to
* indicate that the dock should take over the home key when it is active.
*/
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 6ade837..98a2595 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -18,6 +18,7 @@
import android.content.pm.RegisteredServicesCache;
import android.content.pm.XmlSerializerAndParser;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
@@ -42,8 +43,9 @@
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
}
- public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
- TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+ public SyncAdapterType parseServiceAttributes(Resources res,
+ String packageName, AttributeSet attrs) {
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.SyncAdapter);
try {
final String authority =
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b07bafcf..7a0337cd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -967,8 +967,11 @@
String orig =sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
if (!pkg.packageName.equals(orig)) {
- pkg.mOriginalPackage = orig;
- pkg.mRealPackage = pkg.packageName;
+ if (pkg.mOriginalPackages == null) {
+ pkg.mOriginalPackages = new ArrayList<String>();
+ pkg.mRealPackage = pkg.packageName;
+ }
+ pkg.mOriginalPackages.add(orig);
}
sa.recycle();
@@ -2579,7 +2582,7 @@
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
- public String mOriginalPackage = null;
+ public ArrayList<String> mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList<String> mAdoptPermissions = null;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b74c073..dce3963 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -21,6 +21,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ComponentName;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Environment;
import android.os.Handler;
@@ -402,7 +404,8 @@
"Meta-data does not start with " + mAttributesName + " tag");
}
- V v = parseServiceAttributes(si.packageName, attrs);
+ V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
+ si.packageName, attrs);
if (v == null) {
return null;
}
@@ -410,6 +413,9 @@
final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
final int uid = applicationInfo.uid;
return new ServiceInfo<V>(v, componentName, uid);
+ } catch (NameNotFoundException e) {
+ throw new XmlPullParserException(
+ "Unable to load resources for pacakge " + si.packageName);
} finally {
if (parser != null) parser.close();
}
@@ -499,5 +505,6 @@
}
}
- public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
+ public abstract V parseServiceAttributes(Resources res,
+ String packageName, AttributeSet attrs);
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index a737283..61e3004 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -165,7 +165,8 @@
public static final int UI_MODE_TYPE_MASK = 0x0f;
public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
public static final int UI_MODE_TYPE_NORMAL = 0x01;
- public static final int UI_MODE_TYPE_CAR = 0x02;
+ public static final int UI_MODE_TYPE_DESK = 0x02;
+ public static final int UI_MODE_TYPE_CAR = 0x03;
public static final int UI_MODE_NIGHT_MASK = 0x30;
public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
@@ -175,11 +176,12 @@
/**
* Bit mask of the ui mode. Currently there are two fields:
* <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
- * device. They may be one of
- * {@link #UI_MODE_TYPE_NORMAL} or {@link #UI_MODE_TYPE_CAR}.
+ * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
+ * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
+ * or {@link #UI_MODE_TYPE_CAR}.
*
* <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
- * is in a special mode. They may be one of
+ * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
* {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
*/
public int uiMode;
@@ -272,7 +274,7 @@
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
- uiMode = UI_MODE_TYPE_NORMAL;
+ uiMode = UI_MODE_TYPE_UNDEFINED;
seq = 0;
}
@@ -354,10 +356,17 @@
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
screenLayout = delta.screenLayout;
}
- if (delta.uiMode != UI_MODE_TYPE_NORMAL
+ if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
- uiMode = delta.uiMode;
+ if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
+ uiMode = (uiMode&~UI_MODE_TYPE_MASK)
+ | (delta.uiMode&UI_MODE_TYPE_MASK);
+ }
+ if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
+ uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
+ | (delta.uiMode&UI_MODE_NIGHT_MASK);
+ }
}
if (delta.seq != 0) {
@@ -439,7 +448,7 @@
&& screenLayout != delta.screenLayout) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
- if (delta.uiMode != UI_MODE_TYPE_NORMAL
+ if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
}
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 19ad946..748eb99 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -143,8 +143,7 @@
public void close() {
maybeUnregisterObserverProxy();
- mCursor.deactivate();
-
+ mCursor.close();
}
public int requery(IContentObserver observer, CursorWindow window) {
diff --git a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
new file mode 100644
index 0000000..8ac4c0f
--- /dev/null
+++ b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+/**
+ * An exception that indicates that garbage-collector is finalizing a database object
+ * that is not explicitly closed
+ * @hide
+ */
+public class DatabaseObjectNotClosedException extends RuntimeException
+{
+ private static final String s = "Application did not close the cursor or database object " +
+ "that was opened here";
+
+ public DatabaseObjectNotClosedException()
+ {
+ super(s);
+ }
+}
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 486ad20..4ccf6b0 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -56,7 +56,7 @@
/* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
mDatabase = db;
mSqlStmt = sql;
- mStackTrace = new Exception().fillInStackTrace();
+ mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
this.nHandle = db.mNativeHandle;
compile(sql, true);
}
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 96ed297..3f0fcb1 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -207,7 +207,7 @@
String editTable, SQLiteQuery query) {
// The AbstractCursor constructor needs to do some setup.
super();
- mStackTrace = new Exception().fillInStackTrace();
+ mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
mDatabase = db;
mDriver = driver;
mEditTable = editTable;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index d8ccf85..dfd4024 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -261,7 +261,7 @@
public static final int MAX_SQL_CACHE_SIZE = 250;
private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
private int mCacheFullWarnings;
- private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 5;
+ private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
/** maintain stats about number of cache hits and misses */
private int mNumCacheHits;
@@ -1542,7 +1542,6 @@
}
}
statement.execute();
- statement.close();
return lastChangeCount();
} catch (SQLiteDatabaseCorruptException e) {
onCorruption();
@@ -1638,7 +1637,6 @@
// Run the program and then cleanup
statement.execute();
- statement.close();
int numChangedRows = lastChangeCount();
if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
@@ -1749,7 +1747,7 @@
mFlags = flags;
mPath = path;
mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
- mStackTrace = new Exception().fillInStackTrace();
+ mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
mFactory = factory;
dbopen(mPath, mFlags);
if (SQLiteDebug.DEBUG_SQL_CACHE) {
@@ -1911,43 +1909,28 @@
}
// add this <sql, compiledStatement> to the cache
if (mCompiledQueries.size() == mMaxSqlCacheSize) {
- /* reached max cachesize. before adding new entry, remove an entry from the
- * cache. we don't want to wipe out the entire cache because of this:
- * GCing {@link SQLiteCompiledSql} requires call to sqlite3_finalize
- * JNI method. If entire cache is wiped out, it could cause a big GC activity
- * just because a (rogue) process is using the cache incorrectly.
+ /*
+ * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
+ * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
+ * chances are it is NOT using ? for bindargs - so caching is useless.
+ * TODO: either let the callers set max cchesize for their app, or intelligently
+ * figure out what should be cached for a given app.
*/
- 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 + ". Please change your sql statements to use '?' for " +
- "bindargs, instead of using actual values");
-
- /* increment the number of times this warnings has been printed.
- * if this warning is printed too many times, clear the whole cache - the app
- * is doing something weird or incorrect and printing more warnings will only
- * flood the logfile.
- */
- if (++mCacheFullWarnings > MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
- mCacheFullWarnings = 0;
- // clear the cache
- mCompiledQueries.clear();
- Log.w(TAG, "Compiled-sql statement cache for database: " +
- getPath() + " hit MAX size-limit too many times. " +
- "Removing all compiled-sql statements from the cache.");
- } else {
- // clear just a single entry from cache
- Set<String> keySet = mCompiledQueries.keySet();
- for (String s : keySet) {
- mCompiledQueries.remove(s);
- break;
- }
+ if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
+ 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 + ". Please change your sql statements to use '?' for " +
+ "bindargs, instead of using actual values");
+ }
+ // don't add this entry to cache
+ } else {
+ // cache is NOT full. add this to cache.
+ mCompiledQueries.put(sql, compiledStatement);
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
+ mCompiledQueries.size() + "|" + sql);
}
}
- mCompiledQueries.put(sql, compiledStatement);
- }
- if (SQLiteDebug.DEBUG_SQL_CACHE) {
- Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" +
- sql);
}
return;
}
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 389e15e..6636473 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -58,11 +58,10 @@
db.addSQLiteClosable(this);
this.nHandle = db.mNativeHandle;
- // shouldn't reuse compiled-plans of PRAGMA sql statements
- // because sqlite returns OLD values if compiled-pragma-statements are reused
- //TODO: remove this code when sqlite fixes it (and add tests too)
+ // only cache CRUD statements
String prefixSql = mSql.substring(0, 6);
- if (prefixSql.toLowerCase().startsWith("pragma")) {
+ if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
+ !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
mCompiledSql = new SQLiteCompiledSql(db, sql);
nStatement = mCompiledSql.nStatement;
// since it is not in the cache, no need to acquire() it.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index cddbec9..85d52ec 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -137,8 +137,6 @@
* can be connected to another process.
*
* @throws IOException if the method fails.
- *
- * @hide
*/
public native final void reconnect() throws IOException;
@@ -717,6 +715,7 @@
private static final String KEY_GPS_LONGITUDE = "gps-longitude";
private static final String KEY_GPS_ALTITUDE = "gps-altitude";
private static final String KEY_GPS_TIMESTAMP = "gps-timestamp";
+ private static final String KEY_GPS_PROCESSING_METHOD = "gps-processing-method";
private static final String KEY_WHITE_BALANCE = "whitebalance";
private static final String KEY_EFFECT = "effect";
private static final String KEY_ANTIBANDING = "antibanding";
@@ -1327,6 +1326,16 @@
}
/**
+ * Sets GPS processing method. It will store up to 100 characters
+ * in JPEG EXIF header.
+ *
+ * @param processing_method The processing method to get this location.
+ */
+ public void setGpsProcessingMethod(String processing_method) {
+ set(KEY_GPS_PROCESSING_METHOD, processing_method);
+ }
+
+ /**
* Removes GPS latitude, longitude, altitude, and timestamp from the
* parameters.
*/
@@ -1335,6 +1344,7 @@
remove(KEY_GPS_LONGITUDE);
remove(KEY_GPS_ALTITUDE);
remove(KEY_GPS_TIMESTAMP);
+ remove(KEY_GPS_PROCESSING_METHOD);
}
/**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5d5bd9c..8c8d3e5 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2025,7 +2025,10 @@
InputConnection ic = getCurrentInputConnection();
mExtractedText = ic == null? null
: ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
-
+ if (mExtractedText == null || ic == null) {
+ Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
+ + mExtractedText + ", input connection = " + ic);
+ }
final EditorInfo ei = getCurrentInputEditorInfo();
try {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c76aca1..a6668e7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -364,7 +364,7 @@
/**
* Sets the persisted value for enabling/disabling Mobile data.
*
- * @param allowMobileData Whether the mobile data connection should be
+ * @param enabled Whether the mobile data connection should be
* used or not.
* @hide
*/
@@ -418,22 +418,35 @@
/**
* {@hide}
*/
- public boolean tether(String iface) {
+ public String[] getTetheringErroredIfaces() {
try {
- return mService.tether(iface);
+ return mService.getTetheringErroredIfaces();
} catch (RemoteException e) {
- return false;
+ return new String[0];
}
}
/**
+ * @return error A TETHER_ERROR value indicating success or failure type
* {@hide}
*/
- public boolean untether(String iface) {
+ public int tether(String iface) {
+ try {
+ return mService.tether(iface);
+ } catch (RemoteException e) {
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+ }
+
+ /**
+ * @return error A TETHER_ERROR value indicating success or failure type
+ * {@hide}
+ */
+ public int untether(String iface) {
try {
return mService.untether(iface);
} catch (RemoteException e) {
- return false;
+ return TETHER_ERROR_SERVICE_UNAVAIL;
}
}
@@ -469,4 +482,41 @@
return new String[0];
}
}
+
+ /** {@hide} */
+ public static final int TETHER_ERROR_NO_ERROR = 0;
+ /** {@hide} */
+ public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
+ /** {@hide} */
+ public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
+ /** {@hide} */
+ public static final int TETHER_ERROR_UNSUPPORTED = 3;
+ /** {@hide} */
+ public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
+ /** {@hide} */
+ public static final int TETHER_ERROR_MASTER_ERROR = 5;
+ /** {@hide} */
+ public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
+ /** {@hide} */
+ public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
+ /** {@hide} */
+ public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8;
+ /** {@hide} */
+ public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
+ /** {@hide} */
+ public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+
+ /**
+ * @param iface The name of the interface we're interested in
+ * @return error The error code of the last error tethering or untethering the named
+ * interface
+ * {@hide}
+ */
+ public int getLastTetherError(String iface) {
+ try {
+ return mService.getLastTetherError(iface);
+ } catch (RemoteException e) {
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 2514693..b05c2ed 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -55,9 +55,11 @@
void setMobileDataEnabled(boolean enabled);
- boolean tether(String iface);
+ int tether(String iface);
- boolean untether(String iface);
+ int untether(String iface);
+
+ int getLastTetherError(String iface);
boolean isTetheringSupported();
@@ -65,6 +67,8 @@
String[] getTetheredIfaces();
+ String[] getTetheringErroredIfaces();
+
String[] getTetherableUsbRegexs();
String[] getTetherableWifiRegexs();
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 6941e57..a8c6f9b 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -158,7 +158,7 @@
private synchronized SSLSocketFactory getDelegate() {
// Relax the SSL check if instructed (for this factory, or systemwide)
- if (!mSecure || ("0".equals(SystemProperties.get("ro.secure")) &&
+ if (!mSecure || ("1".equals(SystemProperties.get("ro.debuggable")) &&
"yes".equals(SystemProperties.get("socket.relaxsslcheck")))) {
if (mInsecureFactory == null) {
if (mSecure) {
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index a572f60..e5bc6e3 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -57,7 +57,7 @@
/* scheme */ "(?:(http|HTTP|https|HTTPS|file|FILE)\\:\\/\\/)?" +
/* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
/* host */ "([-" + GOOD_IRI_CHAR + "%_]+(?:\\.[-" + GOOD_IRI_CHAR + "%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" +
- /* port */ "(?:\\:([0-9]+))?" +
+ /* port */ "(?:\\:([0-9]*))?" +
/* path */ "(\\/?[^#]*)?" +
/* anchor */ ".*");
@@ -85,7 +85,8 @@
t = m.group(MATCH_GROUP_HOST);
if (t != null) mHost = t;
t = m.group(MATCH_GROUP_PORT);
- if (t != null) {
+ if (t != null && t.length() > 0) {
+ // The ':' character is not returned by the regex.
try {
mPort = Integer.parseInt(t);
} catch (NumberFormatException ex) {
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index f735f3d..a67fd9a 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -59,7 +59,7 @@
private static SSLSocketFactory mSslSocketFactory = null;
static {
- // This intiialization happens in the zygote. It triggers some
+ // This initialization happens in the zygote. It triggers some
// lazy initialization that can will benefit later invocations of
// initializeEngine().
initializeEngine(null);
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index e2d687d..c29926c 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -128,8 +128,8 @@
String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) {
mIssuedTo = new DName(issuedTo);
mIssuedBy = new DName(issuedBy);
- mValidNotBefore = validNotBefore;
- mValidNotAfter = validNotAfter;
+ mValidNotBefore = cloneDate(validNotBefore);
+ mValidNotAfter = cloneDate(validNotAfter);
}
/**
@@ -148,7 +148,7 @@
* "" if none has been set
*/
public Date getValidNotBeforeDate() {
- return mValidNotBefore;
+ return cloneDate(mValidNotBefore);
}
/**
@@ -166,7 +166,7 @@
* "" if none has been set
*/
public Date getValidNotAfterDate() {
- return mValidNotAfter;
+ return cloneDate(mValidNotAfter);
}
/**
@@ -224,6 +224,16 @@
}
/**
+ * Clone a possibly null Date
+ */
+ private static Date cloneDate(Date date) {
+ if (date == null) {
+ return null;
+ }
+ return (Date) date.clone();
+ }
+
+ /**
* A distinguished name helper class: a 3-tuple of:
* - common name (CN),
* - organization (O),
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index d6fe107..0ec1c74 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -132,6 +132,45 @@
}
/**
+ * Make a Bundle for a single key/value pair.
+ *
+ * @hide
+ */
+ public static Bundle forPair(String key, String value) {
+ // TODO: optimize this case.
+ Bundle b = new Bundle(1);
+ b.putString(key, value);
+ return b;
+ }
+
+ /**
+ * TODO: optimize this later (getting just the value part of a Bundle
+ * with a single pair) once Bundle.forPair() above is implemented
+ * with a special single-value Map implementation/serialization.
+ *
+ * Note: value in single-pair Bundle may be null.
+ *
+ * @hide
+ */
+ public String getPairValue() {
+ unparcel();
+ int size = mMap.size();
+ if (size > 1) {
+ Log.w(LOG_TAG, "getPairValue() used on Bundle with multiple pairs.");
+ }
+ if (size == 0) {
+ return null;
+ }
+ Object o = mMap.values().iterator().next();
+ try {
+ return (String) o;
+ } catch (ClassCastException e) {
+ typeWarning("getPairValue()", o, "String", e);
+ return null;
+ }
+ }
+
+ /**
* Changes the ClassLoader this Bundle uses when instantiating objects.
*
* @param loader An explicit ClassLoader to use when instantiating objects
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a4c595d..2e14667 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -57,6 +57,8 @@
*/
public final class Debug
{
+ private static final String TAG = "Debug";
+
/**
* Flags for startMethodTracing(). These can be ORed together.
*
@@ -1111,7 +1113,7 @@
}
}
} else {
- Log.w("android.os.Debug",
+ Log.wtf(TAG,
"setFieldsOn(" + (cl == null ? "null" : cl.getName()) +
") called in non-DEBUG build");
}
@@ -1127,4 +1129,31 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface DebugProperty {
}
+
+ /**
+ * Get a debugging dump of a system service by name.
+ *
+ * <p>Most services require the caller to hold android.permission.DUMP.
+ *
+ * @param name of the service to dump
+ * @param fd to write dump output to (usually an output log file)
+ * @param args to pass to the service's dump method, may be null
+ * @return true if the service was dumped successfully, false if
+ * the service could not be found or had an error while dumping
+ */
+ public static boolean dumpService(String name, FileDescriptor fd, String[] args) {
+ IBinder service = ServiceManager.getService(name);
+ if (service == null) {
+ Log.e(TAG, "Can't find service to dump: " + name);
+ return false;
+ }
+
+ try {
+ service.dump(fd, args);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't dump service: " + name, e);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 92041d8..d1d8b0a 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -162,4 +162,15 @@
* Check the status of USB RNDIS support
*/
boolean isUsbRNDISStarted();
+
+ /**
+ * Start Wifi Access Point
+ */
+ void startAccessPoint();
+
+ /**
+ * Stop Wifi Access Point
+ */
+ void stopAccessPoint();
+
}
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
index ecd089a..3442ae7 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -330,7 +330,7 @@
(VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
/* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo";
-
+
public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC_UTF8;
private static final Map<String, Integer> sVCardTypeMap;
@@ -363,6 +363,8 @@
final String loweredKey = vcardTypeString.toLowerCase();
if (sVCardTypeMap.containsKey(loweredKey)) {
return sVCardTypeMap.get(loweredKey);
+ } else if ("default".equalsIgnoreCase(vcardTypeString)) {
+ return VCARD_TYPE_DEFAULT;
} else {
Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
return VCARD_TYPE_DEFAULT;
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 1cf3144..f6135fc 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -61,7 +61,7 @@
private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
- private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
+ private static final String GOOGLE_MY_CONTACTS_GROUP = "My Contacts";
private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 10b9371..f32fb5a 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -211,6 +211,57 @@
new String[] { BookmarkColumns.URL }, null, null, null);
}
+ private static final void addOrUrlEquals(StringBuilder sb) {
+ sb.append(" OR " + BookmarkColumns.URL + " = ");
+ }
+
+ /**
+ * Return a Cursor with all history/bookmarks that are similar to url,
+ * where similar means 'http(s)://' and 'www.' are optional, but the rest
+ * of the url is the same.
+ * @param cr The ContentResolver used to access the database.
+ * @param url The url to compare to.
+ * @hide
+ */
+ public static final Cursor getVisitedLike(ContentResolver cr, String url) {
+ boolean secure = false;
+ String compareString = url;
+ if (compareString.startsWith("http://")) {
+ compareString = compareString.substring(7);
+ } else if (compareString.startsWith("https://")) {
+ compareString = compareString.substring(8);
+ secure = true;
+ }
+ if (compareString.startsWith("www.")) {
+ compareString = compareString.substring(4);
+ }
+ StringBuilder whereClause = null;
+ if (secure) {
+ whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ "https://" + compareString);
+ addOrUrlEquals(whereClause);
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ "https://www." + compareString);
+ } else {
+ whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ compareString);
+ addOrUrlEquals(whereClause);
+ String wwwString = "www." + compareString;
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ wwwString);
+ addOrUrlEquals(whereClause);
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ "http://" + compareString);
+ addOrUrlEquals(whereClause);
+ DatabaseUtils.appendEscapedSQLString(whereClause,
+ "http://" + wwwString);
+ }
+ return cr.query(BOOKMARKS_URI, HISTORY_PROJECTION,
+ whereClause.toString(), null, null);
+ }
+
/**
* Update the visited history to acknowledge that a site has been
* visited.
@@ -225,14 +276,7 @@
String url, boolean real) {
long now = new Date().getTime();
try {
- StringBuilder sb = new StringBuilder(BookmarkColumns.URL + " = ");
- DatabaseUtils.appendEscapedSQLString(sb, url);
- Cursor c = cr.query(
- BOOKMARKS_URI,
- HISTORY_PROJECTION,
- sb.toString(),
- null,
- null);
+ Cursor c = getVisitedLike(cr, url);
/* We should only get one answer that is exactly the same. */
if (c.moveToFirst()) {
ContentValues map = new ContentValues();
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 0b90f91..46c82e4 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2935,6 +2935,13 @@
*/
public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(AUTHORITY_URI,
"phone_lookup");
+
+ /**
+ * The MIME type of {@link #CONTENT_FILTER_URI} providing a directory of phone lookup rows.
+ *
+ * @hide
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone_lookup";
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 18e2647..726f98a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -27,6 +27,7 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -492,6 +493,16 @@
// End of Intent actions for Settings
/**
+ * @hide - Private call() method on SettingsProvider to read from 'system' table.
+ */
+ public static final String CALL_METHOD_GET_SYSTEM = "GET_system";
+
+ /**
+ * @hide - Private call() method on SettingsProvider to read from 'secure' table.
+ */
+ public static final String CALL_METHOD_GET_SECURE = "GET_secure";
+
+ /**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
* This can be passed as an extra field in an Activity Intent with one or more syncable content
@@ -544,23 +555,36 @@
}
}
+ // Thread-safe.
private static class NameValueCache {
private final String mVersionSystemProperty;
private final Uri mUri;
- // Must synchronize(mValues) to access mValues and mValuesVersion.
+ private static final String[] SELECT_VALUE =
+ new String[] { Settings.NameValueTable.VALUE };
+ private static final String NAME_EQ_PLACEHOLDER = "name=?";
+
+ // Must synchronize on 'this' to access mValues and mValuesVersion.
private final HashMap<String, String> mValues = new HashMap<String, String>();
private long mValuesVersion = 0;
- public NameValueCache(String versionSystemProperty, Uri uri) {
+ // Initially null; set lazily and held forever. Synchronized on 'this'.
+ private IContentProvider mContentProvider = null;
+
+ // The method we'll call (or null, to not use) on the provider
+ // for the fast path of retrieving settings.
+ private final String mCallCommand;
+
+ public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) {
mVersionSystemProperty = versionSystemProperty;
mUri = uri;
+ mCallCommand = callCommand;
}
public String getString(ContentResolver cr, String name) {
long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
- synchronized (mValues) {
+ synchronized (this) {
if (mValuesVersion != newValuesVersion) {
if (LOCAL_LOGV) {
Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " +
@@ -576,17 +600,47 @@
}
}
+ IContentProvider cp = null;
+ synchronized (this) {
+ cp = mContentProvider;
+ if (cp == null) {
+ cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
+ }
+ }
+
+ // Try the fast path first, not using query(). If this
+ // fails (alternate Settings provider that doesn't support
+ // this interface?) then we fall back to the query/table
+ // interface.
+ if (mCallCommand != null) {
+ try {
+ Bundle b = cp.call(mCallCommand, name, null);
+ if (b != null) {
+ String value = b.getPairValue();
+ synchronized (this) {
+ mValues.put(name, value);
+ }
+ return value;
+ }
+ // If the response Bundle is null, we fall through
+ // to the query interface below.
+ } catch (RemoteException e) {
+ // Not supported by the remote side? Fall through
+ // to query().
+ }
+ }
+
Cursor c = null;
try {
- c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE },
- Settings.NameValueTable.NAME + "=?", new String[]{name}, null);
+ c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
+ new String[]{name}, null);
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + mUri);
return null;
}
String value = c.moveToNext() ? c.getString(0) : null;
- synchronized (mValues) {
+ synchronized (this) {
mValues.put(name, value);
}
if (LOCAL_LOGV) {
@@ -594,7 +648,7 @@
name + " = " + (value == null ? "(null)" : value));
}
return value;
- } catch (SQLException e) {
+ } catch (RemoteException e) {
Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
return null; // Return null, but don't cache it.
} finally {
@@ -611,7 +665,8 @@
public static final class System extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
- private static volatile NameValueCache mNameValueCache = null;
+ // Populated lazily, guarded by class object:
+ private static NameValueCache sNameValueCache = null;
private static final HashSet<String> MOVED_TO_SECURE;
static {
@@ -660,10 +715,11 @@
+ " to android.provider.Settings.Secure, returning read-only value.");
return Secure.getString(resolver, name);
}
- if (mNameValueCache == null) {
- mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+ CALL_METHOD_GET_SYSTEM);
}
- return mNameValueCache.getString(resolver, name);
+ return sNameValueCache.getString(resolver, name);
}
/**
@@ -1905,7 +1961,8 @@
public static final class Secure extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
- private static volatile NameValueCache mNameValueCache = null;
+ // Populated lazily, guarded by class object:
+ private static NameValueCache sNameValueCache = null;
/**
* Look up a name in the database.
@@ -1914,10 +1971,11 @@
* @return the corresponding value, or null if not present
*/
public synchronized static String getString(ContentResolver resolver, String name) {
- if (mNameValueCache == null) {
- mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+ CALL_METHOD_GET_SECURE);
}
- return mNameValueCache.getString(resolver, name);
+ return sNameValueCache.getString(resolver, name);
}
/**
@@ -2386,6 +2444,13 @@
public static final String WIFI_ON = "wifi_on";
/**
+ * Whether the Wi-Fi AP should be on.
+ *
+ * @hide
+ */
+ public static final String WIFI_AP_ON = "wifi_ap_on";
+
+ /**
* The acceptable packet loss percentage (range 0 - 100) before trying
* another AP on the same network.
*/
@@ -3078,13 +3143,6 @@
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
/**
- * Whether or not a notification is displayed when a Tetherable interface is detected.
- * (0 = false, 1 = true)
- * @hide
- */
- public static final String TETHER_NOTIFY = "tether_notify";
-
- /**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
* @hide
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index b28cf43..f363828 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -274,9 +274,11 @@
private void onDeviceRemoved(String deviceObjectPath) {
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
- if (address != null)
+ if (address != null) {
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
+ mBluetoothService.setRemoteDeviceProperty(address, "UUIDs", null);
+ }
}
/*package*/ void onPropertyChanged(String[] propValues) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 52de64c..444135a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -821,6 +821,7 @@
case MSG_WINDOW_RESIZED: {
final boolean reportDraw = message.arg1 != 0;
mEngine.updateSurface(true, false);
+ mEngine.doOffsetsChanged();
if (reportDraw) {
try {
mEngine.mSession.finishDrawing(mEngine.mWindow);
diff --git a/core/java/android/speech/RecognizerResultsIntent.java b/core/java/android/speech/RecognizerResultsIntent.java
index 228258e..b45e4b1 100644
--- a/core/java/android/speech/RecognizerResultsIntent.java
+++ b/core/java/android/speech/RecognizerResultsIntent.java
@@ -16,6 +16,8 @@
package android.speech;
+import android.os.Bundle;
+
import java.util.ArrayList;
/**
@@ -119,7 +121,23 @@
*/
public static final String EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS =
"android.speech.extras.VOICE_SEARCH_RESULT_HTML_BASE_URLS";
-
+
+ /**
+ * The key to an extra {@link ArrayList} of {@link Bundle}s that contains key/value pairs.
+ * All the values and the keys are {@link String}s. Each key/value pair represents an extra HTTP
+ * header. The keys can't be the standard HTTP headers as they are set by the WebView.
+ *
+ * A list of size 1 may be provided to apply the same HTTP headers to all web results. A
+ * list of the same size as {@link #EXTRA_VOICE_SEARCH_RESULT_STRINGS} may be provided to
+ * apply different HTTP headers to each different web result in the list. These headers will
+ * only be used in the case that the url for a particular web result (from
+ * {@link #EXTRA_VOICE_SEARCH_RESULT_URLS}) is loaded.
+ *
+ * @hide not to be exposed immediately as the implementation details may change
+ */
+ public static final String EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS =
+ "android.speech.extras.EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS";
+
/**
* The scheme used currently for html content in {@link #EXTRA_VOICE_SEARCH_RESULT_HTML}.
*
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index d90045f..1ec8be6 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -48,6 +48,28 @@
mValues = new Object[initialCapacity];
mSize = 0;
}
+
+ /**
+ * @return A copy of all keys contained in the sparse array.
+ */
+ public long[] getKeys() {
+ int length = mKeys.length;
+ long[] result = new long[length];
+ System.arraycopy(mKeys, 0, result, 0, length);
+ return result;
+ }
+
+ /**
+ * Sets all supplied keys to the given unique value.
+ * @param keys Keys to set
+ * @param uniqueValue Value to set all supplied keys to
+ */
+ public void setValues(long[] keys, E uniqueValue) {
+ int length = keys.length;
+ for (int i = 0; i < length; i++) {
+ put(keys[i], uniqueValue);
+ }
+ }
/**
* Gets the Object mapped from the specified key, or <code>null</code>
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 8140d61..d8b6d1f 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -154,9 +154,8 @@
boolean handled = true;
if (!mGestureInProgress) {
- if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
- action == MotionEvent.ACTION_POINTER_2_DOWN) &&
- event.getPointerCount() >= 2) {
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_POINTER_DOWN: {
// We have a new multi-finger gesture
// as orientation can change, query the metrics in touch down
@@ -189,7 +188,7 @@
boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
|| x1 > rightSlop || y1 > bottomSlop;
- if(p0sloppy && p1sloppy) {
+ if (p0sloppy && p1sloppy) {
mFocusX = -1;
mFocusY = -1;
mSloppyGesture = true;
@@ -204,54 +203,61 @@
} else {
mGestureInProgress = mListener.onScaleBegin(this);
}
- } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
- // Initiate sloppy gestures if we've moved outside of the slop area.
- final float edgeSlop = mEdgeSlop;
- final float rightSlop = mRightSlopEdge;
- final float bottomSlop = mBottomSlopEdge;
- final float x0 = event.getRawX();
- final float y0 = event.getRawY();
- final float x1 = getRawX(event, 1);
- final float y1 = getRawY(event, 1);
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mSloppyGesture) {
+ // Initiate sloppy gestures if we've moved outside of the slop area.
+ final float edgeSlop = mEdgeSlop;
+ final float rightSlop = mRightSlopEdge;
+ final float bottomSlop = mBottomSlopEdge;
+ final float x0 = event.getRawX();
+ final float y0 = event.getRawY();
+ final float x1 = getRawX(event, 1);
+ final float y1 = getRawY(event, 1);
- boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
- || x0 > rightSlop || y0 > bottomSlop;
- boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
- || x1 > rightSlop || y1 > bottomSlop;
+ boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
+ || x0 > rightSlop || y0 > bottomSlop;
+ boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
+ || x1 > rightSlop || y1 > bottomSlop;
- if(p0sloppy && p1sloppy) {
- mFocusX = -1;
- mFocusY = -1;
- } else if (p0sloppy) {
- mFocusX = event.getX(1);
- mFocusY = event.getY(1);
- } else if (p1sloppy) {
- mFocusX = event.getX(0);
- mFocusY = event.getY(0);
- } else {
- mSloppyGesture = false;
- mGestureInProgress = mListener.onScaleBegin(this);
+ if(p0sloppy && p1sloppy) {
+ mFocusX = -1;
+ mFocusY = -1;
+ } else if (p0sloppy) {
+ mFocusX = event.getX(1);
+ mFocusY = event.getY(1);
+ } else if (p1sloppy) {
+ mFocusX = event.getX(0);
+ mFocusY = event.getY(0);
+ } else {
+ mSloppyGesture = false;
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
}
- } else if ((action == MotionEvent.ACTION_POINTER_1_UP
- || action == MotionEvent.ACTION_POINTER_2_UP)
- && mSloppyGesture) {
- // Set focus point to the remaining finger
- int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
- >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
- mFocusX = event.getX(id);
- mFocusY = event.getY(id);
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ if (mSloppyGesture) {
+ // Set focus point to the remaining finger
+ int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
+ mFocusX = event.getX(id);
+ mFocusY = event.getY(id);
+ }
+ break;
}
} else {
// Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_1_UP:
- case MotionEvent.ACTION_POINTER_2_UP:
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_POINTER_UP:
// Gesture ended
setContext(event);
// Set focus point to the remaining finger
- int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
- >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+ int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
mFocusX = event.getX(id);
mFocusY = event.getY(id);
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 91fd6f1..c17a724 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -100,6 +100,7 @@
}
private VelocityTracker() {
+ clear();
}
/**
@@ -109,7 +110,7 @@
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;
+ pastTime[p][i] = Long.MIN_VALUE;
}
}
}
@@ -129,7 +130,7 @@
int touchIndex = (mLastTouch + 1) % NUM_PAST;
for (int i=0; i<N; i++) {
for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) {
- mPastTime[id][touchIndex] = 0;
+ mPastTime[id][touchIndex] = Long.MIN_VALUE;
}
for (int p = 0; p < pointerCount; p++) {
int id = ev.getPointerId(p);
@@ -141,10 +142,10 @@
touchIndex = (touchIndex + 1) % NUM_PAST;
}
- // During calculation any pointer values with a time of 0 are treated
- // as a break in input. Initialize all to 0 for each new touch index.
+ // During calculation any pointer values with a time of MIN_VALUE are treated
+ // as a break in input. Initialize all to MIN_VALUE for each new touch index.
for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) {
- mPastTime[id][touchIndex] = 0;
+ mPastTime[id][touchIndex] = Long.MIN_VALUE;
}
final long time = ev.getEventTime();
for (int p = 0; p < pointerCount; p++) {
@@ -189,7 +190,7 @@
// find oldest acceptable time
int oldestTouch = lastTouch;
- if (pastTime[lastTouch] > 0) { // cleared ?
+ if (pastTime[lastTouch] != Long.MIN_VALUE) { // cleared ?
final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME;
int nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST;
while (pastTime[nextOldestTouch] >= acceptableTime &&
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 639441b..7a0c445 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1516,19 +1516,28 @@
/**
* Always allow a user to overscroll this view, provided it is a
* view that can scroll.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_ALWAYS = 0;
+ public static final int OVERSCROLL_ALWAYS = 0;
/**
* Allow a user to overscroll this view only if the content is large
* enough to meaningfully scroll, provided it is a view that can scroll.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
+ public static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
/**
* Never allow a user to overscroll this view.
+ *
+ * @see #getOverscrollMode()
+ * @see #setOverscrollMode(int)
*/
- private static final int OVERSCROLL_NEVER = 2;
+ public static final int OVERSCROLL_NEVER = 2;
/**
* Controls the overscroll mode for this view.
@@ -6493,7 +6502,7 @@
* Create a snapshot of the view into a bitmap. We should probably make
* some form of this public, but should think about the API.
*/
- Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor) {
+ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
int width = mRight - mLeft;
int height = mBottom - mTop;
@@ -8770,6 +8779,38 @@
boolean clampedX, boolean clampedY) {
// Intentionally empty.
}
+
+ /**
+ * Returns the overscroll mode for this view. The result will be
+ * one of {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+ * (allow overscrolling only if the view content is larger than the container),
+ * or {@link #OVERSCROLL_NEVER}.
+ *
+ * @return This view's overscroll mode.
+ */
+ public int getOverscrollMode() {
+ return mOverscrollMode;
+ }
+
+ /**
+ * Set the overscroll mode for this view. Valid overscroll modes are
+ * {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+ * (allow overscrolling only if the view content is larger than the container),
+ * or {@link #OVERSCROLL_NEVER}.
+ *
+ * Setting the overscroll mode of a view will have an effect only if the
+ * view is capable of scrolling.
+ *
+ * @param overscrollMode The new overscroll mode for this view.
+ */
+ public void setOverscrollMode(int overscrollMode) {
+ if (overscrollMode != OVERSCROLL_ALWAYS &&
+ overscrollMode != OVERSCROLL_IF_CONTENT_SCROLLS &&
+ overscrollMode != OVERSCROLL_NEVER) {
+ throw new IllegalArgumentException("Invalid overscroll mode " + overscrollMode);
+ }
+ mOverscrollMode = overscrollMode;
+ }
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 616485e..8311bdc 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -23,9 +23,12 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Rect;
import android.os.Environment;
import android.os.Debug;
+import android.os.RemoteException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.BufferedWriter;
import java.io.FileWriter;
@@ -345,6 +348,7 @@
private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
private static final String REMOTE_PROFILE = "PROFILE";
+ private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
private static HashMap<Class<?>, Field[]> sFieldsForClasses;
private static HashMap<Class<?>, Method[]> sMethodsForClasses;
@@ -846,6 +850,8 @@
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
dump(view, clientStream);
+ } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
+ captureLayers(view, new DataOutputStream(clientStream));
} else {
final String[] params = parameters.split(" ");
if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
@@ -1017,24 +1023,109 @@
return duration[0];
}
+ private static void captureLayers(View root, final DataOutputStream clientStream)
+ throws IOException {
+
+ try {
+ Rect outRect = new Rect();
+ try {
+ root.mAttachInfo.mSession.getDisplayFrame(root.mAttachInfo.mWindow, outRect);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+
+ clientStream.writeInt(outRect.width());
+ clientStream.writeInt(outRect.height());
+
+ captureViewLayer(root, clientStream);
+
+ clientStream.write(2);
+ } finally {
+ clientStream.close();
+ }
+ }
+
+ private static void captureViewLayer(View view, DataOutputStream clientStream)
+ throws IOException {
+
+ if ((view.mPrivateFlags & View.SKIP_DRAW) != View.SKIP_DRAW) {
+ final int id = view.getId();
+ String name = view.getClass().getSimpleName();
+ if (id != View.NO_ID) {
+ name = resolveId(view.getContext(), id).toString();
+ }
+
+ clientStream.write(1);
+ clientStream.writeUTF(name);
+ clientStream.writeByte(view.getVisibility() == View.VISIBLE ? 1 : 0);
+
+ int[] position = new int[2];
+ // XXX: Should happen on the UI thread
+ view.getLocationInWindow(position);
+
+ clientStream.writeInt(position[0]);
+ clientStream.writeInt(position[1]);
+ clientStream.flush();
+
+ Bitmap b = performViewCapture(view, true);
+ if (b != null) {
+ ByteArrayOutputStream arrayOut = new ByteArrayOutputStream(b.getWidth() *
+ b.getHeight() * 2);
+ b.compress(Bitmap.CompressFormat.PNG, 100, arrayOut);
+ clientStream.writeInt(arrayOut.size());
+ arrayOut.writeTo(clientStream);
+ }
+ clientStream.flush();
+ }
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ int count = group.getChildCount();
+
+ for (int i = 0; i < count; i++) {
+ captureViewLayer(group.getChildAt(i), clientStream);
+ }
+ }
+ }
+
private static void capture(View root, final OutputStream clientStream, String parameter)
throws IOException {
final View captureView = findView(root, parameter);
+ Bitmap b = performViewCapture(captureView, false);
+
+ if (b != null) {
+ BufferedOutputStream out = null;
+ try {
+ out = new BufferedOutputStream(clientStream, 32 * 1024);
+ b.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.flush();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ b.recycle();
+ }
+ } else {
+ Log.w("View", "Failed to create capture bitmap!");
+ clientStream.close();
+ }
+ }
+ private static Bitmap performViewCapture(final View captureView, final boolean skpiChildren) {
if (captureView != null) {
final CountDownLatch latch = new CountDownLatch(1);
final Bitmap[] cache = new Bitmap[1];
- root.post(new Runnable() {
+ captureView.post(new Runnable() {
public void run() {
try {
cache[0] = captureView.createSnapshot(
- Bitmap.Config.ARGB_8888, 0);
+ Bitmap.Config.ARGB_8888, 0, skpiChildren);
} catch (OutOfMemoryError e) {
try {
cache[0] = captureView.createSnapshot(
- Bitmap.Config.ARGB_4444, 0);
+ Bitmap.Config.ARGB_4444, 0, skpiChildren);
} catch (OutOfMemoryError e2) {
Log.w("View", "Out of memory for bitmap");
}
@@ -1046,28 +1137,14 @@
try {
latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
-
- if (cache[0] != null) {
- BufferedOutputStream out = null;
- try {
- out = new BufferedOutputStream(clientStream, 32 * 1024);
- cache[0].compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- } finally {
- if (out != null) {
- out.close();
- }
- cache[0].recycle();
- }
- } else {
- Log.w("View", "Failed to create capture bitmap!");
- clientStream.close();
- }
+ return cache[0];
} catch (InterruptedException e) {
Log.w("View", "Could not complete the capture of the view " + captureView);
Thread.currentThread().interrupt();
}
}
+
+ return null;
}
private static void dump(View root, OutputStream clientStream) throws IOException {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9bf36bb..597d583 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1272,6 +1272,19 @@
}
}
+ @Override
+ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
+ int oldCount = mChildrenCount;
+ if (skipChildren) {
+ mChildrenCount = 0;
+ }
+
+ Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
+ mChildrenCount = oldCount;
+
+ return b;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 316bcd6..357cb5fe 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -25,6 +25,8 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -92,6 +94,8 @@
+ InputMethod.SERVICE_META_DATA + " meta-data");
}
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
@@ -105,13 +109,16 @@
"Meta-data does not start with input-method tag");
}
- TypedArray sa = context.getResources().obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.InputMethod);
settingsActivityComponent = sa.getString(
com.android.internal.R.styleable.InputMethod_settingsActivity);
isDefaultResId = sa.getResourceId(
com.android.internal.R.styleable.InputMethod_isDefault, 0);
sa.recycle();
+ } catch (NameNotFoundException e) {
+ throw new XmlPullParserException(
+ "Unable to create context for: " + si.packageName);
} finally {
if (parser != null) parser.close();
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 1d28731..427ce76 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -216,19 +216,23 @@
diff = cookie2.domain.length() - cookie1.domain.length();
if (diff != 0) return diff;
- diff = cookie2.name.hashCode() - cookie1.name.hashCode();
- if (diff != 0) return diff;
-
// If cookie2 has a null value, it should come later in
// the list.
if (cookie2.value == null) {
- return -1;
+ // If both cookies have null values, fall back to using the name
+ // difference.
+ if (cookie1.value != null) {
+ return -1;
+ }
} else if (cookie1.value == null) {
// Now we know that cookie2 does not have a null value, if
// cookie1 has a null value, place it later in the list.
return 1;
}
+ diff = cookie2.name.hashCode() - cookie1.name.hashCode();
+ if (diff != 0) return diff;
+
// cookie1 and cookie2 both have non-null values so we emit a
// warning and treat them as the same.
Log.w(LOGTAG, "Found two cookies with the same value."
@@ -804,9 +808,13 @@
cookie = new Cookie(host, path);
// Cookies like "testcookie; path=/;" are valid and used
- // (lovefilm.se). Check for equal as in the string "testcookie"
- // Check for equalIndex == -1 as in the string "testcookie;"
- if (semicolonIndex <= equalIndex || equalIndex == -1) {
+ // (lovefilm.se).
+ // Look for 2 cases:
+ // 1. "foo" or "foo;" where equalIndex is -1
+ // 2. "foo; path=..." where the first semicolon is before an equal
+ // and a semicolon exists.
+ if ((semicolonIndex != -1 && (semicolonIndex < equalIndex)) ||
+ equalIndex == -1) {
// Fix up the index in case we have a string like "testcookie"
if (semicolonIndex == -1) {
semicolonIndex = length;
@@ -815,7 +823,10 @@
cookie.value = null;
} else {
cookie.name = cookieString.substring(index, equalIndex);
- if (cookieString.charAt(equalIndex + 1) == QUOTATION) {
+ // Make sure we do not throw an exception if the cookie is like
+ // "foo="
+ if ((equalIndex < length - 1) &&
+ (cookieString.charAt(equalIndex + 1) == QUOTATION)) {
index = cookieString.indexOf(QUOTATION, equalIndex + 2);
if (index == -1) {
// bad format, force return
@@ -834,7 +845,7 @@
equalIndex + 1 + MAX_COOKIE_LENGTH);
} else if (equalIndex + 1 == semicolonIndex
|| semicolonIndex < equalIndex) {
- // this is an unusual case like foo=;
+ // this is an unusual case like "foo=;" or "foo="
cookie.value = "";
} else {
cookie.value = cookieString.substring(equalIndex + 1,
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index dacb33f..303e417 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -111,11 +111,10 @@
}
mNetwork = Network.getInstance(mListener.getContext());
if (mListener.isSynchronous()) {
- handleHTTPLoad();
- } else {
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget();
+ return handleHTTPLoad();
}
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget();
return true;
} else if (handleLocalFile(url, mListener, mSettings)) {
return true;
@@ -147,34 +146,53 @@
return true;
}
if (URLUtil.isAssetUrl(url)) {
- // load asset in a separate thread as it involves IO
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER,
- new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
- true)).sendToTarget();
+ if (loadListener.isSynchronous()) {
+ new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
+ true).load();
+ } else {
+ // load asset in a separate thread as it involves IO
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
+ true)).sendToTarget();
+ }
return true;
} else if (URLUtil.isResourceUrl(url)) {
- // load resource in a separate thread as it involves IO
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER,
- new FileLoader(url, loadListener, FileLoader.TYPE_RES,
- true)).sendToTarget();
+ if (loadListener.isSynchronous()) {
+ new FileLoader(url, loadListener, FileLoader.TYPE_RES,
+ true).load();
+ } else {
+ // load resource in a separate thread as it involves IO
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_RES,
+ true)).sendToTarget();
+ }
return true;
} else if (URLUtil.isFileUrl(url)) {
- // load file in a separate thread as it involves IO
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER,
- new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
- settings.getAllowFileAccess())).sendToTarget();
+ if (loadListener.isSynchronous()) {
+ new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
+ settings.getAllowFileAccess()).load();
+ } else {
+ // load file in a separate thread as it involves IO
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
+ settings.getAllowFileAccess())).sendToTarget();
+ }
return true;
} else if (URLUtil.isContentUrl(url)) {
// Send the raw url to the ContentLoader because it will do a
// permission check and the url has to match.
- // load content in a separate thread as it involves IO
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER,
- new ContentLoader(loadListener.url(), loadListener))
- .sendToTarget();
+ if (loadListener.isSynchronous()) {
+ new ContentLoader(loadListener.url(), loadListener).load();
+ } else {
+ // load content in a separate thread as it involves IO
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new ContentLoader(loadListener.url(), loadListener))
+ .sendToTarget();
+ }
return true;
} else if (URLUtil.isDataUrl(url)) {
// load data in the current thread to reduce the latency
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index db19bca..39edcad 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -822,8 +822,9 @@
break;
case 1: // TEXT_AREA
single = false;
- inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
+ inputType = EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
| EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
+ | EditorInfo.TYPE_CLASS_TEXT
| EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
imeOptions |= EditorInfo.IME_ACTION_NONE;
break;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 72791fb..a889476 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Widget;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -24,12 +25,17 @@
import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Interpolator;
+import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
import android.net.Uri;
@@ -70,7 +76,7 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import android.widget.ZoomControls;
@@ -202,6 +208,7 @@
* changes, and then just leave the WebView alone. It'll automatically
* re-orient itself as appropriate.</p>
*/
+@Widget
public class WebView extends AbsoluteLayout
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener {
@@ -455,7 +462,9 @@
// time for the longest scroll animation
private static final int MAX_DURATION = 750; // milliseconds
private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
- private Scroller mScroller;
+ private OverScroller mScroller;
+ private boolean mInOverScrollMode = false;
+ private static Paint mOverScrollBackground;
private boolean mWrapContent;
private static final int MOTIONLESS_FALSE = 0;
@@ -810,7 +819,7 @@
mViewManager = new ViewManager(this);
mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
mDatabase = WebViewDatabase.getInstance(context);
- mScroller = new Scroller(context);
+ mScroller = new OverScroller(context);
mZoomButtonsController = new ZoomButtonsController(this);
mZoomButtonsController.setOnZoomListener(mZoomListener);
@@ -1024,7 +1033,8 @@
* Return the amount of the titlebarview (if any) that is visible
*/
private int getVisibleTitleHeight() {
- return Math.max(getTitleHeight() - mScrollY, 0);
+ // need to restrict mScrollY due to over scroll
+ return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
}
/*
@@ -1254,20 +1264,19 @@
final FileOutputStream out = new FileOutputStream(dest);
p.writeToStream(out);
out.close();
- } catch (FileNotFoundException e){
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- if (dest.length() > 0) {
+ // now update the bundle
b.putInt("scrollX", mScrollX);
b.putInt("scrollY", mScrollY);
b.putFloat("scale", mActualScale);
b.putFloat("textwrapScale", mTextWrapScale);
b.putBoolean("overview", mInZoomOverview);
return true;
+ } catch (FileNotFoundException e){
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
}
return false;
}
@@ -1868,11 +1877,13 @@
// Expects x in view coordinates
private int pinLocX(int x) {
+ if (mInOverScrollMode) return x;
return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
}
// Expects y in view coordinates
private int pinLocY(int y) {
+ if (mInOverScrollMode) return y;
int titleH = getTitleHeight();
// if the titlebar is still visible, just pin against 0
if (y <= titleH) {
@@ -2270,6 +2281,24 @@
scrollBar.draw(canvas);
}
+ @Override
+ protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
+ boolean clampedY) {
+ mInOverScrollMode = false;
+ int maxX = computeMaxScrollX();
+ if (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f && maxX == 0) {
+ // do not over scroll x if the page can't be zoomed and it just fits
+ // the screen
+ scrollX = pinLocX(scrollX);
+ } else if (scrollX < 0 || scrollX > maxX) {
+ mInOverScrollMode = true;
+ }
+ if (scrollY < 0 || scrollY > computeMaxScrollY()) {
+ mInOverScrollMode = true;
+ }
+ super.scrollTo(scrollX, scrollY);
+ }
+
/**
* Get the url for the current page. This is not always the same as the url
* passed to WebViewClient.onPageStarted because although the load for
@@ -2480,20 +2509,30 @@
*/
public int findAll(String find) {
if (0 == mNativeClass) return 0; // client isn't initialized
- if (mFindIsUp == false) {
- recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
- false);
- mFindIsUp = true;
- }
- int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
+ int result = find != null ? nativeFindAll(find.toLowerCase(),
+ find.toUpperCase()) : 0;
invalidate();
mLastFind = find;
return result;
}
+ /**
+ * @hide
+ */
+ public void setFindIsUp(boolean isUp) {
+ mFindIsUp = isUp;
+ if (isUp) {
+ recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
+ false);
+ }
+ if (0 == mNativeClass) return; // client isn't initialized
+ nativeSetFindIsUp(isUp);
+ }
+
// Used to know whether the find dialog is open. Affects whether
// or not we draw the highlights for matches.
private boolean mFindIsUp;
+
private int mFindHeight;
// Keep track of the last string sent, so we can search again after an
// orientation change or the dismissal of the soft keyboard.
@@ -2553,14 +2592,21 @@
* Clear the highlighting surrounding text matches created by findAll.
*/
public void clearMatches() {
+ mLastFind = "";
if (mNativeClass == 0)
return;
- if (mFindIsUp) {
- recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
- false);
- mFindIsUp = false;
- }
- nativeSetFindIsUp();
+ nativeSetFindIsEmpty();
+ invalidate();
+ }
+
+ /**
+ * @hide
+ */
+ public void notifyFindDialogDismissed() {
+ clearMatches();
+ setFindIsUp(false);
+ recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
+ false);
// Now that the dialog has been removed, ensure that we scroll to a
// location that is not beyond the end of the page.
pinScrollTo(mScrollX, mScrollY, false, 0);
@@ -2595,13 +2641,14 @@
if (mScroller.computeScrollOffset()) {
int oldX = mScrollX;
int oldY = mScrollY;
- mScrollX = mScroller.getCurrX();
- mScrollY = mScroller.getCurrY();
+ int x = mScroller.getCurrX();
+ int y = mScroller.getCurrY();
postInvalidate(); // So we draw again
- if (oldX != mScrollX || oldY != mScrollY) {
- // as onScrollChanged() is not called, sendOurVisibleRect()
- // needs to be call explicitly
- sendOurVisibleRect();
+ if (oldX != x || oldY != y) {
+ overscrollBy(x - oldX, y - oldY, oldX, oldY,
+ computeMaxScrollX(), computeMaxScrollY(),
+ getViewWidth() / 3, getViewHeight() / 3);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
} else {
super.computeScroll();
@@ -3012,8 +3059,13 @@
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child == mTitleBar) {
// When drawing the title bar, move it horizontally to always show
- // at the top of the WebView.
+ // at the top of the WebView. While overscroll, stick the title bar
+ // on the top otherwise we may have two during loading, one is drawn
+ // here, another is drawn by the Browser.
mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
+ if (mScrollY <= 0) {
+ mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
+ }
}
return super.drawChild(canvas, child, drawingTime);
}
@@ -3041,6 +3093,29 @@
}
int saveCount = canvas.save();
+ if (mInOverScrollMode) {
+ if (mOverScrollBackground == null) {
+ mOverScrollBackground = new Paint();
+ Bitmap bm = BitmapFactory.decodeResource(
+ mContext.getResources(),
+ com.android.internal.R.drawable.pattern_underwear);
+ mOverScrollBackground.setShader(new BitmapShader(bm,
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ }
+ int top = getTitleHeight();
+ // first draw the background and anchor to the top of the view
+ canvas.save();
+ canvas.translate(mScrollX, mScrollY);
+ canvas.clipRect(-mScrollX, top - mScrollY,
+ computeHorizontalScrollRange() - mScrollX, top
+ + computeVerticalScrollRange() - mScrollY,
+ Region.Op.DIFFERENCE);
+ canvas.drawPaint(mOverScrollBackground);
+ canvas.restore();
+ // next clip the region for the content
+ canvas.clipRect(0, top, computeHorizontalScrollRange(), top
+ + computeVerticalScrollRange());
+ }
if (mTitleBar != null) {
canvas.translate(0, (int) mTitleBar.getHeight());
}
@@ -3058,12 +3133,12 @@
canvas.restoreToCount(saveCount);
// Now draw the shadow.
- if (mTitleBar != null) {
- int y = mScrollY + getVisibleTitleHeight();
+ int titleH = getVisibleTitleHeight();
+ if (mTitleBar != null && titleH == 0) {
int height = (int) (5f * getContext().getResources()
.getDisplayMetrics().density);
- mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
- y + height);
+ mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
+ mScrollY + height);
mTitleShadow.draw(canvas);
}
if (AUTO_REDRAW_HACK && mAutoRedraw) {
@@ -3138,27 +3213,6 @@
}
}
- private static class Metrics {
- int mScrollX;
- int mScrollY;
- int mWidth;
- int mHeight;
- float mInvScale;
- }
-
- private Metrics getViewMetrics() {
- Metrics metrics = new Metrics();
- metrics.mScrollX = mScrollX;
- metrics.mScrollY = computeVerticalScrollOffset();
- metrics.mWidth = getWidth();
- metrics.mHeight = getHeight() - getVisibleTitleHeight();
- if (mFindIsUp) {
- metrics.mHeight -= mFindHeight;
- }
- metrics.mInvScale = mInvActualScale;
- return metrics;
- }
-
private void drawExtras(Canvas canvas, int extras) {
// If mNativeClass is 0, we should not reach here, so we do not
// need to check it again.
@@ -3265,7 +3319,7 @@
if (!animateScroll) {
extras = DRAW_EXTRAS_FIND;
}
- } else if (mShiftIsPressed) {
+ } else if (mShiftIsPressed && !nativeFocusIsPlugin()) {
if (!animateZoom && !mPreviewZoomOnly) {
extras = DRAW_EXTRAS_SELECTION;
nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
@@ -3393,7 +3447,6 @@
if (isTextView) {
rebuildWebTextView();
if (inEditingMode()) {
- mWebTextView.setDefaultSelection();
imm.showSoftInput(mWebTextView, 0);
if (zoom) {
didUpdateTextViewBounds(true);
@@ -3621,6 +3674,7 @@
}
if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
+ && !nativeFocusIsPlugin()
&& (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
setUpSelectXY();
@@ -3628,8 +3682,11 @@
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
- // always handle the navigation keys in the UI thread
switchOutDrawHistory();
+ if (nativeFocusIsPlugin()) {
+ letPluginHandleNavKey(keyCode, event.getEventTime(), true);
+ return true;
+ }
if (mShiftIsPressed) {
int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
@@ -3639,7 +3696,7 @@
moveSelection(xRate * multiplier, yRate * multiplier);
return true;
}
- if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
+ if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
playSoundEffect(keyCodeToSoundsEffect(keyCode));
return true;
}
@@ -3650,7 +3707,7 @@
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
switchOutDrawHistory();
if (event.getRepeatCount() == 0) {
- if (mShiftIsPressed) {
+ if (mShiftIsPressed && !nativeFocusIsPlugin()) {
return true; // discard press if copy in progress
}
mGotCenterDown = true;
@@ -3766,6 +3823,10 @@
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+ if (nativeFocusIsPlugin()) {
+ letPluginHandleNavKey(keyCode, event.getEventTime(), false);
+ return true;
+ }
// always handle the navigation keys in the UI thread
// Bubble up the key event as WebView doesn't handle it
return false;
@@ -3776,7 +3837,7 @@
mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
mGotCenterDown = false;
- if (mShiftIsPressed) {
+ if (mShiftIsPressed && !nativeFocusIsPlugin()) {
if (mExtendSelection) {
commitCopy();
} else {
@@ -3842,6 +3903,10 @@
nativeHideCursor();
}
+ /**
+ * Use this method to put the WebView into text selection mode.
+ * Do not rely on this functionality; it will be deprecated in the future.
+ */
public void emulateShiftHeld() {
if (0 == mNativeClass) return; // client isn't initialized
setUpSelectXY();
@@ -4477,12 +4542,12 @@
y = getViewHeightWithTitle() - 1;
}
- // pass the touch events from UI thread to WebCore thread
- if (mForwardTouchEvents
- && (action != MotionEvent.ACTION_MOVE || eventTime
- - mLastSentTouchTime > mCurrentTouchInterval)
- && (action == MotionEvent.ACTION_DOWN
- || mPreventDrag != PREVENT_DRAG_CANCEL)) {
+ // pass the touch events, except ACTION_MOVE which will be handled
+ // later, from UI thread to WebCore thread
+ if (mFullScreenHolder != null || (mForwardTouchEvents
+ && action != MotionEvent.ACTION_MOVE
+ && (action == MotionEvent.ACTION_DOWN || mPreventDrag
+ != PREVENT_DRAG_CANCEL))) {
WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
ted.mAction = action;
ted.mX = viewToContentX((int) x + mScrollX);
@@ -4573,6 +4638,21 @@
if ((deltaX * deltaX + deltaY * deltaY) < mTouchSlopSquare) {
break;
}
+
+ // pass the first ACTION_MOVE from UI thread to WebCore
+ // thread after the distance is confirmed that it is a drag
+ if (mFullScreenHolder == null && mForwardTouchEvents
+ && mPreventDrag != PREVENT_DRAG_CANCEL) {
+ WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
+ ted.mAction = action;
+ ted.mX = viewToContentX((int) x + mScrollX);
+ ted.mY = viewToContentY((int) y + mScrollY);
+ ted.mEventTime = eventTime;
+ ted.mMetaState = ev.getMetaState();
+ mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+ mLastSentTouchTime = eventTime;
+ }
+
if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
// track mLastTouchTime as we may need to do fling at
// ACTION_UP
@@ -4629,21 +4709,23 @@
Toast.LENGTH_LONG).show();
}
}
+ } else {
+ // pass the touch events from UI thread to WebCore thread
+ if (mFullScreenHolder == null && mForwardTouchEvents
+ && eventTime - mLastSentTouchTime > mCurrentTouchInterval
+ && mPreventDrag != PREVENT_DRAG_CANCEL) {
+ WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
+ ted.mAction = action;
+ ted.mX = viewToContentX((int) x + mScrollX);
+ ted.mY = viewToContentY((int) y + mScrollY);
+ ted.mEventTime = eventTime;
+ ted.mMetaState = ev.getMetaState();
+ mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+ mLastSentTouchTime = eventTime;
+ }
}
// do pan
- int newScrollX = pinLocX(mScrollX + deltaX);
- int newDeltaX = newScrollX - mScrollX;
- if (deltaX != newDeltaX) {
- deltaX = newDeltaX;
- fDeltaX = (float) newDeltaX;
- }
- int newScrollY = pinLocY(mScrollY + deltaY);
- int newDeltaY = newScrollY - mScrollY;
- if (deltaY != newDeltaY) {
- deltaY = newDeltaY;
- fDeltaY = (float) newDeltaY;
- }
boolean done = false;
boolean keepScrollBarsVisible = false;
if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -4688,7 +4770,9 @@
}
}
if ((deltaX | deltaY) != 0) {
- scrollBy(deltaX, deltaY);
+ overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
+ computeMaxScrollX(), computeMaxScrollY(),
+ getViewWidth() / 3, getViewHeight() / 3);
if (deltaX != 0) {
mLastTouchX = x;
}
@@ -4771,8 +4855,8 @@
Log.w(LOGTAG, "Miss a drag as we are waiting for" +
" WebCore's response for touch down.");
if (mFullScreenHolder == null
- && (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
- || computeVerticalScrollExtent() < computeVerticalScrollRange())) {
+ && (computeMaxScrollX() > 0
+ || computeMaxScrollY() > 0)) {
// remove the pending TOUCH_EVENT and send a
// cancel
mWebViewCore
@@ -4818,6 +4902,12 @@
mVelocityTracker.addMovement(ev);
doFling();
break;
+ } else {
+ if (mScroller.springback(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0,
+ computeMaxScrollY())) {
+ invalidate();
+ }
}
mLastVelocity = 0;
WebViewCore.resumePriority();
@@ -4838,6 +4928,12 @@
}
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
+ if (mTouchMode == TOUCH_DRAG_MODE) {
+ if (mScroller.springback(mScrollX, mScrollY, 0,
+ computeMaxScrollX(), 0, computeMaxScrollY())) {
+ invalidate();
+ }
+ }
break;
}
}
@@ -4926,7 +5022,7 @@
return true;
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (mShiftIsPressed) {
+ if (mShiftIsPressed && !nativeFocusIsPlugin()) {
return true; // discard press if copy in progress
}
mTrackballDown = true;
@@ -4951,7 +5047,7 @@
mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
mTrackballDown = false;
mTrackballUpTime = time;
- if (mShiftIsPressed) {
+ if (mShiftIsPressed && !nativeFocusIsPlugin()) {
if (mExtendSelection) {
commitCopy();
} else {
@@ -5089,7 +5185,7 @@
float yRate = mTrackballRemainsY * 1000 / elapsed;
int viewWidth = getViewWidth();
int viewHeight = getViewHeight();
- if (mShiftIsPressed) {
+ if (mShiftIsPressed && !nativeFocusIsPlugin()) {
moveSelection(scaleTrackballX(xRate, viewWidth),
scaleTrackballY(yRate, viewHeight));
mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -5127,7 +5223,12 @@
+ " mTrackballRemainsX=" + mTrackballRemainsX
+ " mTrackballRemainsY=" + mTrackballRemainsY);
}
- if (navHandledKey(selectKeyCode, count, false, time, false)) {
+ if (nativeFocusIsPlugin()) {
+ for (int i = 0; i < count; i++) {
+ letPluginHandleNavKey(selectKeyCode, time, true);
+ letPluginHandleNavKey(selectKeyCode, time, false);
+ }
+ } else if (navHandledKey(selectKeyCode, count, false, time)) {
playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
}
mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -5156,16 +5257,18 @@
}
}
+ private int computeMaxScrollX() {
+ return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ }
+
private int computeMaxScrollY() {
- int maxContentH = computeVerticalScrollRange() + getTitleHeight();
- return Math.max(maxContentH - getViewHeightWithTitle(), getTitleHeight());
+ return Math.max(computeVerticalScrollRange() + getTitleHeight()
+ - getViewHeightWithTitle(), getTitleHeight());
}
public void flingScroll(int vx, int vy) {
- int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
- int maxY = computeMaxScrollY();
-
- mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
+ mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
+ computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
invalidate();
}
@@ -5173,7 +5276,7 @@
if (mVelocityTracker == null) {
return;
}
- int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ int maxX = computeMaxScrollX();
int maxY = computeMaxScrollY();
mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
@@ -5195,6 +5298,10 @@
}
if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
WebViewCore.resumePriority();
+ if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
+ 0, computeMaxScrollY())) {
+ invalidate();
+ }
return;
}
float currentVelocity = mScroller.getCurrVelocity();
@@ -5222,7 +5329,8 @@
mLastVelY = vy;
mLastVelocity = (float) Math.hypot(vx, vy);
- mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
+ mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
+ getViewWidth() / 3, getViewHeight() / 3);
// TODO: duration is calculated based on velocity, if the range is
// small, the animation will stop before duration is up. We may
// want to calculate how long the animation is going to run to precisely
@@ -5597,7 +5705,7 @@
return result;
}
if (mNativeClass != 0 && !nativeHasCursorNode()) {
- navHandledKey(fakeKeyDirection, 1, true, 0, true);
+ navHandledKey(fakeKeyDirection, 1, true, 0);
}
}
}
@@ -5879,40 +5987,43 @@
WebViewCore.RestoreState restoreState = draw.mRestoreState;
boolean hasRestoreState = restoreState != null;
if (hasRestoreState) {
- mInZoomOverview = false;
updateZoomRange(restoreState, viewSize.x,
draw.mMinPrefWidth, true);
- if (mInitialScaleInPercent > 0) {
- setNewZoomScale(mInitialScaleInPercent / 100.0f,
+ if (!mDrawHistory) {
+ mInZoomOverview = false;
+
+ if (mInitialScaleInPercent > 0) {
+ setNewZoomScale(mInitialScaleInPercent / 100.0f,
mInitialScaleInPercent != mTextWrapScale * 100,
false);
- } else if (restoreState.mViewScale > 0) {
- mTextWrapScale = restoreState.mTextWrapScale;
- setNewZoomScale(restoreState.mViewScale, false,
+ } else if (restoreState.mViewScale > 0) {
+ mTextWrapScale = restoreState.mTextWrapScale;
+ setNewZoomScale(restoreState.mViewScale, false,
false);
- } else {
- mInZoomOverview = useWideViewport
- && settings.getLoadWithOverviewMode();
- float scale;
- if (mInZoomOverview) {
- scale = (float) viewWidth
- / DEFAULT_VIEWPORT_WIDTH;
} else {
- scale = restoreState.mTextWrapScale;
- }
- setNewZoomScale(scale, Math.abs(scale
+ mInZoomOverview = useWideViewport
+ && settings.getLoadWithOverviewMode();
+ float scale;
+ if (mInZoomOverview) {
+ scale = (float) viewWidth
+ / DEFAULT_VIEWPORT_WIDTH;
+ } else {
+ scale = restoreState.mTextWrapScale;
+ }
+ setNewZoomScale(scale, Math.abs(scale
- mTextWrapScale) >= 0.01f, false);
- }
- setContentScrollTo(restoreState.mScrollX,
+ }
+ setContentScrollTo(restoreState.mScrollX,
restoreState.mScrollY);
- // As we are on a new page, remove the WebTextView. This
- // is necessary for page loads driven by webkit, and in
- // particular when the user was on a password field, so
- // the WebTextView was visible.
- clearTextEntry(false);
- // update the zoom buttons as the scale can be changed
- if (getSettings().getBuiltInZoomControls()) {
- updateZoomButtonsEnabled();
+ // As we are on a new page, remove the WebTextView. This
+ // is necessary for page loads driven by webkit, and in
+ // particular when the user was on a password field, so
+ // the WebTextView was visible.
+ clearTextEntry(false);
+ // update the zoom buttons as the scale can be changed
+ if (getSettings().getBuiltInZoomControls()) {
+ updateZoomButtonsEnabled();
+ }
}
}
// We update the layout (i.e. request a layout from the
@@ -6020,7 +6131,7 @@
}
break;
case MOVE_OUT_OF_PLUGIN:
- navHandledKey(msg.arg1, 1, false, 0, true);
+ navHandledKey(msg.arg1, 1, false, 0);
break;
case UPDATE_TEXT_ENTRY_MSG_ID:
// this is sent after finishing resize in WebViewCore. Make
@@ -6687,21 +6798,34 @@
invalidate();
}
- // return true if the key was handled
- private boolean navHandledKey(int keyCode, int count, boolean noScroll,
- long time, boolean ignorePlugin) {
- if (mNativeClass == 0) {
- return false;
+ /**
+ * Pass the key to the plugin. This assumes that nativeFocusIsPlugin()
+ * returned true.
+ */
+ private void letPluginHandleNavKey(int keyCode, long time, boolean down) {
+ int keyEventAction;
+ int eventHubAction;
+ if (down) {
+ keyEventAction = KeyEvent.ACTION_DOWN;
+ eventHubAction = EventHub.KEY_DOWN;
+ playSoundEffect(keyCodeToSoundsEffect(keyCode));
+ } else {
+ keyEventAction = KeyEvent.ACTION_UP;
+ eventHubAction = EventHub.KEY_UP;
}
- if (ignorePlugin == false && nativeFocusIsPlugin()) {
- KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
- , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
+ KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
+ 1, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
| (false ? KeyEvent.META_ALT_ON : 0) // FIXME
| (false ? KeyEvent.META_SYM_ON : 0) // FIXME
, 0, 0, 0);
- mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
- mWebViewCore.sendMessage(EventHub.KEY_UP, event);
- return true;
+ mWebViewCore.sendMessage(eventHubAction, event);
+ }
+
+ // return true if the key was handled
+ private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+ long time) {
+ if (mNativeClass == 0) {
+ return false;
}
mLastCursorTime = time;
mLastCursorBounds = nativeGetCursorRingBounds();
@@ -6863,7 +6987,8 @@
private native void nativeRecordButtons(boolean focused,
boolean pressed, boolean invalidate);
private native void nativeSelectBestAt(Rect rect);
- private native void nativeSetFindIsUp();
+ private native void nativeSetFindIsEmpty();
+ private native void nativeSetFindIsUp(boolean isUp);
private native void nativeSetFollowedLink(boolean followed);
private native void nativeSetHeightCanMeasure(boolean measure);
private native void nativeSetRootLayer(int layer);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 71f69fe..b339015 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2191,7 +2191,14 @@
);
}
data.mWidth = Math.round(webViewWidth / data.mScale);
- data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth;
+ // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
+ // first layout before we sync our webview dimensions to it. In that case, we
+ // request the real height of the webview. This is not a perfect solution as we
+ // are calling a WebView method from the WebCore thread. But this is preferable
+ // to syncing an incorrect height.
+ data.mHeight = mCurrentViewHeight == 0 ?
+ Math.round(mWebView.getViewHeight() / data.mScale)
+ : mCurrentViewHeight * data.mWidth / viewportWidth;
data.mTextWrapWidth = Math.round(webViewWidth
/ mRestoreState.mTextWrapScale);
data.mIgnoreHeight = false;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 2ea6abc..06880a1 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2067,10 +2067,10 @@
// Remember where the motion event started
v = getChildAt(motionPosition - mFirstPosition);
mMotionViewOriginalTop = v.getTop();
- mMotionX = x;
- mMotionY = y;
- mMotionPosition = motionPosition;
}
+ mMotionX = x;
+ mMotionY = y;
+ mMotionPosition = motionPosition;
mLastY = Integer.MIN_VALUE;
break;
}
@@ -2387,6 +2387,7 @@
mVelocityTracker.clear();
}
}
+ awakenScrollBars();
}
private int getOverscrollMax() {
@@ -4032,6 +4033,17 @@
@ViewDebug.ExportedProperty
boolean recycledHeaderFooter;
+ /**
+ * When an AbsListView is measured with an AT_MOST measure spec, it needs
+ * to obtain children views to measure itself. When doing so, the children
+ * are not attached to the window, but put in the recycler which assumes
+ * they've been attached before. Setting this flag will force the reused
+ * view to be attached to the window rather than just attached to the
+ * parent.
+ */
+ @ViewDebug.ExportedProperty
+ boolean forceAdd;
+
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 9c43e9b..01d3a4a 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -67,7 +67,7 @@
private int mMaxExpGroupCount = Integer.MAX_VALUE;
/** Change observer used to have ExpandableListAdapter changes pushed to us */
- private DataSetObserver mDataSetObserver = new MyDataSetObserver();
+ private final DataSetObserver mDataSetObserver = new MyDataSetObserver();
/**
* Constructs the connector
@@ -849,7 +849,7 @@
* position to either a) group position for groups, or b) child position for
* children
*/
- static class GroupMetadata implements Parcelable, Comparable {
+ static class GroupMetadata implements Parcelable, Comparable<GroupMetadata> {
final static int REFRESH = -1;
/** This group's flat list position */
@@ -885,12 +885,12 @@
return gm;
}
- public int compareTo(Object another) {
- if (another == null || !(another instanceof GroupMetadata)) {
- throw new ClassCastException();
+ public int compareTo(GroupMetadata another) {
+ if (another == null) {
+ throw new IllegalArgumentException();
}
- return gPos - ((GroupMetadata) another).gPos;
+ return gPos - another.gPos;
}
public int describeContents() {
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 9cc8bd5..8bd797b 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -389,7 +389,8 @@
// Only proceed as possible child if the divider isn't above all items (if it is above
// all items, then the item below it has to be a group)
if (flatListPosition >= 0) {
- PositionMetadata pos = mConnector.getUnflattenedPos(flatListPosition);
+ final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
+ PositionMetadata pos = mConnector.getUnflattenedPos(adjustedPosition);
// If this item is a child, or it is a non-empty group that is expanded
if ((pos.position.type == ExpandableListPosition.CHILD) || (pos.isExpanded() &&
pos.groupMetadata.lastChildFlPos != pos.groupMetadata.flPos)) {
@@ -482,11 +483,37 @@
return mAdapter;
}
+ /**
+ * @param position An absolute (including header and footer) flat list position.
+ * @return true if the position corresponds to a header or a footer item.
+ */
private boolean isHeaderOrFooterPosition(int position) {
final int footerViewsStart = mItemCount - getFooterViewsCount();
return (position < getHeaderViewsCount() || position >= footerViewsStart);
}
+ /**
+ * Converts an absolute item flat position into a group/child flat position, shifting according
+ * to the number of header items.
+ *
+ * @param flatListPosition The absolute flat position
+ * @return A group/child flat position as expected by the connector.
+ */
+ private int getFlatPositionForConnector(int flatListPosition) {
+ return flatListPosition - getHeaderViewsCount();
+ }
+
+ /**
+ * Converts a group/child flat position into an absolute flat position, that takes into account
+ * the possible headers.
+ *
+ * @param flatListPosition The child/group flat position
+ * @return An absolute flat position.
+ */
+ private int getAbsoluteFlatPosition(int flatListPosition) {
+ return flatListPosition + getHeaderViewsCount();
+ }
+
@Override
public boolean performItemClick(View v, int position, long id) {
// Ignore clicks in header/footers
@@ -496,8 +523,8 @@
}
// Internally handle the item click
- final int headerViewsCount = getHeaderViewsCount();
- return handleItemClick(v, position - headerViewsCount, id);
+ final int adjustedPosition = getFlatPositionForConnector(position);
+ return handleItemClick(v, adjustedPosition, id);
}
/**
@@ -711,8 +738,8 @@
return PACKED_POSITION_VALUE_NULL;
}
- final int shiftedPosition = flatListPosition - getHeaderViewsCount();
- PositionMetadata pm = mConnector.getUnflattenedPos(shiftedPosition);
+ final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
+ PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
long packedPos = pm.position.getPackedPosition();
pm.recycle();
return packedPos;
@@ -732,9 +759,9 @@
public int getFlatListPosition(long packedPosition) {
PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition
.obtainPosition(packedPosition));
- int retValue = pm.position.flatListPos;
+ final int flatListPosition = pm.position.flatListPos;
pm.recycle();
- return retValue + getHeaderViewsCount();
+ return getAbsoluteFlatPosition(flatListPosition);
}
/**
@@ -783,7 +810,8 @@
.obtainGroupPosition(groupPosition);
PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
elGroupPos.recycle();
- super.setSelection(pm.position.flatListPos);
+ final int absoluteFlatPosition = getAbsoluteFlatPosition(pm.position.flatListPos);
+ super.setSelection(absoluteFlatPosition);
pm.recycle();
}
@@ -819,7 +847,8 @@
}
}
- super.setSelection(flatChildPos.position.flatListPos);
+ int absoluteFlatPosition = getAbsoluteFlatPosition(flatChildPos.position.flatListPos);
+ super.setSelection(absoluteFlatPosition);
elChildPos.recycle();
flatChildPos.recycle();
@@ -937,7 +966,7 @@
return new AdapterContextMenuInfo(view, flatListPosition, id);
}
- final int adjustedPosition = flatListPosition - getHeaderViewsCount();
+ final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
ExpandableListPosition pos = pm.position;
pm.recycle();
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9e930a5..be633be 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -938,6 +938,7 @@
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(0);
+ p.forceAdd = true;
int childHeightSpec = getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
@@ -1257,9 +1258,10 @@
}
p.viewType = mAdapter.getItemViewType(position);
- if (recycled) {
+ if (recycled && !p.forceAdd) {
attachViewToParent(child, where, p);
} else {
+ p.forceAdd = false;
addViewInLayout(child, where, p, true);
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index acb7e02..d80a5e3 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -318,11 +318,7 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Let the focused view and/or our descendants get the key first
- boolean handled = super.dispatchKeyEvent(event);
- if (handled) {
- return true;
- }
- return executeKeyEvent(event);
+ return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}
/**
@@ -509,8 +505,11 @@
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0,
getOverscrollMax(), 0);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
break;
case MotionEvent.ACTION_UP:
@@ -524,7 +523,7 @@
fling(-initialVelocity);
} else {
final int right = getScrollRange();
- if (mScroller.springback(mScrollX, mScrollY, 0, 0, right, 0)) {
+ if (mScroller.springback(mScrollX, mScrollY, 0, right, 0, 0)) {
invalidate();
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 88f2e12..1a9c0df5 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -30,6 +30,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -131,6 +132,7 @@
private int mChoiceMode = CHOICE_MODE_NONE;
private SparseBooleanArray mCheckStates;
+ private LongSparseArray<Boolean> mCheckedIdStates;
// used for temporary calculations.
private final Rect mTempRect = new Rect();
@@ -453,6 +455,12 @@
checkSelectionChanged();
}
+ if (mChoiceMode != CHOICE_MODE_NONE &&
+ mAdapter.hasStableIds() &&
+ mCheckedIdStates == null) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
+
} else {
mAreAllItemsSelectable = true;
checkFocus();
@@ -463,6 +471,10 @@
if (mCheckStates != null) {
mCheckStates.clear();
}
+
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
requestLayout();
}
@@ -1109,6 +1121,7 @@
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(position);
+ p.forceAdd = true;
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
@@ -1743,10 +1756,11 @@
}
p.viewType = mAdapter.getItemViewType(position);
- if (recycled || (p.recycledHeaderFooter &&
+ if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&
p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
attachViewToParent(child, flowDown ? -1 : 0, p);
} else {
+ p.forceAdd = false;
if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
p.recycledHeaderFooter = true;
}
@@ -3308,8 +3322,13 @@
*/
public void setChoiceMode(int choiceMode) {
mChoiceMode = choiceMode;
- if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates == null) {
- mCheckStates = new SparseBooleanArray();
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ if (mCheckStates == null) {
+ mCheckStates = new SparseBooleanArray();
+ }
+ if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
}
}
@@ -3321,14 +3340,25 @@
handled = true;
if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
- boolean oldValue = mCheckStates.get(position, false);
- mCheckStates.put(position, !oldValue);
+ boolean newValue = !mCheckStates.get(position, false);
+ mCheckStates.put(position, newValue);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (newValue) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
} else {
- boolean oldValue = mCheckStates.get(position, false);
- if (!oldValue) {
+ boolean newValue = !mCheckStates.get(position, false);
+ if (newValue) {
mCheckStates.clear();
mCheckStates.put(position, true);
- }
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates.clear();
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
+ }
}
mDataChanged = true;
@@ -3356,16 +3386,30 @@
if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
mCheckStates.put(position, value);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (value) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
} else {
+ boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
// Clear all values if we're checking something, or unchecking the currently
// selected item
if (value || isItemChecked(position)) {
mCheckStates.clear();
+ if (updateIds) {
+ mCheckedIdStates.clear();
+ }
}
// this may end up selecting the value we just cleared but this way
// we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
if (value) {
mCheckStates.put(position, true);
+ if (updateIds) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
}
}
@@ -3431,38 +3475,39 @@
/**
* Returns the set of checked items ids. The result is only valid if the
- * choice mode has not been set to {@link #CHOICE_MODE_SINGLE}.
+ * choice mode has not been set to {@link #CHOICE_MODE_NONE}.
+ *
+ * @return A new array which contains the id of each checked item in the
+ * list.
+ *
+ * @deprecated Use {@link #getCheckedItemIds()} instead.
+ */
+ public long[] getCheckItemIds() {
+ return getCheckedItemIds();
+ }
+
+ /**
+ * Returns the set of checked items ids. The result is only valid if the
+ * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
+ * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
*
* @return A new array which contains the id of each checked item in the
* list.
*/
- public long[] getCheckItemIds() {
- if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null && mAdapter != null) {
- final SparseBooleanArray states = mCheckStates;
- final int count = states.size();
- final long[] ids = new long[count];
- final ListAdapter adapter = mAdapter;
-
- int checkedCount = 0;
- for (int i = 0; i < count; i++) {
- if (states.valueAt(i)) {
- ids[checkedCount++] = adapter.getItemId(states.keyAt(i));
- }
- }
-
- // Trim array if needed. mCheckStates may contain false values
- // resulting in checkedCount being smaller than count.
- if (checkedCount == count) {
- return ids;
- } else {
- final long[] result = new long[checkedCount];
- System.arraycopy(ids, 0, result, 0, checkedCount);
-
- return result;
- }
+ public long[] getCheckedItemIds() {
+ if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
+ return new long[0];
}
-
- return new long[0];
+
+ final LongSparseArray<Boolean> idStates = mCheckedIdStates;
+ final int count = idStates.size();
+ final long[] ids = new long[count];
+
+ for (int i = 0; i < count; i++) {
+ ids[i] = idStates.keyAt(i);
+ }
+
+ return ids;
}
/**
@@ -3472,17 +3517,23 @@
if (mCheckStates != null) {
mCheckStates.clear();
}
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
}
static class SavedState extends BaseSavedState {
SparseBooleanArray checkState;
+ LongSparseArray<Boolean> checkIdState;
/**
* Constructor called from {@link ListView#onSaveInstanceState()}
*/
- SavedState(Parcelable superState, SparseBooleanArray checkState) {
+ SavedState(Parcelable superState, SparseBooleanArray checkState,
+ LongSparseArray<Boolean> checkIdState) {
super(superState);
this.checkState = checkState;
+ this.checkIdState = checkIdState;
}
/**
@@ -3491,12 +3542,19 @@
private SavedState(Parcel in) {
super(in);
checkState = in.readSparseBooleanArray();
+ long[] idState = in.createLongArray();
+
+ if (idState.length > 0) {
+ checkIdState = new LongSparseArray<Boolean>();
+ checkIdState.setValues(idState, Boolean.TRUE);
+ }
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeSparseBooleanArray(checkState);
+ out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
}
@Override
@@ -3521,7 +3579,7 @@
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
- return new SavedState(superState, mCheckStates);
+ return new SavedState(superState, mCheckStates, mCheckedIdStates);
}
@Override
@@ -3534,5 +3592,8 @@
mCheckStates = ss.checkState;
}
+ if (ss.checkIdState != null) {
+ mCheckedIdStates = ss.checkIdState;
+ }
}
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index fd18db4..582d9e4 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -473,10 +473,13 @@
private int getSelectedPos(String str) {
if (mDisplayedValues == null) {
- return Integer.parseInt(str);
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ /* Ignore as if it's not a number we don't care */
+ }
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
-
/* Don't force the user to type in jan when ja will do */
str = str.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index b11caa1..8469c8b 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -394,4 +394,56 @@
public float getCurrVelocity() {
return mCurrScroller.getCurrVelocity();
}
+
+ /**
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+ *
+ * @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
+ */
+ public void extendDuration(int extend) {
+ if (mScrollMode == MODE_DEFAULT) {
+ mDefaultScroller.extendDuration(extend);
+ }
+ }
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ */
+ public void setFinalX(int newX) {
+ if (mScrollMode == MODE_DEFAULT) {
+ if (newX < mMinimumX) {
+ mMinimumX = newX;
+ }
+ if (newX > mMaximumX) {
+ mMaximumX = newX;
+ }
+ mDefaultScroller.setFinalX(newX);
+ }
+ }
+
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ */
+ public void setFinalY(int newY) {
+ if (mScrollMode == MODE_DEFAULT) {
+ if (newY < mMinimumY) {
+ mMinimumY = newY;
+ }
+ if (newY > mMaximumY) {
+ mMaximumY = newY;
+ }
+ mDefaultScroller.setFinalY(newY);
+ }
+ }
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 489c44d..d7dfa86 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -22,7 +22,6 @@
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -52,8 +51,6 @@
* <p>ScrollView only supports vertical scrolling.
*/
public class ScrollView extends FrameLayout {
- private static final String TAG = "ScrollView";
-
static final int ANIMATED_SCROLL_GAP = 250;
static final float MAX_SCROLL_FACTOR = 0.5f;
@@ -318,11 +315,7 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Let the focused view and/or our descendants get the key first
- boolean handled = super.dispatchKeyEvent(event);
- if (handled) {
- return true;
- }
- return executeKeyEvent(event);
+ return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}
/**
@@ -510,8 +503,11 @@
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
0, getOverscrollMax());
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
break;
case MotionEvent.ACTION_UP:
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 51e1c57..7b21be5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1376,15 +1376,18 @@
dr.mDrawableLeft.setCallback(null);
}
dr.mDrawableLeft = left;
- if (dr.mDrawableTop != left && dr.mDrawableTop != null) {
+
+ if (dr.mDrawableTop != top && dr.mDrawableTop != null) {
dr.mDrawableTop.setCallback(null);
}
dr.mDrawableTop = top;
- if (dr.mDrawableRight != left && dr.mDrawableRight != null) {
+
+ if (dr.mDrawableRight != right && dr.mDrawableRight != null) {
dr.mDrawableRight.setCallback(null);
}
dr.mDrawableRight = right;
- if (dr.mDrawableBottom != left && dr.mDrawableBottom != null) {
+
+ if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) {
dr.mDrawableBottom.setCallback(null);
}
dr.mDrawableBottom = bottom;
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index c2517a8..fb8b5aa 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -484,6 +484,9 @@
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
if (mMediaController != null) mMediaController.hide();
+ if (mCurrentState != STATE_SUSPEND) {
+ release(true);
+ }
}
};
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index 2f48499..37898a1 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -28,6 +28,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Power;
import android.os.ServiceManager;
@@ -56,6 +57,8 @@
private final Object mBroadcastDoneSync = new Object();
private boolean mBroadcastDone;
private Context mContext;
+ private PowerManager mPowerManager;
+ private PowerManager.WakeLock mWakeLock;
private Handler mHandler;
private ShutdownThread() {
@@ -125,6 +128,18 @@
// start the thread that initiates shutdown
sInstance.mContext = context;
+ sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ sInstance.mWakeLock = null;
+ if (sInstance.mPowerManager.isScreenOn()) {
+ try {
+ sInstance.mWakeLock = sInstance.mPowerManager.newWakeLock(
+ PowerManager.FULL_WAKE_LOCK, "Shutdown");
+ sInstance.mWakeLock.acquire();
+ } catch (SecurityException e) {
+ Log.w(TAG, "No permission to acquire wake lock", e);
+ sInstance.mWakeLock = null;
+ }
+ }
sInstance.mHandler = new Handler() {
};
sInstance.start();
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
deleted file mode 100644
index 5d71231..0000000
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2007 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.app;
-
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.widget.Toast;
-import android.util.Log;
-
-/**
- * This activity is shown to the user in two cases: when a connection is possible via
- * a usb tether and when any type of tether is connected. In the connecting case
- * It allows them to start a USB tether. In the Tethered/disconnecting case it
- * will disconnect all tethers.
- */
-public class TetherActivity extends AlertActivity implements
- DialogInterface.OnClickListener {
-
- private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
-
- // count of the number of tethered connections at activity create time.
- private int mTethered;
-
- /* Used to detect when the USB cable is unplugged, so we can call finish() */
- private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == ConnectivityManager.ACTION_TETHER_STATE_CHANGED) {
- handleTetherStateChanged(intent);
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // determine if we advertise tethering or untethering
- ConnectivityManager cm =
- (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- mTethered = cm.getTetheredIfaces().length;
- int tetherable = cm.getTetherableIfaces().length;
- if ((mTethered == 0) && (tetherable == 0)) {
- finish();
- return;
- }
-
- // Set up the dialog
- // if we have a tethered connection we put up a "Do you want to Disconect" dialog
- // otherwise we must have a tetherable interface (else we'd return above)
- // and so we want to put up the "do you want to connect" dialog
- if (mTethered == 0) {
- mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
- mAlertParams.mTitle = getString(com.android.internal.R.string.tether_title);
- mAlertParams.mMessage = getString(com.android.internal.R.string.tether_message);
- mAlertParams.mPositiveButtonText =
- getString(com.android.internal.R.string.tether_button);
- mAlertParams.mPositiveButtonListener = this;
- mAlertParams.mNegativeButtonText =
- getString(com.android.internal.R.string.tether_button_cancel);
- mAlertParams.mNegativeButtonListener = this;
- } else {
- mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
- mAlertParams.mTitle = getString(com.android.internal.R.string.tether_stop_title);
- mAlertParams.mMessage = getString(com.android.internal.R.string.tether_stop_message);
- mAlertParams.mPositiveButtonText =
- getString(com.android.internal.R.string.tether_stop_button);
- mAlertParams.mPositiveButtonListener = this;
- mAlertParams.mNegativeButtonText =
- getString(com.android.internal.R.string.tether_stop_button_cancel);
- mAlertParams.mNegativeButtonListener = this;
- }
- setupAlert();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- registerReceiver(mTetherReceiver, new IntentFilter(
- ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- unregisterReceiver(mTetherReceiver);
- }
-
- /**
- * {@inheritDoc}
- */
- public void onClick(DialogInterface dialog, int which) {
- boolean error = false;
-
- if (which == POSITIVE_BUTTON) {
- ConnectivityManager cm =
- (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- // start/stop tethering
- String[] tethered = cm.getTetheredIfaces();
-
- if (tethered.length == 0) {
- String[] tetherable = cm.getTetherableIfaces();
- String[] usbRegexs = cm.getTetherableUsbRegexs();
- for (String t : tetherable) {
- for (String r : usbRegexs) {
- if (t.matches(r)) {
- if (!cm.tether(t))
- error = true;
- break;
- }
- }
- }
- if (error) {
- showTetheringError();
- }
- } else {
- for (String t : tethered) {
- if (!cm.untether(t)) {
- error = true;
- }
- }
- if (error) {
- showUnTetheringError();
- }
- }
- }
- // No matter what, finish the activity
- finish();
- }
-
- private void handleTetherStateChanged(Intent intent) {
- // determine if we advertise tethering or untethering
- ConnectivityManager cm =
- (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- if (mTethered != cm.getTetheredIfaces().length) {
- finish();
- }
- }
-
- private void showTetheringError() {
- Toast.makeText(this, com.android.internal.R.string.tether_error_message,
- Toast.LENGTH_LONG).show();
- }
-
- private void showUnTetheringError() {
- Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
- Toast.LENGTH_LONG).show();
- }
-
-}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 9da1066..b535fc1 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -16,7 +16,7 @@
package com.android.internal.backup;
-import android.backup.RestoreSet;
+import android.app.backup.RestoreSet;
import android.content.pm.PackageInfo;
import android.os.ParcelFileDescriptor;
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 23ec647..be83eb9 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -1,8 +1,8 @@
package com.android.internal.backup;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
-import android.backup.RestoreSet;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.RestoreSet;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index c5db83f..04a10b9 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -54,11 +54,13 @@
// Create mount point via MountService
IMountService mountService = getMountService();
long len = tmpPackageFile.length();
- int mbLen = (int) (len/(1024*1024));
+ int mbLen = (int) (len >> 20);
if ((len - (mbLen * 1024 * 1024)) > 0) {
mbLen++;
}
- if (localLOGV) Log.i(TAG, "Size of resource " + mbLen);
+ // Add buffer size
+ mbLen++;
+ if (localLOGV) Log.i(TAG, "Size of container " + mbLen + " MB " + len + " bytes");
try {
int rc = mountService.createSecureContainer(
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0f1b845..88bbafd 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -527,6 +527,53 @@
}
}
+static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
+ const SkBitmap* bm1) {
+ if (bm0->width() != bm1->width() ||
+ bm0->height() != bm1->height() ||
+ bm0->config() != bm1->config()) {
+ return false;
+ }
+
+ SkAutoLockPixels alp0(*bm0);
+ SkAutoLockPixels alp1(*bm1);
+
+ // if we can't load the pixels, return false
+ if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
+ return false;
+ }
+
+ if (bm0->config() == SkBitmap::kIndex8_Config) {
+ SkColorTable* ct0 = bm0->getColorTable();
+ SkColorTable* ct1 = bm1->getColorTable();
+ if (NULL == ct0 || NULL == ct1) {
+ return false;
+ }
+ if (ct0->count() != ct1->count()) {
+ return false;
+ }
+
+ SkAutoLockColors alc0(ct0);
+ SkAutoLockColors alc1(ct1);
+ const size_t size = ct0->count() * sizeof(SkPMColor);
+ if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
+ return false;
+ }
+ }
+
+ // now compare each scanline. We can't do the entire buffer at once,
+ // since we don't care about the pixel values that might extend beyond
+ // the width (since the scanline might be larger than the logical width)
+ const int h = bm0->height();
+ const size_t size = bm0->width() * bm0->bytesPerPixel();
+ for (int y = 0; y < h; y++) {
+ if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
bitmap->lockPixels();
bitmap->unlockPixels();
@@ -567,7 +614,8 @@
(void*)Bitmap_copyPixelsToBuffer },
{ "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
(void*)Bitmap_copyPixelsFromBuffer },
- { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw }
+ { "nativeSameAs", "(II)Z", (void*)Bitmap_sameAs },
+ { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw },
};
#define kClassPathName "android/graphics/Bitmap"
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index cf8a8e8..b03dd16 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -28,7 +28,7 @@
// java.io.FileDescriptor
static jfieldID s_descriptorField = 0;
-// android.backup.BackupDataInput$EntityHeader
+// android.app.backup.BackupDataInput$EntityHeader
static jfieldID s_keyField = 0;
static jfieldID s_dataSizeField = 0;
@@ -130,7 +130,7 @@
static const JNINativeMethod g_methods[] = {
{ "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native },
{ "dtor", "(I)V", (void*)dtor_native },
- { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I",
+ { "readNextHeader_native", "(ILandroid/app/backup/BackupDataInput$EntityHeader;)I",
(void*)readNextHeader_native },
{ "readEntityData_native", "(I[BII)I", (void*)readEntityData_native },
{ "skipEntityData_native", "(I)I", (void*)skipEntityData_native },
@@ -148,16 +148,16 @@
LOG_FATAL_IF(s_descriptorField == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
- clazz = env->FindClass("android/backup/BackupDataInput$EntityHeader");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader");
+ clazz = env->FindClass("android/app/backup/BackupDataInput$EntityHeader");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataInput.EntityHeader");
s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;");
LOG_FATAL_IF(s_keyField == NULL,
- "Unable to find key field in android.backup.BackupDataInput.EntityHeader");
+ "Unable to find key field in android.app.backup.BackupDataInput.EntityHeader");
s_dataSizeField = env->GetFieldID(clazz, "dataSize", "I");
LOG_FATAL_IF(s_dataSizeField == NULL,
- "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader");
+ "Unable to find dataSize field in android.app.backup.BackupDataInput.EntityHeader");
- return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput",
+ return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupDataInput",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index ce30aaa8..b895d11 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -121,7 +121,7 @@
LOG_FATAL_IF(s_descriptorField == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
- return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataOutput",
+ return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupDataOutput",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
index 2e3f0b9..26e7d66 100644
--- a/core/jni/android_backup_BackupHelperDispatcher.cpp
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -219,16 +219,16 @@
static const JNINativeMethod g_methods[] = {
{ "readHeader_native",
- "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
(void*)readHeader_native },
{ "skipChunk_native",
"(Ljava/io/FileDescriptor;I)I",
(void*)skipChunk_native },
{ "allocateHeader_native",
- "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+ "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
(void*)allocateHeader_native },
{ "writeHeader_native",
- "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
+ "(Landroid/app/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
(void*)writeHeader_native },
};
@@ -242,17 +242,17 @@
LOG_FATAL_IF(s_descriptorField == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
- clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
+ clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header");
LOG_FATAL_IF(clazz == NULL,
- "Unable to find class android.backup.BackupHelperDispatcher.Header");
+ "Unable to find class android.app.backup.BackupHelperDispatcher.Header");
s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
LOG_FATAL_IF(s_chunkSizeField == NULL,
- "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
+ "Unable to find chunkSize field in android.app.backup.BackupHelperDispatcher.Header");
s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
LOG_FATAL_IF(s_keyPrefixField == NULL,
- "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
+ "Unable to find keyPrefix field in android.app.backup.BackupHelperDispatcher.Header");
- return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
+ return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupHelperDispatcher",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp
index 8225a36..0137a06 100644
--- a/core/jni/android_backup_FileBackupHelperBase.cpp
+++ b/core/jni/android_backup_FileBackupHelperBase.cpp
@@ -129,7 +129,7 @@
LOG_FATAL_IF(s_descriptorField == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
- return AndroidRuntime::registerNativeMethods(env, "android/backup/FileBackupHelperBase",
+ return AndroidRuntime::registerNativeMethods(env, "android/app/backup/FileBackupHelperBase",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 0bee8c0..44e091d 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -63,23 +63,32 @@
static jfieldID offset_db_handle;
+static char *createStr(const char *path) {
+ int len = strlen(path);
+ char *str = (char *)malloc(len + 1);
+ strncpy(str, path, len);
+ str[len] = NULL;
+ return str;
+}
+
static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
- LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
+ LOGI("sqlite returned: database = %s, error code = %d, msg = %s\n",
+ (char *)databaseName, iErrCode, zMsg);
}
// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
-static void registerLoggingFunc() {
+static void registerLoggingFunc(const char *path) {
static bool loggingFuncSet = false;
if (loggingFuncSet) {
return;
}
LOGV("Registering sqlite logging func \n");
- //int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, 0);
- //if (err != SQLITE_OK) {
- //LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
- //#return;
- //}
+ int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path));
+ if (err != SQLITE_OK) {
+ LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
+ return;
+ }
loggingFuncSet = true;
}
@@ -93,7 +102,7 @@
int sqliteFlags;
// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
- registerLoggingFunc();
+ registerLoggingFunc(path8);
// convert our flags into the sqlite flags
if (flags & CREATE_IF_NECESSARY) {
@@ -172,10 +181,7 @@
LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
return NULL; // VM would have thrown OutOfMemoryError
}
- int len = strlen(path);
- char *dbNameStr = (char *)malloc(len + 1);
- strncpy(dbNameStr, path, len);
- dbNameStr[len-1] = NULL;
+ char *dbNameStr = createStr(path);
env->ReleaseStringUTFChars(databaseName, path);
return dbNameStr;
}
@@ -361,7 +367,7 @@
if (err != SQLITE_OK) {
LOGE("register_localized_collators() failed setting locale\n");
throw_sqlite3_exception(env, handle);
- goto done;
+ goto rollback;
}
err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
@@ -409,7 +415,9 @@
}
rollback:
- sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
+ if (err != SQLITE_OK) {
+ sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
+ }
done:
if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index f845878..a3be309 100755
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -42,6 +42,7 @@
static const GpsXtraInterface* sGpsXtraInterface = NULL;
static const AGpsInterface* sAGpsInterface = NULL;
static const GpsNiInterface* sGpsNiInterface = NULL;
+static const GpsDebugInterface* sGpsDebugInterface = NULL;
// data written to by GPS callbacks
static GpsLocation sGpsLocation;
@@ -57,7 +58,7 @@
GpsUtcTime timestamp;
char nmea[NMEA_SENTENCE_LENGTH];
};
-static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_LENGTH];
+static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_COUNT];
static int mNmeaSentenceCount = 0;
// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event
@@ -66,7 +67,7 @@
static GpsStatus sGpsStatusCopy;
static GpsSvStatus sGpsSvStatusCopy;
static AGpsStatus sAGpsStatusCopy;
-static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_LENGTH];
+static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_COUNT];
static GpsNiNotification sGpsNiNotificationCopy;
enum CallbackType {
@@ -226,6 +227,9 @@
if (sGpsNiInterface)
sGpsNiInterface->init(&sGpsNiCallbacks);
+ if (!sGpsDebugInterface)
+ sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
+
return true;
}
@@ -472,11 +476,26 @@
static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
jint notifId, jint response)
{
- if (!sGpsNiInterface)
- sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
- if (sGpsNiInterface) {
- sGpsNiInterface->respond(notifId, response);
- }
+ if (!sGpsNiInterface) {
+ sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ }
+ if (sGpsNiInterface) {
+ sGpsNiInterface->respond(notifId, response);
+ }
+}
+
+static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
+{
+ jstring result = NULL;
+ if (sGpsDebugInterface) {
+ const size_t maxLength = 2047;
+ char buffer[maxLength+1];
+ size_t length = sGpsDebugInterface->get_internal_state(buffer, maxLength);
+ if (length > maxLength) length = maxLength;
+ buffer[length] = 0;
+ result = env->NewStringUTF(buffer);
+ }
+ return result;
}
static JNINativeMethod sMethods[] = {
@@ -501,6 +520,7 @@
{"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
{"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server},
{"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response},
+ {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
};
int register_android_location_GpsLocationProvider(JNIEnv* env)
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 5afa0342..a2b7cc4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "asset"
#define DEBUG_STYLES(x) //x
+#define THROW_ON_BAD_ID 0
#include <android_runtime/android_util_AssetManager.h>
@@ -719,9 +720,21 @@
ResTable_config config;
uint32_t typeSpecFlags;
ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags, &config);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+#endif
uint32_t ref = ident;
if (resolve) {
block = res.resolveReference(&value, block, &ref);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+#endif
}
return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block;
}
@@ -763,6 +776,12 @@
uint32_t ref = ident;
if (resolve) {
block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+#endif
}
return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
}
@@ -852,6 +871,12 @@
uint32_t ref = 0;
if (resolve) {
block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+#endif
}
return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
}
@@ -1071,6 +1096,12 @@
value.dataType, value.data));
newBlock = res.resolveReference(&value, block, &resid,
&typeSetFlags, &config);
+#if THROW_ON_BAD_ID
+ if (newBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return JNI_FALSE;
+ }
+#endif
if (newBlock >= 0) block = newBlock;
DEBUG_STYLES(LOGI("-> Resolved theme: type=0x%x, data=0x%08x",
value.dataType, value.data));
@@ -1207,6 +1238,12 @@
//printf("Resolving attribute reference\n");
ssize_t newBlock = res.resolveReference(&value, block, &resid,
&typeSetFlags, &config);
+#if THROW_ON_BAD_ID
+ if (newBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return JNI_FALSE;
+ }
+#endif
if (newBlock >= 0) block = newBlock;
}
@@ -1314,6 +1351,12 @@
//printf("Resolving attribute reference\n");
ssize_t newBlock = res.resolveReference(&value, block, &resid,
&typeSetFlags, &config);
+#if THROW_ON_BAD_ID
+ if (newBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return JNI_FALSE;
+ }
+#endif
if (newBlock >= 0) block = newBlock;
}
@@ -1421,6 +1464,13 @@
stringIndex = value.data;
}
+#if THROW_ON_BAD_ID
+ if (stringBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+#endif
+
//todo: It might be faster to allocate a C array to contain
// the blocknums and indices, put them in there and then
// do just one SetIntArrayRegion()
@@ -1469,6 +1519,12 @@
// Take care of resolving the found resource to its final value.
ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+#endif
if (value.dataType == Res_value::TYPE_STRING) {
const ResStringPool* pool = res.getTableStringBlock(block);
const char* str8 = pool->string8At(value.data, &strLen);
@@ -1520,6 +1576,12 @@
// Take care of resolving the found resource to its final value.
ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
+#if THROW_ON_BAD_ID
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+#endif
if (value.dataType >= Res_value::TYPE_FIRST_INT
&& value.dataType <= Res_value::TYPE_LAST_INT) {
int intVal = value.data;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 63584ed..f1e614b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -52,7 +52,13 @@
<protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
+ <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
+
<protected-broadcast android:name="android.backup.intent.RUN" />
<protected-broadcast android:name="android.backup.intent.CLEAR" />
<protected-broadcast android:name="android.backup.intent.INIT" />
@@ -1271,10 +1277,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name="com.android.internal.app.TetherActivity"
- android:theme="@style/Theme.Dialog.Alert"
- android:excludeFromRecents="true">
- </activity>
<activity android:name="com.android.server.status.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png
index 5697369..42c7c146 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
index 9940245..01e2506 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
index 5a26d83..83c6eb3 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
index 089dbf3..e047eaf 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
index c10a3db..218a2d2 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
index 9e83ace..afe4951 100644
--- a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
index 44668b3..eda6e16 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
index 3a4571e..4158ac4 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
index 60dc632..6f68f25 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_search_play.png b/core/res/res/drawable-hdpi/ic_btn_search_play.png
new file mode 100644
index 0000000..fb1b974
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_search_play.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_silent_mode.png b/core/res/res/drawable-hdpi/ic_lock_silent_mode.png
index 00e1960..0d4c590 100644
--- a/core/res/res/drawable-hdpi/ic_lock_silent_mode.png
+++ b/core/res/res/drawable-hdpi/ic_lock_silent_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png b/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png
index 6b4ce89..17d705c 100644
--- a/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png
+++ b/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_silent_mode_vibrate.png b/core/res/res/drawable-hdpi/ic_lock_silent_mode_vibrate.png
new file mode 100644
index 0000000..4503aceb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_silent_mode_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_vibrate.png b/core/res/res/drawable-hdpi/ic_vibrate.png
index aa83534..ca23372 100644
--- a/core/res/res/drawable-hdpi/ic_vibrate.png
+++ b/core/res/res/drawable-hdpi/ic_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_vibrate_small.png b/core/res/res/drawable-hdpi/ic_vibrate_small.png
new file mode 100644
index 0000000..61b8bd9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_vibrate_small.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume.png b/core/res/res/drawable-hdpi/ic_volume.png
index 7714f6a..bf538ee 100644
--- a/core/res/res/drawable-hdpi/ic_volume.png
+++ b/core/res/res/drawable-hdpi/ic_volume.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_off.png b/core/res/res/drawable-hdpi/ic_volume_off.png
index 313dd5b..aa344083 100644
--- a/core/res/res/drawable-hdpi/ic_volume_off.png
+++ b/core/res/res/drawable-hdpi/ic_volume_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_off_small.png b/core/res/res/drawable-hdpi/ic_volume_off_small.png
index 62322ec..1329414 100644
--- a/core/res/res/drawable-hdpi/ic_volume_off_small.png
+++ b/core/res/res/drawable-hdpi/ic_volume_off_small.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_small.png b/core/res/res/drawable-hdpi/ic_volume_small.png
index 96c7948..4e9a7ea 100644
--- a/core/res/res/drawable-hdpi/ic_volume_small.png
+++ b/core/res/res/drawable-hdpi/ic_volume_small.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate1.png b/core/res/res/drawable-hdpi/progressbar_indeterminate1.png
old mode 100644
new mode 100755
index ea88e32..197b34d
--- a/core/res/res/drawable-hdpi/progressbar_indeterminate1.png
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate2.png b/core/res/res/drawable-hdpi/progressbar_indeterminate2.png
old mode 100644
new mode 100755
index 436c48c..c6cf008
--- a/core/res/res/drawable-hdpi/progressbar_indeterminate2.png
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate3.png b/core/res/res/drawable-hdpi/progressbar_indeterminate3.png
old mode 100644
new mode 100755
index ea88e32..bf129e0
--- a/core/res/res/drawable-hdpi/progressbar_indeterminate3.png
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_empty_default.9.png b/core/res/res/drawable-hdpi/textfield_search_empty_default.9.png
new file mode 100644
index 0000000..c0b84da
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_empty_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_empty_pressed.9.png b/core/res/res/drawable-hdpi/textfield_search_empty_pressed.9.png
new file mode 100644
index 0000000..0a0fc6b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_empty_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_empty_selected.9.png b/core/res/res/drawable-hdpi/textfield_search_empty_selected.9.png
new file mode 100644
index 0000000..04813c2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_empty_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android.png b/core/res/res/drawable-hdpi/usb_android.png
index f6f899a..8b5b1a9 100644
--- a/core/res/res/drawable-hdpi/usb_android.png
+++ b/core/res/res/drawable-hdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android_connected.png b/core/res/res/drawable-hdpi/usb_android_connected.png
index 583ca00..442f2b3 100644
--- a/core/res/res/drawable-hdpi/usb_android_connected.png
+++ b/core/res/res/drawable-hdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_search_play.png b/core/res/res/drawable-mdpi/ic_btn_search_play.png
new file mode 100644
index 0000000..dc25dae
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_search_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_silent_mode.png b/core/res/res/drawable-mdpi/ic_lock_silent_mode.png
index 439a6f5..5c3a226 100644
--- a/core/res/res/drawable-mdpi/ic_lock_silent_mode.png
+++ b/core/res/res/drawable-mdpi/ic_lock_silent_mode.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png b/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png
index fc7e960..95257a3 100644
--- a/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png
+++ b/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_silent_mode_vibrate.png b/core/res/res/drawable-mdpi/ic_lock_silent_mode_vibrate.png
new file mode 100644
index 0000000..7da79aa
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lock_silent_mode_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_vibrate.png b/core/res/res/drawable-mdpi/ic_vibrate.png
old mode 100755
new mode 100644
index eb24e50..4fecce1
--- a/core/res/res/drawable-mdpi/ic_vibrate.png
+++ b/core/res/res/drawable-mdpi/ic_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_vibrate_small.png b/core/res/res/drawable-mdpi/ic_vibrate_small.png
new file mode 100644
index 0000000..f04804e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_vibrate_small.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_volume.png b/core/res/res/drawable-mdpi/ic_volume.png
old mode 100755
new mode 100644
index cee70f0..20aa030
--- a/core/res/res/drawable-mdpi/ic_volume.png
+++ b/core/res/res/drawable-mdpi/ic_volume.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_volume_off.png b/core/res/res/drawable-mdpi/ic_volume_off.png
index f3850fc..fefb9c4 100644
--- a/core/res/res/drawable-mdpi/ic_volume_off.png
+++ b/core/res/res/drawable-mdpi/ic_volume_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_volume_off_small.png b/core/res/res/drawable-mdpi/ic_volume_off_small.png
old mode 100755
new mode 100644
index ae55bd6..529298c
--- a/core/res/res/drawable-mdpi/ic_volume_off_small.png
+++ b/core/res/res/drawable-mdpi/ic_volume_off_small.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_volume_small.png b/core/res/res/drawable-mdpi/ic_volume_small.png
old mode 100755
new mode 100644
index 00a4f89..2a7ec03
--- a/core/res/res/drawable-mdpi/ic_volume_small.png
+++ b/core/res/res/drawable-mdpi/ic_volume_small.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progressbar_indeterminate1.png b/core/res/res/drawable-mdpi/progressbar_indeterminate1.png
index 5eddb30..71780ef 100644
--- a/core/res/res/drawable-mdpi/progressbar_indeterminate1.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progressbar_indeterminate2.png b/core/res/res/drawable-mdpi/progressbar_indeterminate2.png
index 4ca3a63..236988b 100644
--- a/core/res/res/drawable-mdpi/progressbar_indeterminate2.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progressbar_indeterminate3.png b/core/res/res/drawable-mdpi/progressbar_indeterminate3.png
index da8e601..1570235 100644
--- a/core/res/res/drawable-mdpi/progressbar_indeterminate3.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_empty_default.9.png b/core/res/res/drawable-mdpi/textfield_search_empty_default.9.png
new file mode 100644
index 0000000..515117f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_empty_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_empty_pressed.9.png b/core/res/res/drawable-mdpi/textfield_search_empty_pressed.9.png
new file mode 100644
index 0000000..a01f763
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_empty_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_empty_selected.9.png b/core/res/res/drawable-mdpi/textfield_search_empty_selected.9.png
new file mode 100644
index 0000000..611276f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_empty_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android.png b/core/res/res/drawable-mdpi/usb_android.png
index df1afbb..492b6e1 100644
--- a/core/res/res/drawable-mdpi/usb_android.png
+++ b/core/res/res/drawable-mdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android_connected.png b/core/res/res/drawable-mdpi/usb_android_connected.png
index fca77a7..3dd2950 100644
--- a/core/res/res/drawable-mdpi/usb_android_connected.png
+++ b/core/res/res/drawable-mdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
new file mode 100644
index 0000000..651212f
--- /dev/null
+++ b/core/res/res/drawable/pattern_underwear.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_search_empty.xml b/core/res/res/drawable/textfield_search_empty.xml
new file mode 100644
index 0000000..55eec83
--- /dev/null
+++ b/core/res/res/drawable/textfield_search_empty.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/textfield_search_empty_default" />
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/textfield_search_empty_pressed" />
+
+ <item android:state_enabled="true" android:state_focused="true"
+ android:drawable="@drawable/textfield_search_empty_selected" />
+
+ <item android:state_enabled="true"
+ android:drawable="@drawable/textfield_search_empty_default" />
+
+</selector>
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index c665f7a..6e4e5e1 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -31,4 +31,5 @@
android:layout_marginTop="5px"
android:cacheColorHint="@null"
android:divider="@android:drawable/divider_horizontal_bright"
- android:scrollbars="vertical" />
+ android:scrollbars="vertical"
+ android:overscrollMode="ifContentScrolls" />
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0c8bafc..72fd0ad 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -185,18 +185,12 @@
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"允許應用程式為其他程式開啟偵錯功能。請注意:惡意程式可利用此功能終止其他應用程式。"</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"變更介面設定"</string>
<string name="permdesc_changeConfiguration" msgid="3465121501528064399">"允許應用程式變更目前設定,例如:地區設定或字型大小。"</string>
- <!-- no translation found for permlab_enableCarMode (5684504058192921098) -->
- <skip />
- <!-- no translation found for permdesc_enableCarMode (5673461159384850628) -->
- <skip />
- <!-- no translation found for permlab_killBackgroundProcesses (8373714752793061963) -->
- <skip />
- <!-- no translation found for permdesc_killBackgroundProcesses (2908829602869383753) -->
- <skip />
- <!-- no translation found for permlab_forceStopPackages (1447830113260156236) -->
- <skip />
- <!-- no translation found for permdesc_forceStopPackages (7263036616161367402) -->
- <skip />
+ <string name="permlab_enableCarMode" msgid="5684504058192921098">"啟用行車模式"</string>
+ <string name="permdesc_enableCarMode" msgid="5673461159384850628">"允許應用程式啟用行車模式。"</string>
+ <string name="permlab_killBackgroundProcesses" msgid="8373714752793061963">"關閉背景程序"</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="2908829602869383753">"允許應用程式關閉其他應用程式背景程序 (即使記憶體足夠)。"</string>
+ <string name="permlab_forceStopPackages" msgid="1447830113260156236">"強制停止應用程式"</string>
+ <string name="permdesc_forceStopPackages" msgid="7263036616161367402">"允許應用程式強制停止其他應用程式。"</string>
<string name="permlab_forceBack" msgid="1804196839880393631">"強制關閉應用程式"</string>
<string name="permdesc_forceBack" msgid="6534109744159919013">"允許應用程式強制關閉在前端運作的活動並返回。一般應用程式不需要此功能。"</string>
<string name="permlab_dump" msgid="1681799862438954752">"接收系統內部狀態"</string>
@@ -237,10 +231,8 @@
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"允許擁有人連結至輸入法的最頂層介面。一般應用程式不需使用此選項。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"連結至桌布"</string>
<string name="permdesc_bindWallpaper" msgid="5287754520361915347">"允許擁有人連結至桌布的最頂層介面,一般應用程式不需使用此選項。"</string>
- <!-- no translation found for permlab_bindDeviceAdmin (8704986163711455010) -->
- <skip />
- <!-- no translation found for permdesc_bindDeviceAdmin (8714424333082216979) -->
- <skip />
+ <string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"與裝置管理員互動"</string>
+ <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允許應用程式將調用請求 (intent) 傳送至裝置管理員;一般應用程式不需使用此選項。"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方向"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"允許應用程式隨時變更螢幕顯示方向。一般應用程式不需要此功能。"</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"傳送 Linux 訊號到應用程式"</string>
@@ -324,26 +316,16 @@
<string name="permdesc_mount_unmount_filesystems" msgid="6253263792535859767">"允許應用程式掛載/卸載抽取式儲存設備的檔案系統。"</string>
<string name="permlab_mount_format_filesystems" msgid="5523285143576718981">"將外接式儲存裝置格式化"</string>
<string name="permdesc_mount_format_filesystems" msgid="574060044906047386">"允許應用程式將可移除式儲存裝置格式化。"</string>
- <!-- no translation found for permlab_asec_access (1070364079249834666) -->
- <skip />
- <!-- no translation found for permdesc_asec_access (7691616292170590244) -->
- <skip />
- <!-- no translation found for permlab_asec_create (7312078032326928899) -->
- <skip />
- <!-- no translation found for permdesc_asec_create (7041802322759014035) -->
- <skip />
- <!-- no translation found for permlab_asec_destroy (7787322878955261006) -->
- <skip />
- <!-- no translation found for permdesc_asec_destroy (5740754114967893169) -->
- <skip />
- <!-- no translation found for permlab_asec_mount_unmount (7517449694667828592) -->
- <skip />
- <!-- no translation found for permdesc_asec_mount_unmount (5438078121718738625) -->
- <skip />
- <!-- no translation found for permlab_asec_rename (5685344390439934495) -->
- <skip />
- <!-- no translation found for permdesc_asec_rename (1387881770708872470) -->
- <skip />
+ <string name="permlab_asec_access" msgid="1070364079249834666">"取得安全儲存空間的資訊"</string>
+ <string name="permdesc_asec_access" msgid="7691616292170590244">"允許應用程式取得安全儲存空間的資訊。"</string>
+ <string name="permlab_asec_create" msgid="7312078032326928899">"建立安全儲存空間"</string>
+ <string name="permdesc_asec_create" msgid="7041802322759014035">"允許應用程式建立安全儲存空間。"</string>
+ <string name="permlab_asec_destroy" msgid="7787322878955261006">"銷毀安全儲存空間"</string>
+ <string name="permdesc_asec_destroy" msgid="5740754114967893169">"允許應用程式銷毀安全儲存空間。"</string>
+ <string name="permlab_asec_mount_unmount" msgid="7517449694667828592">"掛載/卸載安全儲存空間"</string>
+ <string name="permdesc_asec_mount_unmount" msgid="5438078121718738625">"允許應用程式掛載/卸載安全儲存空間。"</string>
+ <string name="permlab_asec_rename" msgid="5685344390439934495">"重新命名安全儲存空間"</string>
+ <string name="permdesc_asec_rename" msgid="1387881770708872470">"允許應用程式重新命名安全儲存空間。"</string>
<string name="permlab_vibrate" msgid="7768356019980849603">"控制震動"</string>
<string name="permdesc_vibrate" msgid="2886677177257789187">"允許應用程式控制震動。"</string>
<string name="permlab_flashlight" msgid="2155920810121984215">"控制閃光燈"</string>
@@ -378,10 +360,8 @@
<string name="permdesc_setWallpaperHints" msgid="6019479164008079626">"允許應用程式設定系統桌布大小提示。"</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"將系統還原至出廠預設值"</string>
<string name="permdesc_masterClear" msgid="5033465107545174514">"允許應用程式將手機完全重設至出廠設定,清除所有資料、設定與已安裝程式。"</string>
- <!-- no translation found for permlab_setTime (2021614829591775646) -->
- <skip />
- <!-- no translation found for permdesc_setTime (667294309287080045) -->
- <skip />
+ <string name="permlab_setTime" msgid="2021614829591775646">"設定時間"</string>
+ <string name="permdesc_setTime" msgid="667294309287080045">"允許應用程式變更手機時鐘時間。"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"設定時區"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"允許應用程式變更時區。"</string>
<string name="permlab_accountManagerService" msgid="4829262349691386986">"作為 AccountManagerService"</string>
@@ -401,12 +381,9 @@
<string name="permlab_writeApnSettings" msgid="7823599210086622545">"輸入存取點名稱設定"</string>
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"允許應用程式修改 APN 設定,例如:Proxy 及 APN 的連接埠。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"變更網路連線"</string>
- <!-- no translation found for permdesc_changeNetworkState (4199958910396387075) -->
- <skip />
- <!-- no translation found for permlab_changeTetherState (5952584964373017960) -->
- <skip />
- <!-- no translation found for permdesc_changeTetherState (8905815579146349568) -->
- <skip />
+ <string name="permdesc_changeNetworkState" msgid="4199958910396387075">"允許應用程式變更網路連線狀態。"</string>
+ <string name="permlab_changeTetherState" msgid="5952584964373017960">"變更數據連線"</string>
+ <string name="permdesc_changeTetherState" msgid="8905815579146349568">"允許應用程式變更數據網路連線狀態。"</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"變更背景資料使用設定"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"允許應用程式變更背景資料使用設定。"</string>
<string name="permlab_accessWifiState" msgid="8100926650211034400">"檢視 Wi-Fi 狀態"</string>
@@ -437,30 +414,18 @@
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"允許應用程式將新字詞寫入使用者的字典。"</string>
<string name="permlab_sdcardWrite" msgid="8079403759001777291">"修改/刪除 SD 卡的內容"</string>
<string name="permdesc_sdcardWrite" msgid="6643963204976471878">"允許應用程式寫入 SD 卡。"</string>
- <!-- no translation found for permlab_cache_filesystem (5656487264819669824) -->
- <skip />
- <!-- no translation found for permdesc_cache_filesystem (1624734528435659906) -->
- <skip />
- <!-- no translation found for policylab_limitPassword (4307861496302850201) -->
- <skip />
- <!-- no translation found for policydesc_limitPassword (1719877245692318299) -->
- <skip />
- <!-- no translation found for policylab_watchLogin (7374780712664285321) -->
- <skip />
- <!-- no translation found for policydesc_watchLogin (1961251179624843483) -->
- <skip />
- <!-- no translation found for policylab_resetPassword (9084772090797485420) -->
- <skip />
- <!-- no translation found for policydesc_resetPassword (3332167600331799991) -->
- <skip />
- <!-- no translation found for policylab_forceLock (5760466025247634488) -->
- <skip />
- <!-- no translation found for policydesc_forceLock (2819868664946089740) -->
- <skip />
- <!-- no translation found for policylab_wipeData (3910545446758639713) -->
- <skip />
- <!-- no translation found for policydesc_wipeData (2314060933796396205) -->
- <skip />
+ <string name="permlab_cache_filesystem" msgid="5656487264819669824">"存取快取檔案系統"</string>
+ <string name="permdesc_cache_filesystem" msgid="1624734528435659906">"允許應用程式讀取及寫入快取檔案系統。"</string>
+ <string name="policylab_limitPassword" msgid="4307861496302850201">"限制密碼設定規則"</string>
+ <string name="policydesc_limitPassword" msgid="1719877245692318299">"限制允許使用的密碼類型。"</string>
+ <string name="policylab_watchLogin" msgid="7374780712664285321">"查看登入嘗試記錄"</string>
+ <string name="policydesc_watchLogin" msgid="1961251179624843483">"監視者無法登入裝置執行部分動作。"</string>
+ <string name="policylab_resetPassword" msgid="9084772090797485420">"重設密碼"</string>
+ <string name="policydesc_resetPassword" msgid="3332167600331799991">"強制重新設定密碼。您必須取得以管理員提供的新密碼,才能登入。"</string>
+ <string name="policylab_forceLock" msgid="5760466025247634488">"強制鎖定"</string>
+ <string name="policydesc_forceLock" msgid="2819868664946089740">"裝置鎖定時可取得控制,但必須重新輸入密碼。"</string>
+ <string name="policylab_wipeData" msgid="3910545446758639713">"清除所有資料"</string>
+ <string name="policydesc_wipeData" msgid="2314060933796396205">"重設為原廠設定 (系統會刪除所有資料,且不會向您進行確認)。"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"住家電話"</item>
<item msgid="869923650527136615">"行動電話"</item>
@@ -557,8 +522,7 @@
<string name="contact_status_update_attribution" msgid="5112589886094402795">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
<string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>透過「<xliff:g id="SOURCE">%2$s</xliff:g>」"</string>
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
- <!-- no translation found for keyguard_password_enter_password_code (9138158344813213754) -->
- <skip />
+ <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
@@ -579,7 +543,7 @@
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"沒有 SIM 卡。"</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"手機未插入 SIM 卡。"</string>
<string name="lockscreen_missing_sim_instructions" msgid="8874620818937719067">"請插入 SIM 卡。"</string>
- <string name="emergency_calls_only" msgid="6733978304386365407">"只能撥打緊急電話"</string>
+ <string name="emergency_calls_only" msgid="6733978304386365407">"僅可撥打緊急電話"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"網路已鎖定"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM 的 PUK 已鎖定。"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="635967534992394321">"請參閱《使用者指南》或聯絡客戶服務中心。"</string>
@@ -600,12 +564,9 @@
<string name="lockscreen_unlock_label" msgid="737440483220667054">"解除封鎖"</string>
<string name="lockscreen_sound_on_label" msgid="9068877576513425970">"開啟音效"</string>
<string name="lockscreen_sound_off_label" msgid="996822825154319026">"關閉音效"</string>
- <!-- no translation found for password_keyboard_label_symbol_key (992280756256536042) -->
- <skip />
- <!-- no translation found for password_keyboard_label_alpha_key (8001096175167485649) -->
- <skip />
- <!-- no translation found for password_keyboard_label_alt_key (1284820942620288678) -->
- <skip />
+ <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
+ <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
@@ -631,10 +592,8 @@
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string>
<string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
<string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允許應用程式修改儲存在電話上的瀏覽記錄或書籤。請注意:惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
- <!-- no translation found for permlab_writeGeolocationPermissions (4715212655598275532) -->
- <skip />
- <!-- no translation found for permdesc_writeGeolocationPermissions (4011908282980861679) -->
- <skip />
+ <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改瀏覽器地理資訊的權限"</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允許應用程式修改瀏覽器的地理位置權限,惡意應用程式可能會透過此方式允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="save_password_message" msgid="767344687139195790">"是否記憶此密碼?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"現在不要"</string>
<string name="save_password_remember" msgid="6491879678996749466">"記住"</string>
@@ -733,11 +692,6 @@
<string name="weeks" msgid="6509623834583944518">"週"</string>
<string name="year" msgid="4001118221013892076">"年"</string>
<string name="years" msgid="6881577717993213522">"年"</string>
- <string name="every_weekday" msgid="8777593878457748503">"每天 (週一至週五)"</string>
- <string name="daily" msgid="5738949095624133403">"每天"</string>
- <string name="weekly" msgid="983428358394268344">"每週<xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"每月"</string>
- <string name="yearly" msgid="1519577999407493836">"每年"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"無法播放影片"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"很抱歉,影片格式無效,裝置無法進行串流處理。"</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"很抱歉,此影片無法播放。"</string>
@@ -820,26 +774,19 @@
<string name="no_permissions" msgid="7283357728219338112">"無須許可"</string>
<string name="perms_hide" msgid="7283915391320676226"><b>" 隱藏"</b></string>
<string name="perms_show_all" msgid="2671791163933091180"><b>"顯示全部"</b></string>
- <!-- no translation found for usb_storage_activity_title (2399289999608900443) -->
- <skip />
+ <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB 大量儲存裝置"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB 已連接"</string>
- <!-- no translation found for usb_storage_message (4796759646167247178) -->
- <skip />
- <!-- no translation found for usb_storage_button_mount (1052259930369508235) -->
- <skip />
+ <string name="usb_storage_message" msgid="4796759646167247178">"已透過 USB 連接手機與電腦。如要從電腦或 Android 系統的 SD 卡複製檔案,請選取下方按鈕。"</string>
+ <string name="usb_storage_button_mount" msgid="1052259930369508235">"開啟 USB 儲存裝置"</string>
<string name="usb_storage_error_message" msgid="2534784751603345363">"把 SD 卡當成 USB 儲存裝置時發生問題。"</string>
<string name="usb_storage_notification_title" msgid="8175892554757216525">"USB 已連接"</string>
<string name="usb_storage_notification_message" msgid="7380082404288219341">"選取此項將檔案複製到電腦,或從電腦複製。"</string>
<string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"關閉 USB 儲存裝置"</string>
<string name="usb_storage_stop_notification_message" msgid="2591813490269841539">"選取此處關閉 USB 儲存裝置。"</string>
- <!-- no translation found for usb_storage_stop_title (660129851708775853) -->
- <skip />
- <!-- no translation found for usb_storage_stop_message (3613713396426604104) -->
- <skip />
- <!-- no translation found for usb_storage_stop_button_mount (7060218034900696029) -->
- <skip />
- <!-- no translation found for usb_storage_stop_error_message (143881914840412108) -->
- <skip />
+ <string name="usb_storage_stop_title" msgid="660129851708775853">"USB 儲存空間使用中"</string>
+ <string name="usb_storage_stop_message" msgid="3613713396426604104">"關閉 USB 儲存裝置前,請務必先將 Android 系統的 SD 卡從電腦上卸下 (退出)。"</string>
+ <string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"關閉 USB 儲存裝置"</string>
+ <string name="usb_storage_stop_error_message" msgid="143881914840412108">"關閉 USB 儲存裝置時發生問題。請檢查您是否已卸載 USB Host,然後再試一次。"</string>
<!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
<skip />
<!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
@@ -872,10 +819,8 @@
<string name="activity_list_empty" msgid="4168820609403385789">"找不到符合的活動"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"更新元件使用統計資料"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"允許修改收集到的元件使用統計資料。一般應用程式不會使用此功能。"</string>
- <!-- no translation found for permlab_copyProtectedData (1660908117394854464) -->
- <skip />
- <!-- no translation found for permdesc_copyProtectedData (537780957633976401) -->
- <skip />
+ <string name="permlab_copyProtectedData" msgid="1660908117394854464">"允許觸發預設容器服務,以便複製內容 (不建議一般應用程式使用)。"</string>
+ <string name="permdesc_copyProtectedData" msgid="537780957633976401">"允許觸發預設容器服務,以便複製內容 (不建議一般應用程式使用)。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"點兩下以進行縮放控制"</string>
<string name="gadget_host_error_inflating" msgid="2613287218853846830">"擴大小工具時發生錯誤"</string>
<string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -888,12 +833,9 @@
<string name="create_contact_using" msgid="4947405226788104538">"建立手機號碼為 <xliff:g id="NUMBER">%s</xliff:g>"\n"的聯絡人"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已勾選"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未勾選"</string>
- <!-- no translation found for grant_credentials_permission_message_header (6824538733852821001) -->
- <skip />
- <!-- no translation found for grant_credentials_permission_message_footer (3125211343379376561) -->
- <skip />
- <!-- no translation found for grant_permissions_header_text (2722567482180797717) -->
- <skip />
+ <string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"下列一或多個應用程式要求現在及將來的帳戶存取權限。"</string>
+ <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您確定要允許這個要求嗎?"</string>
+ <string name="grant_permissions_header_text" msgid="2722567482180797717">"存取要求"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
<string name="permission_request_notification_title" msgid="5390555465778213840">"已要求權限"</string>
@@ -907,44 +849,27 @@
<string name="l2tp_vpn_description" msgid="3750692169378923304">"第二層通道通訊協定"</string>
<string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"採用預先共用金鑰的 L2TP/IPSec VPN"</string>
<string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"採用憑證的 L2TP/IPSec VPN"</string>
- <!-- no translation found for upload_file (2897957172366730416) -->
+ <string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
+ <string name="reset" msgid="2448168080964209908">"重設"</string>
+ <string name="submit" msgid="1602335572089911941">"提交"</string>
+ <string name="description_star" msgid="2654319874908576133">"我的最愛"</string>
+ <string name="tether_title" msgid="6970447107301643248">"USB 數據連線運作中"</string>
+ <string name="tether_message" msgid="554549994538298101">"如要與電腦分享手機的資料連線,請選取 [數據連線]。"</string>
+ <string name="tether_button" msgid="7409514810151603641">"數據連線"</string>
+ <string name="tether_button_cancel" msgid="6744369928952219677">"取消"</string>
+ <!-- no translation found for tether_error_message (950843122853838817) -->
<skip />
- <!-- no translation found for reset (2448168080964209908) -->
- <skip />
- <!-- no translation found for submit (1602335572089911941) -->
- <skip />
- <!-- no translation found for description_star (2654319874908576133) -->
- <skip />
- <!-- no translation found for tether_title (6970447107301643248) -->
- <skip />
- <!-- no translation found for tether_message (554549994538298101) -->
- <skip />
- <!-- no translation found for tether_button (7409514810151603641) -->
- <skip />
- <!-- no translation found for tether_button_cancel (6744369928952219677) -->
- <skip />
- <!-- no translation found for tether_error_message (6672110337349077628) -->
- <skip />
- <!-- no translation found for tether_available_notification_title (367754042700082080) -->
- <skip />
- <!-- no translation found for tether_available_notification_message (8439443306503453793) -->
- <skip />
- <!-- no translation found for tether_stop_notification_title (1902246071807668101) -->
- <skip />
- <!-- no translation found for tether_stop_notification_message (6920086906891516128) -->
- <skip />
- <!-- no translation found for tether_stop_title (3118332507220912235) -->
- <skip />
- <!-- no translation found for tether_stop_message (7741840433788363174) -->
- <skip />
- <!-- no translation found for tether_stop_button (8061941592702063029) -->
- <skip />
- <!-- no translation found for tether_stop_button_cancel (3694669546501300230) -->
- <skip />
- <!-- no translation found for tether_stop_error_message (4244697367270211648) -->
- <skip />
+ <string name="tether_available_notification_title" msgid="367754042700082080">"USB 數據連線運作中"</string>
+ <string name="tether_available_notification_message" msgid="8439443306503453793">"選取此選項可讓您的電腦與手機進行數據連線。"</string>
+ <string name="tether_stop_notification_title" msgid="1902246071807668101">"中斷數據連線"</string>
+ <string name="tether_stop_notification_message" msgid="6920086906891516128">"選取即可中斷與電腦的數據連線。"</string>
+ <string name="tether_stop_title" msgid="3118332507220912235">"中斷數據連線"</string>
+ <string name="tether_stop_message" msgid="7741840433788363174">"您已與電腦分享手機的行動資料連線,如要中斷 USB 數據連線,請選取 [中斷連線]。"</string>
+ <string name="tether_stop_button" msgid="8061941592702063029">"中斷連線"</string>
+ <string name="tether_stop_button_cancel" msgid="3694669546501300230">"取消"</string>
+ <string name="tether_stop_error_message" msgid="4244697367270211648">"關閉數據連線時發生問題,請再試一次。"</string>
<!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
<skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
+ <!-- no translation found for car_mode_disable_notification_message (668663626721675614) -->
<skip />
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1b5b3e4..84cd4c3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -87,7 +87,7 @@
</string-array>
<!-- Regex array of allowable upstream ifaces for tethering - for example if you want
- tethering on your a new interface called "foo2" add "foo\\d" here -->
+ tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
<string-array translatable="false" name="config_tether_upstream_regexs">
</string-array>
@@ -267,6 +267,11 @@
it will be removed when the lower-level touch driver generates better
data. -->
<bool name="config_filterTouchEvents">false</bool>
+
+ <!-- Enables special filtering code in the framework for raw touch events
+ from the touch driver. This code exists for one particular device,
+ and should not be enabled for any others. -->
+ <bool name="config_filterJumpyTouchEvents">false</bool>
<!-- Component name of the default wallpaper. This will be ImageWallpaper if not
specified -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 35ea0cc..e68f214 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -131,9 +131,17 @@
<!-- Displayed to tell the user that emergency service is blocked by access control. -->
<string name="RestrictedOnEmergency">Emergency service is blocked.</string>
<!-- Displayed to tell the user that normal service is blocked by access control. -->
- <string name="RestrictedOnNormal">Voice/SMS service is blocked.</string>
- <!-- Displayed to tell the user that all voice service is blocked by access control. -->
- <string name="RestrictedOnAll">All voice/SMS services are blocked.</string>
+ <string name="RestrictedOnNormal">Voice service is blocked.</string>
+ <!-- Displayed to tell the user that all emergency and normal voice services are blocked by access control. -->
+ <string name="RestrictedOnAllVoice">All Voice services are blocked.</string>
+ <!-- Displayed to tell the user that sms service is blocked by access control. -->
+ <string name="RestrictedOnSms">SMS service is blocked.</string>
+ <!-- Displayed to tell the user that voice/data service is blocked by access control. -->
+ <string name="RestrictedOnVoiceData">Voice/Data services are blocked.</string>
+ <!-- Displayed to tell the user that voice and sms service are blocked by access control. -->
+ <string name="RestrictedOnVoiceSms">Voice/SMS services are blocked.</string>
+ <!-- Displayed to tell the user that all service is blocked by access control. -->
+ <string name="RestrictedOnAll">All Voice/Data/SMS services are blocked.</string>
<!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
<!-- Example: Service was enabled for: Voice, Data -->
@@ -1093,7 +1101,7 @@
the state of network connectivity.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_changeTetherState">change tethered connectivity</string>
+ <string name="permlab_changeTetherState">Change tethered connectivity</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the applicaiton to do this. -->
<string name="permdesc_changeTetherState">Allows an application to change
the state of tethered network connectivity.</string>
@@ -2238,41 +2246,6 @@
-->
<string name="description_star">favorite</string>
-
- <!-- Strings for Tethering dialogs -->
- <!-- This is the label for the activity, and should never be visible to the user. -->
- <!-- See TETHERING. TETHERING_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to Tether. This is the title. -->
- <string name="tether_title">USB tethering available</string>
- <!-- See TETHER. This is the message. -->
- <string name="tether_message">Select \"Tether\" if you want to share your phone\'s data connection with your computer.</string>
- <!-- See TETHER. This is the button text to Tether the computer with the phone. -->
- <string name="tether_button">Tether</string>
- <!-- See TETHER. This is the button text to ignore the plugging in of the phone.. -->
- <string name="tether_button_cancel">Cancel</string>
- <!-- See TETHER. If there was an error mounting, this is the text. -->
- <string name="tether_error_message">There is a problem tethering.</string>
- <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection. This is the title -->
- <string name="tether_available_notification_title">USB tethering available</string>
- <!-- See USB_STORAGE. This is the message. -->
- <string name="tether_available_notification_message">Select to tether your computer to your phone.</string>
- <!-- TETHER_STOP: While TETHER is enabled, we show a notification dialog asking if he wants to stop. This is the title -->
- <string name="tether_stop_notification_title">Untether</string>
- <!-- See TETHER. This is the message. -->
- <string name="tether_stop_notification_message">Select to untether your computer.</string>
-
- <!-- TETHER stop dialog strings -->
- <!-- This is the label for the activity, and should never be visible to the user. -->
- <!-- See TETHER_STOP. TETHER_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop tethering. This is the title. -->
- <string name="tether_stop_title">Disconnect tethering</string>
- <!-- See TETHER_STOP. This is the message. -->
- <string name="tether_stop_message">You have been sharing your phone\'s cellular data connection with your computer. Select \"Disconnect\" to disconnect USB tethering.</string>
- <!-- See TETHER_STOP. This is the button text to disconnect tethering. -->
- <string name="tether_stop_button">Disconnect</string>
- <!-- See TETHER_STOP. This is the button text to cancel disconnecting the tether. -->
- <string name="tether_stop_button_cancel">Cancel</string>
- <!-- See TETHER_STOP_DIALOG. If there was an error disconnect, this is the text. -->
- <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
-
<!-- Strings for car mode notification -->
<!-- Shown when car mode is enabled -->
<string name="car_mode_disable_notification_title">Car mode enabled</string>
diff --git a/core/tests/coretests/src/android/content/ObserverNodeTest.java b/core/tests/coretests/src/android/content/ObserverNodeTest.java
index 68cc75b..736c759 100644
--- a/core/tests/coretests/src/android/content/ObserverNodeTest.java
+++ b/core/tests/coretests/src/android/content/ObserverNodeTest.java
@@ -24,50 +24,49 @@
import android.net.Uri;
import android.os.Handler;
import android.test.AndroidTestCase;
-import android.util.Log;
-public class ObserverNodeTest extends AndroidTestCase {
+public class ObserverNodeTest extends AndroidTestCase {
static class TestObserver extends ContentObserver {
public TestObserver() {
super(new Handler());
}
}
-
+
public void testUri() {
ObserverNode root = new ObserverNode("");
Uri[] uris = new Uri[] {
Uri.parse("content://c/a/"),
Uri.parse("content://c/"),
- Uri.parse("content://x/"),
+ Uri.parse("content://x/"),
Uri.parse("content://c/b/"),
Uri.parse("content://c/a/a1/1/"),
Uri.parse("content://c/a/a1/2/"),
Uri.parse("content://c/b/1/"),
Uri.parse("content://c/b/2/"),
};
-
+
int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3};
-
+
// special case
- root.addObserver(uris[0], new TestObserver().getContentObserver(), false);
+ root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root);
for(int i = 1; i < uris.length; i++) {
- root.addObserver(uris[i], new TestObserver().getContentObserver(), true);
+ root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root);
}
-
+
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
-
+
for (int i = nums.length - 1; i >=0; --i) {
- root.collectObservers(uris[i], 0, null, false, calls);
+ root.collectObserversLocked(uris[i], 0, null, false, calls);
assertEquals(nums[i], calls.size());
calls.clear();
}
}
-
+
public void testUriNotNotify() {
ObserverNode root = new ObserverNode("");
Uri[] uris = new Uri[] {
Uri.parse("content://c/"),
- Uri.parse("content://x/"),
+ Uri.parse("content://x/"),
Uri.parse("content://c/a/"),
Uri.parse("content://c/b/"),
Uri.parse("content://c/a/1/"),
@@ -76,15 +75,15 @@
Uri.parse("content://c/b/2/"),
};
int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1};
-
+
for(int i = 0; i < uris.length; i++) {
- root.addObserver(uris[i], new TestObserver().getContentObserver(), false);
+ root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root);
}
-
+
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
-
+
for (int i = uris.length - 1; i >=0; --i) {
- root.collectObservers(uris[i], 0, null, false, calls);
+ root.collectObserversLocked(uris[i], 0, null, false, calls);
assertEquals(nums[i], calls.size());
calls.clear();
}
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index f82d79a..370ae78 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -59,7 +59,7 @@
assertEquals("content://settings/system/test_setting",
Settings.System.getUriFor("test_setting").toString());
- assertEquals("content://settings/gservices/test_service",
+ assertEquals("content://settings/secure/test_service",
Settings.Secure.getUriFor("test_service").toString());
// These tables use the row name (not ID) as their content URI.
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index da6036a..8e7e63e 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -39,42 +39,42 @@
public static final String GIMEL = "\u05d2";
public static final String DALET = "\u05d3";
- @SmallTest
+ //@SmallTest
public void testAllLtr() {
expectBidi(REQ_DL, "a test", "000000", L);
}
- @SmallTest
+ //@SmallTest
public void testLtrRtl() {
expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L);
}
- @SmallTest
+ //@SmallTest
public void testAllRtl() {
expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R);
}
- @SmallTest
+ //@SmallTest
public void testRtlLtr() {
expectBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111000", R);
}
- @SmallTest
+ //@SmallTest
public void testRAllLtr() {
expectBidi(REQ_R, "a test", "000000", R);
}
- @SmallTest
+ //@SmallTest
public void testRLtrRtl() {
expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R);
}
- @SmallTest
+ //@SmallTest
public void testLAllRtl() {
expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L);
}
- @SmallTest
+ //@SmallTest
public void testLRtlLtr() {
expectBidi(REQ_L, ALEF + BET + GIMEL + " abc", "1110000", L);
}
@@ -104,7 +104,7 @@
assertEquals("levels", expectedLevels, resultLevels);
}
- @SmallTest
+ //@SmallTest
public void testNativeBidi() {
// native bidi returns levels, not simply directions
expectNativeBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111222", R);
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 7511ec1..1f58a2c 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -26,6 +26,11 @@
/**
* Tests StaticLayout vertical metrics behavior.
+ *
+ * Requires disabling access checks in the vm since this calls package-private
+ * APIs.
+ *
+ * @Suppress
*/
public class StaticLayoutTest extends TestCase {
@@ -33,7 +38,7 @@
* Basic test showing expected behavior and relationship between font
* metrics and line metrics.
*/
- @SmallTest
+ //@SmallTest
public void testGetters1() {
LayoutBuilder b = builder();
FontMetricsInt fmi = b.paint.getFontMetricsInt();
@@ -59,7 +64,7 @@
* Basic test showing effect of includePad = true with 1 line.
* Top and bottom padding are affected, as is the line descent and height.
*/
- @SmallTest
+ //@SmallTest
public void testGetters2() {
LayoutBuilder b = builder()
.setIncludePad(true);
@@ -74,7 +79,7 @@
* Basic test showing effect of includePad = true wrapping to 2 lines.
* Ascent of top line and descent of bottom line are affected.
*/
- @SmallTest
+ //@SmallTest
public void testGetters3() {
LayoutBuilder b = builder()
.setIncludePad(true)
@@ -91,7 +96,7 @@
* Basic test showing effect of includePad = true wrapping to 3 lines.
* First line ascent is top, bottom line descent is bottom.
*/
- @SmallTest
+ //@SmallTest
public void testGetters4() {
LayoutBuilder b = builder()
.setText("This is a longer test")
@@ -111,7 +116,7 @@
* large text. See effect of leading. Currently, we don't expect there to
* even be non-zero leading.
*/
- @SmallTest
+ //@SmallTest
public void testGetters5() {
LayoutBuilder b = builder()
.setText("This is a longer test")
@@ -138,7 +143,7 @@
* Basic test showing effect of includePad = true, spacingAdd = 2, wrapping
* to 3 lines.
*/
- @SmallTest
+ //@SmallTest
public void testGetters6() {
int spacingAdd = 2; // int so expressions return int
LayoutBuilder b = builder()
@@ -159,7 +164,7 @@
* Basic test showing effect of includePad = true, spacingAdd = 2,
* spacingMult = 1.5, wrapping to 3 lines.
*/
- @SmallTest
+ //@SmallTest
public void testGetters7() {
LayoutBuilder b = builder()
.setText("This is a longer test")
@@ -181,7 +186,7 @@
* Basic test showing effect of includePad = true, spacingAdd = 0,
* spacingMult = 0.8 when wrapping to 3 lines.
*/
- @SmallTest
+ //@SmallTest
public void testGetters8() {
LayoutBuilder b = builder()
.setText("This is a longer test")
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 5aa88b0..7ca3741 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -972,6 +972,19 @@
}
/**
+ * Given another bitmap, return true if it has the same dimensions, config,
+ * and pixel data as this bitmap. If any of those differ, return false.
+ * If other is null, return false.
+ *
+ * @hide (just needed internally right now)
+ */
+ public boolean sameAs(Bitmap other) {
+ return this == other ||
+ (other != null &&
+ nativeSameAs(mNativeBitmap, other.mNativeBitmap));
+ }
+
+ /**
* Rebuilds any caches associated with the bitmap that are used for
* drawing it. In the case of purgeable bitmaps, this call will attempt to
* ensure that the pixels have been decoded.
@@ -1042,6 +1055,7 @@
private static native void nativePrepareToDraw(int nativeBitmap);
private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha);
+ private static native boolean nativeSameAs(int nb0, int nb1);
/* package */ final int ni() {
return mNativeBitmap;
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 752afc2..6c6d0bc 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -139,6 +139,9 @@
// stored in JPEG EXIF header.
// Example value: "1251192757". Write only.
static const char KEY_GPS_TIMESTAMP[];
+ // GPS Processing Method
+ // Example value: "GPS" or "NETWORK". Write only.
+ static const char KEY_GPS_PROCESSING_METHOD[];
// Current white balance setting.
// Example value: "auto" or WHITE_BALANCE_XXX constants. Read/write.
static const char KEY_WHITE_BALANCE[];
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index cbcef4e..0e796dc 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -946,7 +946,8 @@
MASK_UI_MODE_TYPE = 0x0f,
UI_MODE_TYPE_ANY = 0x00,
UI_MODE_TYPE_NORMAL = 0x01,
- UI_MODE_TYPE_CAR = 0x02,
+ UI_MODE_TYPE_DESK = 0x02,
+ UI_MODE_TYPE_CAR = 0x03,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 2269352..8089389 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -3247,7 +3247,10 @@
if (mBytesRead < 0) {
LOGE("Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
- sleep(1);
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
}
mRsmpInIndex = mFrameCount;
framesOut = 0;
@@ -3429,7 +3432,10 @@
if (mBytesRead < 0) {
LOGE("RecordThread::getNextBuffer() Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
- sleep(1);
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
}
buffer->raw = 0;
buffer->frameCount = 0;
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index 7b866c7..cfcc3ea 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -1290,7 +1290,7 @@
a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
}
}
- // do not change newDevice is it was already set before this call by a previous call to
+ // do not change newDevice if it was already set before this call by a previous call to
// getNewDevice() or checkOutputForStrategy() for a strategy with higher priority
if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) {
newDevice = getDeviceForStrategy(strategy, false);
@@ -1466,6 +1466,12 @@
case STRATEGY_MEDIA: {
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ }
#ifdef WITH_A2DP
if (mA2dpOutput != 0) {
if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
@@ -1483,12 +1489,6 @@
}
#endif
if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- }
- if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
}
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index cf4cbfa..65785c7 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -44,6 +44,7 @@
const char CameraParameters::KEY_GPS_LONGITUDE[] = "gps-longitude";
const char CameraParameters::KEY_GPS_ALTITUDE[] = "gps-altitude";
const char CameraParameters::KEY_GPS_TIMESTAMP[] = "gps-timestamp";
+const char CameraParameters::KEY_GPS_PROCESSING_METHOD[] = "gps-processing-method";
const char CameraParameters::KEY_WHITE_BALANCE[] = "whitebalance";
const char CameraParameters::KEY_SUPPORTED_WHITE_BALANCE[] = "whitebalance-values";
const char CameraParameters::KEY_EFFECT[] = "effect";
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index cd8361c..d280f50 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -202,6 +202,12 @@
RS_PRIMITIVE_TRIANGLE_FAN
};
+enum RsError {
+ RS_ERROR_NONE,
+ RS_ERROR_BAD_SHADER,
+ RS_ERROR_BAD_SCRIPT
+};
+
#ifndef NO_RS_FUNCS
#include "rsgApiFuncDecl.h"
#endif
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 4d97c0f..cb9937c 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -36,6 +36,11 @@
param int32_t bits
}
+ContextGetError {
+ param RsError *err
+ ret const char *
+ }
+
ContextSetPriority {
param int32_t priority
}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index cc3a74fb..d8a9a99 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -178,6 +178,11 @@
uint32_t ret = runScript(mRootScript.get(), 0);
checkError("runRootScript");
+ if (mError != RS_ERROR_NONE) {
+ // If we have an error condition we stop rendering until
+ // somthing changes that might fix it.
+ ret = 0;
+ }
return ret;
}
@@ -240,10 +245,13 @@
}
}
-void Context::setupCheck()
+bool Context::setupCheck()
{
if (checkVersion2_0()) {
- mShaderCache.lookup(this, mVertex.get(), mFragment.get());
+ if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
+ LOGE("Context::setupCheck() 1 fail");
+ return false;
+ }
mFragmentStore->setupGL2(this, &mStateFragmentStore);
mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
@@ -256,6 +264,7 @@
mRaster->setupGL(this, &mStateRaster);
mVertex->setupGL(this, &mStateVertex);
}
+ return true;
}
static bool getProp(const char *str)
@@ -389,6 +398,9 @@
mUseDepth = useDepth;
mPaused = false;
mObjHead = NULL;
+ mError = RS_ERROR_NONE;
+ mErrorMsg = NULL;
+
memset(&mEGL, 0, sizeof(mEGL));
memset(&mGL, 0, sizeof(mGL));
mIsGraphicsContext = isGraphics;
@@ -764,6 +776,23 @@
mIO.mToClient.shutdown();
}
+const char * Context::getError(RsError *err)
+{
+ *err = mError;
+ mError = RS_ERROR_NONE;
+ if (*err != RS_ERROR_NONE) {
+ return mErrorMsg;
+ }
+ return NULL;
+}
+
+void Context::setError(RsError e, const char *msg)
+{
+ mError = e;
+ mErrorMsg = msg;
+}
+
+
void Context::dumpDebug() const
{
LOGE("RS Context debug %p", this);
@@ -874,6 +903,15 @@
ObjectBase::dumpAll(rsc);
}
+const char * rsi_ContextGetError(Context *rsc, RsError *e)
+{
+ const char *msg = rsc->getError(e);
+ if (*e != RS_ERROR_NONE) {
+ LOGE("RS Error %i %s", *e, msg);
+ }
+ return msg;
+}
+
}
}
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 04bd748..82c3687 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -93,7 +93,7 @@
const ProgramRaster * getRaster() {return mRaster.get();}
const ProgramVertex * getVertex() {return mVertex.get();}
- void setupCheck();
+ bool setupCheck();
bool checkDriver() const {return mEGL.mSurface != 0;}
void pause();
@@ -160,6 +160,8 @@
void dumpDebug() const;
void checkError(const char *) const;
+ const char * getError(RsError *);
+ void setError(RsError e, const char *msg);
mutable const ObjectBase * mObjHead;
@@ -211,6 +213,8 @@
bool mExit;
bool mUseDepth;
bool mPaused;
+ RsError mError;
+ const char *mErrorMsg;
pthread_t mThreadId;
pid_t mNativeThreadId;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 656a3c3..478a6dc 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -39,6 +39,7 @@
mInputCount = 0;
mOutputCount = 0;
mConstantCount = 0;
+ mIsValid = false;
}
Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
@@ -216,6 +217,7 @@
}
glDeleteShader(mShaderID);
mShaderID = 0;
+ rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
return false;
}
}
@@ -224,6 +226,7 @@
if (rsc->props.mLogShaders) {
LOGV("--Shader load result %x ", glGetError());
}
+ mIsValid = true;
return true;
}
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index a34e89f..86f85fb 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -59,6 +59,8 @@
String8 getGLSLOutputString() const;
String8 getGLSLConstantString() const;
+ bool isValid() const {return mIsValid;}
+
protected:
// Components not listed in "in" will be passed though
// unless overwritten by components in out.
@@ -68,6 +70,7 @@
uint32_t mInputCount;
uint32_t mOutputCount;
uint32_t mConstantCount;
+ bool mIsValid;
ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index cb1436b..a33933b 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -96,6 +96,10 @@
void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot)
{
Script *s = static_cast<Script *>(vs);
+ if (s->mEnviroment.mInvokables[slot] == NULL) {
+ rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script");
+ return;
+ }
s->setupScript();
s->mEnviroment.mInvokables[slot]();
}
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index b7e0b86..1f23773 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -62,6 +62,11 @@
uint32_t ScriptC::run(Context *rsc, uint32_t launchIndex)
{
+ if (mProgram.mScript == NULL) {
+ rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script");
+ return 0;
+ }
+
Context::ScriptTLSStruct * tls =
(Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
rsAssert(tls);
@@ -154,7 +159,9 @@
ACCchar buf[4096];
ACCsizei len;
accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf);
- LOGV(buf);
+ LOGE(buf);
+ rsc->setError(RS_ERROR_BAD_SCRIPT, "Error compiling user script.");
+ return;
}
if (s->mProgram.mInit) {
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 235c153..202ca3d 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -683,7 +683,9 @@
float x2, float y2, float z2)
{
GET_TLS();
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
float vtx[] = { x1, y1, z1, x2, y2, z2 };
VertexArray va;
@@ -700,7 +702,9 @@
static void SC_drawPoint(float x, float y, float z)
{
GET_TLS();
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
float vtx[] = { x, y, z };
@@ -725,7 +729,9 @@
float u4, float v4)
{
GET_TLS();
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
//LOGE("Quad");
//LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
@@ -782,7 +788,9 @@
float cx0, float cy0, float cx1, float cy1)
{
GET_TLS();
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
GLint crop[4] = {cx0, cy0, cx1, cy1};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
@@ -831,7 +839,9 @@
{
GET_TLS();
SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
sm->render(rsc);
}
@@ -839,7 +849,9 @@
{
GET_TLS();
SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
- rsc->setupCheck();
+ if (!rsc->setupCheck()) {
+ return;
+ }
sm->renderRange(rsc, start, len);
}
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 3a1f370..4711d1b 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -123,6 +123,8 @@
}
}
glDeleteProgram(pgm);
+ rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs");
+ return false;
}
if (vtx->isUserProgram()) {
for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) {
@@ -146,6 +148,7 @@
}
}
+ e->mIsValid = true;
//LOGV("SC made program %i", e->program);
glUseProgram(e->program);
mEntryCount++;
diff --git a/libs/rs/rsShaderCache.h b/libs/rs/rsShaderCache.h
index 7aa8183..df99ccc 100644
--- a/libs/rs/rsShaderCache.h
+++ b/libs/rs/rsShaderCache.h
@@ -56,6 +56,7 @@
int32_t mFragAttribSlots[Program::MAX_ATTRIBS];
int32_t mFragUniformSlots[Program::MAX_UNIFORMS];
bool mUserVertexProgram;
+ bool mIsValid;
} entry_t;
entry_t *mEntries;
entry_t *mCurrent;
diff --git a/libs/rs/spec.l b/libs/rs/spec.l
index d81d47e..6a9010fe 100644
--- a/libs/rs/spec.l
+++ b/libs/rs/spec.l
@@ -148,6 +148,9 @@
BEGIN(api_entry2);
}
+<api_entry2>"*" {
+ currType->ptrLevel ++;
+ }
<api_entry2>"}" {
apiCount++;
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index d9d9bfe..5969617 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -246,10 +246,15 @@
LOGI("version : %s", glGetString(GL_VERSION));
LOGI("extensions: %s", gl_extensions);
- if (strstr(gl_renderer, "Adreno")) {
+#if 0
+ // for drivers that don't have proper support for flushing cached buffers
+ // on gralloc unlock, uncomment this block and test for the specific
+ // renderer substring
+ if (strstr(gl_renderer, "<some vendor string>")) {
LOGD("Assuming uncached graphics buffers.");
mFlags &= ~CACHED_BUFFERS;
}
+#endif
if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) {
mFlags |= NPOT_EXTENSION;
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index c4d4f99..d45eaf0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -176,7 +176,7 @@
if (device == NULL) return -1;
if (sw >= 0 && sw <= SW_MAX) {
- uint8_t sw_bitmask[(SW_MAX+1)/8];
+ uint8_t sw_bitmask[(SW_MAX+7)/8];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
@@ -200,7 +200,7 @@
if (device == NULL) return -1;
if (code >= 0 && code <= KEY_MAX) {
- uint8_t key_bitmask[(KEY_MAX+1)/8];
+ uint8_t key_bitmask[(KEY_MAX+7)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
@@ -225,7 +225,7 @@
Vector<int32_t> scanCodes;
device->layoutMap->findScancodes(code, &scanCodes);
- uint8_t key_bitmask[(KEY_MAX+1)/8];
+ uint8_t key_bitmask[(KEY_MAX+7)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
@@ -604,13 +604,16 @@
// figure out the kinds of events the device reports
- // See if this is a keyboard, and classify it.
- uint8_t key_bitmask[(KEY_MAX+1)/8];
+ // See if this is a keyboard, and classify it. Note that we only
+ // consider up through the function keys; we don't want to include
+ // ones after that (play cd etc) so we don't mistakenly consider a
+ // controller to be a keyboard.
+ uint8_t key_bitmask[(KEY_MAX+7)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP\n");
- //for (int i=0; i<((KEY_MAX+1)/8); i++) {
+ //for (int i=0; i<((KEY_MAX+7)/8); i++) {
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
//}
for (int i=0; i<((BTN_MISC+7)/8); i++) {
@@ -633,7 +636,7 @@
// See if this is a trackball.
if (test_bit(BTN_MOUSE, key_bitmask)) {
- uint8_t rel_bitmask[(REL_MAX+1)/8];
+ uint8_t rel_bitmask[(REL_MAX+7)/8];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
@@ -644,7 +647,7 @@
}
}
- uint8_t abs_bitmask[(ABS_MAX+1)/8];
+ uint8_t abs_bitmask[(ABS_MAX+7)/8];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
@@ -663,7 +666,7 @@
#ifdef EV_SW
// figure out the switches this device reports
- uint8_t sw_bitmask[(SW_MAX+1)/8];
+ uint8_t sw_bitmask[(SW_MAX+7)/8];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
@@ -702,22 +705,20 @@
device->layoutMap->load(keylayoutFilename);
// tell the world about the devname (the descriptive name)
- int32_t publicID;
- if (!mHaveFirstKeyboard && !defaultKeymap) {
- publicID = 0;
+ if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
// the built-in keyboard has a well-known device ID of 0,
// this device better not go away.
mHaveFirstKeyboard = true;
mFirstKeyboardId = device->id;
+ property_set("hw.keyboards.0.devname", name);
} else {
- publicID = device->id;
// ensure mFirstKeyboardId is set to -something-.
if (mFirstKeyboardId == 0) {
mFirstKeyboardId = device->id;
}
}
char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", publicID);
+ sprintf(propName, "hw.keyboards.%u.devname", device->id);
property_set(propName, name);
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
@@ -734,8 +735,8 @@
device->classes |= CLASS_DPAD;
}
- LOGI("New keyboard: publicID=%d device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
- publicID, device->id, name, propName, keylayoutFilename);
+ LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
+ device->id, name, propName, keylayoutFilename);
}
LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
@@ -808,18 +809,15 @@
device->next = mClosingDevices;
mClosingDevices = device;
- uint32_t publicID;
if (device->id == mFirstKeyboardId) {
LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
device->path.string(), mFirstKeyboardId);
mFirstKeyboardId = 0;
- publicID = 0;
- } else {
- publicID = device->id;
+ property_set("hw.keyboards.0.devname", NULL);
}
// clear the property
char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", publicID);
+ sprintf(propName, "hw.keyboards.%u.devname", device->id);
property_set(propName, NULL);
return 0;
}
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 6da11b5..38d8412 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -1850,7 +1850,7 @@
if (Res_GETPACKAGE(resID)+1 == 0) {
LOGW("No package identifier when getting name for resource number 0x%08x", resID);
} else {
- LOGV("Resources don't contain package for resource number 0x%08x", resID);
+ LOGW("No known package when getting name for resource number 0x%08x", resID);
}
return false;
}
@@ -1898,9 +1898,9 @@
if (p < 0) {
if (Res_GETPACKAGE(resID)+1 == 0) {
- LOGW("No package identifier when getting name for resource number 0x%08x", resID);
+ LOGW("No package identifier when getting value for resource number 0x%08x", resID);
} else {
- LOGV("Resources don't contain package for resource number 0x%08x", resID);
+ LOGW("No known package when getting value for resource number 0x%08x", resID);
}
return BAD_INDEX;
}
@@ -1921,7 +1921,7 @@
const PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
LOGW("Bad identifier when getting value for resource number 0x%08x", resID);
- return false;
+ return BAD_INDEX;
}
size_t ip = grp->packages.size();
while (ip > 0) {
@@ -2003,7 +2003,7 @@
return bestPackage->header->index;
}
- return BAD_INDEX;
+ return BAD_VALUE;
}
ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -2018,6 +2018,9 @@
uint32_t newFlags = 0;
const ssize_t newIndex = getResource(value->data, value, true, &newFlags,
outConfig);
+ if (newIndex == BAD_INDEX) {
+ return BAD_INDEX;
+ }
TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n",
(void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data));
//printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex);
diff --git a/location/java/android/location/GeocoderParams.java b/location/java/android/location/GeocoderParams.java
index 8b8e63b..174fe3e 100644
--- a/location/java/android/location/GeocoderParams.java
+++ b/location/java/android/location/GeocoderParams.java
@@ -29,6 +29,8 @@
* as well as the Geocoder client's package name for geocoder server
* logging. This information is kept in a separate class to allow for
* future expansion of the IGeocodeProvider interface.
+ *
+ * @hide
*/
public class GeocoderParams implements Parcelable {
private Locale mLocale;
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 5529b11..97b283c 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -39,6 +39,7 @@
void disable();
int getStatus(out Bundle extras);
long getStatusUpdateTime();
+ String getInternalState();
void enableLocationTracking(boolean enable);
void setMinTime(long minTime);
void updateNetworkState(int state, in NetworkInfo info);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index da760a1..9e4a16b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -317,7 +317,7 @@
List<String> providers = getProviders(enabledOnly);
for (String providerName : providers) {
LocationProvider provider = getProvider(providerName);
- if (provider.meetsCriteria(criteria)) {
+ if (provider != null && provider.meetsCriteria(criteria)) {
if (goodProviders.isEmpty()) {
goodProviders = new ArrayList<String>();
}
diff --git a/location/java/android/location/LocationProviderInterface.java b/location/java/android/location/LocationProviderInterface.java
index 98beffe..5ffe15c3 100644
--- a/location/java/android/location/LocationProviderInterface.java
+++ b/location/java/android/location/LocationProviderInterface.java
@@ -42,6 +42,7 @@
int getStatus(Bundle extras);
long getStatusUpdateTime();
void enableLocationTracking(boolean enable);
+ String getInternalState();
void setMinTime(long minTime);
void updateNetworkState(int state, NetworkInfo info);
void updateLocation(Location location);
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index 4163def..56cfb33 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -95,6 +95,10 @@
return LocationProvider.this.onGetStatusUpdateTime();
}
+ public String getInternalState() {
+ return LocationProvider.this.onGetInternalState();
+ }
+
public void enableLocationTracking(boolean enable) {
LocationProvider.this.onEnableLocationTracking(enable);
}
@@ -267,6 +271,13 @@
public abstract long onGetStatusUpdateTime();
/**
+ * Returns debugging information about the location provider.
+ *
+ * @return string describing the internal state of the location provider, or null.
+ */
+ public abstract String onGetInternalState();
+
+ /**
* 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.
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 90b50cc..44a5a6b 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -633,6 +633,10 @@
}
}
+ public String getInternalState() {
+ return native_get_internal_state();
+ }
+
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mListener;
@@ -1391,12 +1395,15 @@
private native int native_read_nmea(int index, byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
- // XTRA Support
+ // XTRA Support
private native void native_inject_time(long time, long timeReference, int uncertainty);
private native boolean native_supports_xtra();
private native void native_inject_xtra_data(byte[] data, int length);
- // AGPS Support
+ // DEBUG Support
+ private native String native_get_internal_state();
+
+ // AGPS Support
private native void native_agps_data_conn_open(String apn);
private native void native_agps_data_conn_closed();
private native void native_agps_data_conn_failed();
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index abb90b7..31ec09a 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -266,6 +266,15 @@
return 0;
}
+ public String getInternalState() {
+ try {
+ return mProvider.getInternalState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getInternalState failed", e);
+ return null;
+ }
+ }
+
public boolean isLocationTracking() {
return mLocationTracking;
}
diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index bc1893e..d912740 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -168,6 +168,10 @@
mStatusUpdateTime = 0;
}
+ public String getInternalState() {
+ return null;
+ }
+
public void enableLocationTracking(boolean enable) {
}
diff --git a/location/java/com/android/internal/location/PassiveProvider.java b/location/java/com/android/internal/location/PassiveProvider.java
index 7eb711d..ab90937 100644
--- a/location/java/com/android/internal/location/PassiveProvider.java
+++ b/location/java/com/android/internal/location/PassiveProvider.java
@@ -106,6 +106,10 @@
return -1;
}
+ public String getInternalState() {
+ return null;
+ }
+
public void enableLocationTracking(boolean enable) {
mTracking = enable;
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a36ee85..e586869 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -450,10 +450,20 @@
VolumeStreamState streamState = mStreamStates[streamType];
if (streamState.setIndex(index, lastAudible) || force) {
// Post message to set system volume (it in turn will post a message
- // to persist). Do not change volume if stream is muted.
- if (streamState.muteCount() == 0) {
+ // to persist).
+ // If stream is muted or we are in silent mode and stream is affected by ringer mode
+ // and the new volume is not 0, just persist the new volume but do not change
+ // current value
+ if (streamState.muteCount() == 0 &&
+ (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
+ !isStreamAffectedByRingerMode(streamType) ||
+ index == 0)) {
sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
streamState, 0);
+ } else {
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+ SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
}
}
}
@@ -512,7 +522,7 @@
if (!isStreamAffectedByRingerMode(streamType)) continue;
// Bring back last audible volume
setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
- false, false);
+ true, false);
}
} else {
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
@@ -524,7 +534,7 @@
// to non affected by ringer mode. Does not arm to do it for streams that
// are not affected as well.
setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
- false, false);
+ true, false);
}
}
}
@@ -1269,14 +1279,18 @@
// Post a persist volume msg
sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
- SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
+ SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
}
- private void persistVolume(VolumeStreamState streamState) {
- System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
- (streamState.mIndex + 5)/ 10);
- System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
+ private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
+ if (current) {
+ System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
+ (streamState.mIndex + 5)/ 10);
+ }
+ if (lastAudible) {
+ System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
(streamState.mLastAudibleIndex + 5) / 10);
+ }
}
private void persistRingerMode() {
@@ -1361,7 +1375,7 @@
break;
case MSG_PERSIST_VOLUME:
- persistVolume((VolumeStreamState) msg.obj);
+ persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
break;
case MSG_PERSIST_RINGER_MODE:
@@ -1469,7 +1483,7 @@
// and persist with no delay as there might be registered observers of the persisted
// notification volume.
sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
- SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
+ SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
}
}
}
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index eade680..09ac0ac 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -34,7 +34,6 @@
* <li> Audio sample rate
* <li> Number of audio channels for recording.
* </ul>
- * {@hide}
*/
public class CamcorderProfile
{
diff --git a/media/java/android/media/CameraProfile.java b/media/java/android/media/CameraProfile.java
index 9685e7e..f1616cc 100644
--- a/media/java/android/media/CameraProfile.java
+++ b/media/java/android/media/CameraProfile.java
@@ -21,7 +21,6 @@
* capture (jpeg) quality levels (0-100) used for low, medium, and high
* quality settings in the Camera application.
*
- * {@hide}
*/
public class CameraProfile
{
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 15e35010..2c5cbf6 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -225,46 +225,6 @@
public static final int MPEG_4_SP = 3;
}
-
- /**
- * @hide Defines the audio sampling rate. This must be set before
- * setAudioEncoder() or it will be ignored.
- * This parameter is used with
- * {@link MediaRecorder#setParameters(String)}.
- */
- public final class AudioParamSamplingRate {
- /* Do not change these values without updating their counterparts
- * in include/media/mediarecorder.h!
- */
- private AudioParamSamplingRate() {}
- public static final String AUDIO_PARAM_SAMPLING_RATE_KEY = "audio-param-sampling-rate=";
- }
-
- /**
- * @hide Defines the audio number of channels. This must be set before
- * setAudioEncoder() or it will be ignored.
- * This parameter is used with
- * {@link MediaRecorder#setParameters(String)}.
- */
- public final class AudioParamChannels {
- /* Do not change these values without updating their counterparts
- * in include/media/mediarecorder.h!
- */
- private AudioParamChannels() {}
- public static final String AUDIO_PARAM_NUMBER_OF_CHANNELS = "audio-param-number-of-channels=";
- }
-
- /**
- * @hide Defines the audio encoding bitrate. This must be set before
- * setAudioEncoder() or it will be ignored.
- * This parameter is used with
- * {@link MediaRecorder#setParameters(String)}.
- */
- public final class AudioParamEncodingBitrate{
- private AudioParamEncodingBitrate() {}
- public static final String AUDIO_PARAM_ENCODING_BITRATE = "audio-param-encoding-bitrate=";
- }
-
/**
* Sets the audio source to be used for recording. If this method is not
* called, the output file will not contain an audio track. The source needs
@@ -399,14 +359,69 @@
throws IllegalStateException;
/**
- * @hide Sets a parameter in the author engine.
+ * Sets the audio sampling rate for recording. Call this method before prepare().
+ * Prepare() may perform additional checks on the parameter to make sure whether
+ * the specified audio sampling rate is applicable. The sampling rate really depends
+ * on the format for the audio recording, as well as the capabilities of the platform.
+ * For instance, the sampling rate supported by AAC audio coding standard ranges
+ * from 8 to 96 kHz. Please consult with the related audio coding standard for the
+ * supported audio sampling rate.
*
- * @param params the parameter to set.
- * @see android.media.MediaRecorder.AudioParamSamplingRate
- * @see android.media.MediaRecorder.AudioParamChannels
- * @see android.media.MediaRecorder.AudioParamEncodingBitrate
+ * @param samplingRate the sampling rate for audio in samples per second.
*/
- public native void setParameters(String params);
+ public void setAudioSamplingRate(int samplingRate) {
+ if (samplingRate <= 0) {
+ throw new IllegalArgumentException("Audio sampling rate is not positive");
+ }
+ setParameter(String.format("audio-param-sampling-rate=%d", samplingRate));
+ }
+
+ /**
+ * Sets the number of audio channels for recording. Call this method before prepare().
+ * Prepare() may perform additional checks on the parameter to make sure whether the
+ * specified number of audio channels are applicable.
+ *
+ * @param numChannels the number of audio channels. Usually it is either 1 (mono) or 2
+ * (stereo).
+ */
+ public void setAudioChannels(int numChannels) {
+ if (numChannels <= 0) {
+ throw new IllegalArgumentException("Number of channels is not positive");
+ }
+ setParameter(String.format("audio-param-number-of-channels=%d", numChannels));
+ }
+
+ /**
+ * Sets the audio encoding bit rate for recording. Call this method before prepare().
+ * Prepare() may perform additional checks on the parameter to make sure whether the
+ * specified bit rate is applicable, and sometimes the passed bitRate will be clipped
+ * internally to ensure the audio recording can proceed smoothly based on the
+ * capabilities of the platform.
+ *
+ * @param bitRate the audio encoding bit rate in bits per second.
+ */
+ public void setAudioEncodingBitRate(int bitRate) {
+ if (bitRate <= 0) {
+ throw new IllegalArgumentException("Audio encoding bit rate is not positive");
+ }
+ setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate));
+ }
+
+ /**
+ * Sets the video encoding bit rate for recording. Call this method before prepare().
+ * Prepare() may perform additional checks on the parameter to make sure whether the
+ * specified bit rate is applicable, and sometimes the passed bitRate will be
+ * clipped internally to ensure the video recording can proceed smoothly based on
+ * the capabilities of the platform.
+ *
+ * @param bitRate the video encoding bit rate in bits per second.
+ */
+ public void setVideoEncodingBitRate(int bitRate) {
+ if (bitRate <= 0) {
+ throw new IllegalArgumentException("Video encoding bit rate is not positive");
+ }
+ setParameter(String.format("video-param-encoding-bitrate=%d", bitRate));
+ }
/**
* Pass in the file descriptor of the file to be written. Call this after
@@ -670,6 +685,8 @@
private native final void native_finalize();
+ private native void setParameter(String nameValuePair);
+
@Override
protected void finalize() { native_finalize(); }
}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 30d95e3..23f850a 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -52,20 +52,19 @@
/* Options used internally. */
private static final int OPTIONS_NONE = 0x0;
- private static final int OPTIONS_DO_NOT_USE_NATIVE = 0x1;
- private static final int OPTIONS_SCALE_UP = 0x2;
+ private static final int OPTIONS_SCALE_UP = 0x1;
/**
* Constant used to indicate we should recycle the input in
* {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input.
*/
- public static final int OPTIONS_RECYCLE_INPUT = 0x4;
+ public static final int OPTIONS_RECYCLE_INPUT = 0x2;
/**
- * Constant used to indicate the dimension of normal thumbnail in
+ * Constant used to indicate the dimension of mini thumbnail in
* {@link #extractThumbnail(Bitmap, int, int, int)}.
*/
- public static final int TARGET_SIZE_NORMAL_THUMBNAIL = 320;
+ public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
/**
* Constant used to indicate the dimension of micro thumbnail in
@@ -95,7 +94,7 @@
long origId, int kind, boolean saveMini) {
boolean wantMini = (kind == Images.Thumbnails.MINI_KIND || saveMini);
int targetSize = wantMini ?
- TARGET_SIZE_NORMAL_THUMBNAIL : TARGET_SIZE_MICRO_THUMBNAIL;
+ TARGET_SIZE_MINI_THUMBNAIL : TARGET_SIZE_MICRO_THUMBNAIL;
int maxPixels = wantMini ?
MAX_NUM_PIXELS_THUMBNAIL : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
@@ -264,40 +263,16 @@
}
/**
- * Returns Options that set the native alloc flag for Bitmap decode.
- */
- private static BitmapFactory.Options createNativeAllocOptions() {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inNativeAlloc = true;
- return options;
- }
-
- /**
* Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
+ * The image data will be read from specified ContentResolver.
*/
private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
Uri uri, ContentResolver cr) {
- return makeBitmap(minSideLength, maxNumOfPixels, uri, cr,
- OPTIONS_DO_NOT_USE_NATIVE);
- }
-
- /**
- * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
- * The image data will be read from specified ContentResolver and clients are allowed to specify
- * whether they want the Bitmap be created in native memory.
- */
- private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
- Uri uri, ContentResolver cr, int opt) {
- boolean useNative = (opt & OPTIONS_DO_NOT_USE_NATIVE) != 0;
ParcelFileDescriptor input = null;
try {
input = cr.openFileDescriptor(uri, "r");
- BitmapFactory.Options options = null;
- if (useNative) {
- options = createNativeAllocOptions();
- }
return makeBitmap(minSideLength, maxNumOfPixels, uri, cr, input,
- options);
+ null);
} catch (IOException ex) {
Log.e(TAG, "", ex);
return null;
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 3063f15..00af3a2 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -223,9 +223,9 @@
}
static void
-android_media_MediaRecorder_setParameters(JNIEnv *env, jobject thiz, jstring params)
+android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring params)
{
- LOGV("setParameters()");
+ LOGV("setParameter()");
if (params == NULL)
{
LOGE("Invalid or empty params string. This parameter will be ignored.");
@@ -455,7 +455,7 @@
{"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat},
{"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder},
{"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder},
- {"setParameters", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameters},
+ {"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter},
{"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD},
{"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize},
{"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate},
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index bce3371..ad037d6 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -552,13 +552,17 @@
audioBuffer.frameCount = userSize/frameSize();
- // Calling obtainBuffer() with a negative wait count causes
- // an (almost) infinite wait time.
- status_t err = obtainBuffer(&audioBuffer, -1);
+ // By using a wait count corresponding to twice the timeout period in
+ // obtainBuffer() we give a chance to recover once for a read timeout
+ // (if media_server crashed for instance) before returning a length of
+ // 0 bytes read to the client
+ status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS));
if (err < 0) {
// out of buffers, return #bytes written
if (err == status_t(NO_MORE_BUFFERS))
break;
+ if (err == status_t(TIMED_OUT))
+ err = 0;
return ssize_t(err);
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index c26d682..594e010 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1768,7 +1768,7 @@
status_t MediaPlayerService::AudioCache::wait()
{
Mutex::Autolock lock(mLock);
- if (!mCommandComplete) {
+ while (!mCommandComplete) {
mSignal.wait(mLock);
}
mCommandComplete = false;
@@ -1805,6 +1805,7 @@
}
// wake up thread
+ Mutex::Autolock lock(p->mLock);
p->mCommandComplete = true;
p->mSignal.signal();
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ab65b44..b8a50bf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -297,13 +297,11 @@
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
- if (setVideoSource(extractor->getTrack(i)) == OK) {
- haveVideo = true;
- }
+ setVideoSource(extractor->getTrack(i));
+ haveVideo = true;
} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
- if (setAudioSource(extractor->getTrack(i)) == OK) {
- haveAudio = true;
- }
+ setAudioSource(extractor->getTrack(i));
+ haveAudio = true;
}
if (haveAudio && haveVideo) {
@@ -331,6 +329,9 @@
}
mPrefetcher.clear();
+ mAudioTrack.clear();
+ mVideoTrack.clear();
+
// Shutdown audio first, so that the respone to the reset request
// appears to happen instantaneously as far as the user is concerned
// If we did this later, audio would continue playing while we
@@ -699,32 +700,34 @@
return OK;
}
-status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {
- if (source == NULL) {
- return UNKNOWN_ERROR;
- }
+void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
if (mPrefetcher != NULL) {
source = mPrefetcher->addSource(source);
}
- sp<MetaData> meta = source->getFormat();
+ mAudioTrack = source;
+}
+
+status_t AwesomePlayer::initAudioDecoder() {
+ sp<MetaData> meta = mAudioTrack->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
- mAudioSource = source;
+ mAudioSource = mAudioTrack;
} else {
mAudioSource = OMXCodec::Create(
- mClient.interface(), source->getFormat(),
+ mClient.interface(), mAudioTrack->getFormat(),
false, // createEncoder
- source);
+ mAudioTrack);
}
if (mAudioSource != NULL) {
int64_t durationUs;
- if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (mDurationUs < 0 || durationUs > mDurationUs) {
mDurationUs = durationUs;
}
@@ -734,30 +737,32 @@
return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
}
-status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
- if (source == NULL) {
- return UNKNOWN_ERROR;
- }
+void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
if (mPrefetcher != NULL) {
source = mPrefetcher->addSource(source);
}
+ mVideoTrack = source;
+}
+
+status_t AwesomePlayer::initVideoDecoder() {
mVideoSource = OMXCodec::Create(
- mClient.interface(), source->getFormat(),
+ mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
- source);
+ mVideoTrack);
if (mVideoSource != NULL) {
int64_t durationUs;
- if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (mDurationUs < 0 || durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
- CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
- CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
+ CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
+ CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
mVideoSource->start();
}
@@ -1045,7 +1050,22 @@
return setDataSource_l(extractor);
}
+void AwesomePlayer::abortPrepare(status_t err) {
+ CHECK(err != OK);
+
+ if (mIsAsyncPrepare) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ }
+
+ mPrepareResult = err;
+ mFlags &= ~PREPARING;
+ mAsyncPrepareEvent = NULL;
+ mPreparedCondition.broadcast();
+}
+
void AwesomePlayer::onPrepareAsyncEvent() {
+ sp<Prefetcher> prefetcher;
+
{
Mutex::Autolock autoLock(mLock);
@@ -1053,24 +1073,29 @@
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();
-
+ abortPrepare(err);
return;
}
}
- }
- sp<Prefetcher> prefetcher;
+ if (mVideoTrack != NULL && mVideoSource == NULL) {
+ status_t err = initVideoDecoder();
- {
- Mutex::Autolock autoLock(mLock);
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ if (mAudioTrack != NULL && mAudioSource == NULL) {
+ status_t err = initAudioDecoder();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
prefetcher = mPrefetcher;
}
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index ab38bca..c168771 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -36,8 +36,10 @@
namespace android {
// Everything must match except for
-// protection, bitrate, padding, private bits and mode extension.
-static const uint32_t kMask = 0xfffe0ccf;
+// protection, bitrate, padding, private bits, mode extension,
+// copyright bit, original bit and emphasis.
+// Yes ... there are things that must indeed match...
+static const uint32_t kMask = 0xfffe0cc0;
static bool get_mp3_frame_size(
uint32_t header, size_t *frame_size,
@@ -669,7 +671,7 @@
}
// Lost sync.
- LOGV("lost sync!\n");
+ LOGV("lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader);
off_t pos = mCurrentPos;
if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index 9c73f4a..493570a 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -59,6 +59,7 @@
int64_t mSeekTimeUs;
int64_t mCacheDurationUs;
bool mPrefetcherStopped;
+ bool mCurrentlyPrefetching;
List<MediaBuffer *> mCachedBuffers;
@@ -205,10 +206,6 @@
continue;
}
- if (cacheDurationUs >= kMaxCacheDurationUs) {
- continue;
- }
-
if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
minCacheDurationUs = cacheDurationUs;
minIndex = i;
@@ -223,13 +220,13 @@
}
status_t Prefetcher::prepare() {
- // Buffer about 2 secs worth of data on prepare.
+ // Fill the cache.
int64_t duration;
bool noMoreData;
do {
duration = getCachedDurationUs(&noMoreData);
- } while (!noMoreData && duration < 2000000);
+ } while (!noMoreData && duration < kMaxCacheDurationUs);
return OK;
}
@@ -245,7 +242,8 @@
mReachedEOS(false),
mSeekTimeUs(0),
mCacheDurationUs(0),
- mPrefetcherStopped(false) {
+ mPrefetcherStopped(false),
+ mCurrentlyPrefetching(false) {
}
PrefetchedSource::~PrefetchedSource() {
@@ -275,6 +273,10 @@
Mutex::Autolock autoLock(mLock);
+ while (mCurrentlyPrefetching) {
+ mCondition.wait(mLock);
+ }
+
clearCache_l();
status_t err = mSource->stop();
@@ -344,15 +346,24 @@
return;
}
+ mCurrentlyPrefetching = true;
+
if (mSeekTimeUs >= 0) {
options.setSeekTo(mSeekTimeUs);
mSeekTimeUs = -1;
}
+ // Ensure our object does not go away while we're not holding
+ // the lock.
+ sp<PrefetchedSource> me = this;
+
+ mLock.unlock();
MediaBuffer *buffer;
status_t err = mSource->read(&buffer, &options);
+ mLock.lock();
if (err != OK) {
+ mCurrentlyPrefetching = false;
mReachedEOS = true;
mFinalStatus = err;
mCondition.signal();
@@ -380,6 +391,8 @@
mCachedBuffers.push_back(copy);
updateCacheDuration_l();
+
+ mCurrentlyPrefetching = false;
mCondition.signal();
}
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 4c11c36..a6dbf69 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -45,7 +45,9 @@
LOGI("Creating physical memory heap failed, reverting to regular heap.");
mMemoryHeap = new MemoryHeapBase(2 * mFrameSize);
} else {
- mMemoryHeap = new MemoryHeapPmem(mMemoryHeap);
+ sp<MemoryHeapPmem> pmemHeap = new MemoryHeapPmem(mMemoryHeap);
+ pmemHeap->slap();
+ mMemoryHeap = pmemHeap;
}
CHECK(mISurface.get() != NULL);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 3590987..7106524 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -112,10 +112,12 @@
sp<DataSource> mFileSource;
+ sp<MediaSource> mVideoTrack;
sp<MediaSource> mVideoSource;
sp<AwesomeRenderer> mVideoRenderer;
bool mVideoRendererIsPreview;
+ sp<MediaSource> mAudioTrack;
sp<MediaSource> mAudioSource;
AudioPlayer *mAudioPlayer;
int64_t mDurationUs;
@@ -199,8 +201,11 @@
void cancelPlayerEvents(bool keepBufferingGoing = false);
- status_t setAudioSource(sp<MediaSource> source);
- status_t setVideoSource(sp<MediaSource> source);
+ void setAudioSource(sp<MediaSource> source);
+ status_t initAudioDecoder();
+
+ void setVideoSource(sp<MediaSource> source);
+ status_t initVideoDecoder();
void onStreamDone();
@@ -210,6 +215,8 @@
void onBufferingUpdate();
void onCheckAudioStatus();
void onPrepareAsyncEvent();
+ void abortPrepare(status_t err);
+
status_t finishSetDataSource_l();
AwesomePlayer(const AwesomePlayer &);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
index 39caccd..a52fd76 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
@@ -141,14 +141,10 @@
mMediaRecorder.setOutputFile(filename);
mMediaRecorder.setVideoFrameRate(videoFps);
mMediaRecorder.setVideoSize(videoWidth, videoHeight);
- mMediaRecorder.setParameters(String.format("video-param-encoding-bitrate=%d",
- videoBitrate));
- mMediaRecorder.setParameters(String.format("audio-param-encoding-bitrate=%d",
- audioBitrate));
- mMediaRecorder.setParameters(String.format("audio-param-number-of-channels=%d",
- audioChannels));
- mMediaRecorder.setParameters(String.format("audio-param-sampling-rate=%d",
- audioSamplingRate));
+ mMediaRecorder.setVideoEncodingBitRate(videoBitrate);
+ mMediaRecorder.setAudioEncodingBitRate(audioBitrate);
+ mMediaRecorder.setAudioChannels(audioChannels);
+ mMediaRecorder.setAudioSamplingRate(audioSamplingRate);
mMediaRecorder.setVideoEncoder(videoEncoder);
mMediaRecorder.setAudioEncoder(audioEncoder);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 88e171d..a3bbb74 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -20,8 +20,11 @@
import com.android.mediaframeworktest.MediaNames;
import android.database.sqlite.SQLiteDatabase;
+import android.hardware.Camera;
+import android.hardware.Camera.PreviewCallback;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
+import android.os.Looper;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -42,6 +45,8 @@
import android.media.MediaMetadataRetriever;
import com.android.mediaframeworktest.MediaProfileReader;
+import android.hardware.Camera.PreviewCallback;
+
/**
* Junit / Instrumentation - performance measurement for media player and
* recorder
@@ -58,14 +63,24 @@
private static final String MEDIA_MEMORY_OUTPUT =
"/sdcard/mediaMemOutput.txt";
- //the tolerant memory leak
- private static final int MAX_ACCEPTED_MEMORY_LEAK_KB = 150;
-
private static int mStartMemory = 0;
private static int mEndMemory = 0;
private static int mStartPid = 0;
private static int mEndPid = 0;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback();
+ private final Object lock = new Object();
+ private final Object previewDone = new Object();
+ private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; // Milliseconds.
+
+ //the tolerant memory leak
+ private static int ENCODER_LIMIT = 150;
+ private static int DECODER_LIMIT = 150;
+ private static int CAMERA_LIMIT = 80;
+
+ Camera mCamera;
public MediaPlayerPerformance() {
super("com.android.mediaframeworktest", MediaFrameworkTest.class);
@@ -178,6 +193,66 @@
Log.v(TAG, "Average duration = " + sum / testFile.length);
}
+ private void initializeMessageLooper() {
+ new Thread() {
+ @Override
+ public void run() {
+ Looper.prepare();
+ Log.v(TAG, "start loopRun");
+ mLooper = Looper.myLooper();
+ mCamera = Camera.open();
+ synchronized (lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop();
+ Log.v(TAG, "initializeMessageLooper: quit.");
+ }
+ }.start();
+ }
+
+ private void terminateMessageLooper() {
+ mLooper.quit();
+ mCamera.release();
+ }
+
+ private final class RawPreviewCallback implements PreviewCallback {
+ public void onPreviewFrame(byte[] rawData, Camera camera) {
+ synchronized (previewDone) {
+ previewDone.notify();
+ }
+ }
+ }
+
+ public void stressCameraPreview() {
+ try {
+ synchronized (lock) {
+ initializeMessageLooper();
+ try {
+ lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+ } catch (Exception e) {
+ Log.v(TAG, "runTestOnMethod: wait was interrupted.");
+ }
+ }
+ mCamera.setPreviewCallback(mRawPreviewCallback);
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ mCamera.setPreviewDisplay(mSurfaceHolder);
+ mCamera.startPreview();
+ synchronized (previewDone) {
+ try {
+ previewDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+ Log.v(TAG, "Preview Done");
+ } catch (Exception e) {
+ Log.v(TAG, "wait was interrupted.");
+ }
+ }
+ Thread.sleep(1000);
+ mCamera.stopPreview();
+ terminateMessageLooper();
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
// Note: This test is to assume the mediaserver's pid is 34
public void mediaStressPlayback(String testFilePath) {
@@ -314,8 +389,9 @@
return vsizevalue;
}
- public boolean validateMemoryResult (int startPid, int startMemory, Writer output) throws Exception {
- //Wait for 10 seconds to make sure the memory settle.
+ public boolean validateMemoryResult(int startPid, int startMemory, Writer output, int limit)
+ throws Exception {
+ // Wait for 10 seconds to make sure the memory settle.
Thread.sleep(10000);
mEndPid = getMediaserverPid();
int memDiff = mEndMemory - startMemory;
@@ -329,9 +405,8 @@
output.write("mediaserver died. Test failed\n");
return false;
}
- //memory leak greter than the tolerant
- if (memDiff > MAX_ACCEPTED_MEMORY_LEAK_KB )
- return false;
+ // memory leak greter than the tolerant
+ if (memDiff > limit) return false;
return true;
}
@@ -356,7 +431,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
output.close();
assertTrue("H263 playback memory test", memoryResult);
}
@@ -375,7 +450,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
output.close();
assertTrue("H264 playback memory test", memoryResult);
}
@@ -394,7 +469,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, DECODER_LIMIT);
output.close();
assertTrue("wmv playback memory test", memoryResult);
}
@@ -415,7 +490,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
output.close();
assertTrue("H263 record only memory test", memoryResult);
}
@@ -435,7 +510,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
output.close();
assertTrue("mpeg4 record only memory test", memoryResult);
}
@@ -456,7 +531,7 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
output.close();
assertTrue("H263 audio video record memory test", memoryResult);
}
@@ -475,8 +550,27 @@
getMemoryWriteToLog(output, i);
}
output.write("\n");
- memoryResult = validateMemoryResult(mStartPid, mStartMemory, output);
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, ENCODER_LIMIT);
output.close();
assertTrue("audio record only memory test", memoryResult);
}
+
+ // Test case 8: Capture the memory usage after every 20 camera preview
+ @LargeTest
+ public void testCameraPreviewMemoryUsage() throws Exception {
+ boolean memoryResult = false;
+ mStartPid = getMediaserverPid();
+
+ File cameraPreviewMemoryOut = new File(MEDIA_MEMORY_OUTPUT);
+ Writer output = new BufferedWriter(new FileWriter(cameraPreviewMemoryOut, true));
+ output.write("Camera Preview Only\n");
+ for (int i = 0; i < NUM_STRESS_LOOP; i++) {
+ stressCameraPreview();
+ getMemoryWriteToLog(output, i);
+ }
+ output.write("\n");
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, output, CAMERA_LIMIT);
+ output.close();
+ assertTrue("camera preview memory test", memoryResult);
+ }
}
diff --git a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
index 2d05a49..7537e35 100644
--- a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
+++ b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
@@ -32,8 +32,7 @@
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm,
- SkBitmap::Config pref, Mode);
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
};
int nullObjectReturn(const char msg[]) {
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index f229f9d..209e71c 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -97,14 +97,14 @@
}
bool OmxJpegImageDecoder::onDecode(SkStream* stream,
- SkBitmap* bm, SkBitmap::Config pref, Mode mode) {
+ SkBitmap* bm, Mode mode) {
sp<MediaSource> source = prepareMediaSource(stream);
sp<MetaData> meta = source->getFormat();
int width;
int height;
meta->findInt32(kKeyWidth, &width);
meta->findInt32(kKeyHeight, &height);
- configBitmapSize(bm, pref, width, height);
+ configBitmapSize(bm, getPrefConfig(k32Bit_SrcDepth, false), width, height);
// mode == DecodeBounds
if (mode == SkImageDecoder::kDecodeBounds_Mode) {
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index 7d8bac09c..a313877 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -42,8 +42,7 @@
}
protected:
- virtual bool onDecode(SkStream* stream, SkBitmap* bm,
- SkBitmap::Config pref, Mode mode);
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
private:
JPEGSource* prepareMediaSource(SkStream* stream);
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index a186e58..882cbc5 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.pm.ConfigurationInfo;
+import android.graphics.PixelFormat;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
@@ -75,15 +76,23 @@
* <li>{@link #setGLWrapper(GLWrapper)}
* </ul>
* <p>
- * <h4>Choosing an EGL Configuration</h4>
- * A given Android device may support multiple possible types of drawing surfaces.
- * The available surfaces may differ in how may channels of data are present, as
- * well as how many bits are allocated to each channel. Therefore, the first thing
- * GLSurfaceView has to do when starting to render is choose what type of surface to use.
+ * <h4>Specifying the android.view.Surface</h4>
+ * By default GLSurfaceView will create a PixelFormat.RGB_565 format surface. If a translucent
+ * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
+ * The exact format of a TRANSLUCENT surface is device dependent, but it will be
+ * a 32-bit-per-pixel surface with 8 bits per component.
* <p>
- * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface
- * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example,
- * if you do not need a depth buffer) you can override the default behavior by calling one of the
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple EGLConfig rendering configurations.
+ * The available configurations may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
+ * <p>
+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_656 pixel format,
+ * with at least a 16-bit depth buffer and no stencil.
+ * <p>
+ * If you would prefer a different EGLConfig
+ * you can override the default behavior by calling one of the
* setEGLConfigChooser methods.
* <p>
* <h4>Debug Behavior</h4>
@@ -149,6 +158,7 @@
private final static boolean LOG_THREADS = false;
private final static boolean LOG_SURFACE = false;
private final static boolean LOG_RENDERER = false;
+ private final static boolean LOG_RENDERER_DRAW_FRAME = false;
// Work-around for bug 2263168
private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
/**
@@ -210,6 +220,7 @@
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
+ holder.setFormat(PixelFormat.RGB_565);
// setType is not needed for SDK 2.0 or newer. Uncomment this
// statement if back-porting this code to older SDKs.
// holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
@@ -330,8 +341,9 @@
* is called.
* <p>
* If no setEGLConfigChooser method is called, then by default the
- * view will choose a config as close to 16-bit RGB as possible, with
- * a depth buffer as close to 16 bits as possible.
+ * view will choose an EGLConfig that is compatible with the current
+ * android.view.Surface, with a depth buffer depth of
+ * at least 16 bits.
* @param configChooser
*/
public void setEGLConfigChooser(EGLConfigChooser configChooser) {
@@ -347,9 +359,9 @@
* called, it must be called before {@link #setRenderer(Renderer)}
* is called.
* <p>
- * If no setEGLConfigChooser method is called, then by default the
- * view will choose a config as close to 16-bit RGB as possible, with
- * a depth buffer as close to 16 bits as possible.
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_565 surface with a depth buffer depth of
+ * at least 16 bits.
*
* @param needDepth
*/
@@ -359,15 +371,15 @@
/**
* Install a config chooser which will choose a config
- * with at least the specified component sizes, and as close
- * to the specified component sizes as possible.
+ * with at least the specified depthSize and stencilSize,
+ * and exactly the specified redSize, greenSize, blueSize and alphaSize.
* <p>If this method is
* called, it must be called before {@link #setRenderer(Renderer)}
* is called.
* <p>
* If no setEGLConfigChooser method is called, then by default the
- * view will choose a config as close to 16-bit RGB as possible, with
- * a depth buffer as close to 16 bits as possible.
+ * view will choose an RGB_565 surface with a depth buffer depth of
+ * at least 16 bits.
*
*/
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
@@ -774,6 +786,10 @@
}
}
+ /**
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
+ * and at least the specified depth and stencil sizes.
+ */
private class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
@@ -797,14 +813,12 @@
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
- EGLConfig closestConfig = null;
- int closestDistance = 1000;
- for(EGLConfig config : configs) {
+ for (EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
- if (d >= mDepthSize && s>= mStencilSize) {
+ if ((d >= mDepthSize) && (s >= mStencilSize)) {
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
@@ -813,17 +827,13 @@
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
- int distance = Math.abs(r - mRedSize)
- + Math.abs(g - mGreenSize)
- + Math.abs(b - mBlueSize)
- + Math.abs(a - mAlphaSize);
- if (distance < closestDistance) {
- closestDistance = distance;
- closestConfig = config;
+ if ((r == mRedSize) && (g == mGreenSize)
+ && (b == mBlueSize) && (a == mAlphaSize)) {
+ return config;
}
}
}
- return closestConfig;
+ return null;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
@@ -846,18 +856,13 @@
}
/**
- * This class will choose a supported surface as close to
- * RGB565 as possible, with or without a depth buffer.
+ * This class will choose a RGB_565 surface with
+ * or without a depth buffer.
*
*/
private class SimpleEGLConfigChooser extends ComponentSizeChooser {
public SimpleEGLConfigChooser(boolean withDepthBuffer) {
- super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
- // Adjust target values. This way we'll accept a 4444 or
- // 555 buffer if there's no 565 buffer available.
- mRedSize = 5;
- mGreenSize = 6;
- mBlueSize = 5;
+ super(5, 6, 5, 0, withDepthBuffer ? 16 : 0, 0);
}
}
@@ -1209,15 +1214,18 @@
}
if (createEglSurface) {
+ if (LOG_SURFACE) {
+ Log.w("GLThread", "egl createSurface");
+ }
gl = (GL10) mEglHelper.createSurface(getHolder());
sGLThreadManager.checkGLDriver(gl);
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceCreated");
- }
createEglSurface = false;
}
if (createEglContext) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
createEglContext = false;
}
@@ -1230,7 +1238,7 @@
sizeChanged = false;
}
- if (LOG_RENDERER) {
+ if (LOG_RENDERER_DRAW_FRAME) {
Log.w("GLThread", "onDrawFrame");
}
mRenderer.onDrawFrame(gl);
@@ -1438,6 +1446,7 @@
}
private static class GLThreadManager {
+ private static String TAG = "GLThreadManager";
public synchronized void threadExiting(GLThread thread) {
if (LOG_THREADS) {
@@ -1483,7 +1492,7 @@
public synchronized boolean shouldReleaseEGLContextWhenPausing() {
checkGLESVersion();
- return mMultipleGLESContextsAllowed;
+ return !mMultipleGLESContextsAllowed;
}
public synchronized void checkGLDriver(GL10 gl) {
@@ -1493,6 +1502,10 @@
String renderer = gl.glGetString(GL10.GL_RENDERER);
mMultipleGLESContextsAllowed =
! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
+ + mMultipleGLESContextsAllowed);
+ }
notifyAll();
}
mGLESDriverCheckComplete = true;
@@ -1507,6 +1520,10 @@
if (mGLESVersion >= kGLES_20) {
mMultipleGLESContextsAllowed = true;
}
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLESVersion mGLESVersion =" +
+ " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
+ }
mGLESVersionCheckComplete = true;
}
}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index a79f0cd..02e1f07 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -93,11 +93,16 @@
* PackageHelper.RECOMMEND_FAILED_INVALID_APK for parse errors.
*/
public int getRecommendedInstallLocation(final Uri fileUri) {
- if (!fileUri.getScheme().equals("file")) {
+ if (fileUri == null) {
+ Log.i(TAG, "Invalid package uri " + fileUri);
+ return PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ }
+ String scheme = fileUri.getScheme();
+ if (scheme != null && !scheme.equals("file")) {
Log.w(TAG, "Falling back to installing on internal storage only");
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
- final String archiveFilePath = fileUri.getPath();
+ String archiveFilePath = fileUri.getPath();
PackageParser packageParser = new PackageParser(archiveFilePath);
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
@@ -252,7 +257,8 @@
}
private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) {
- if (pPackageURI.getScheme().equals("file")) {
+ String scheme = pPackageURI.getScheme();
+ if (scheme == null || scheme.equals("file")) {
final File srcPackageFile = new File(pPackageURI.getPath());
// We copy the source package file to a temp file and then rename it to the
// destination file in order to eliminate a window where the package directory
@@ -261,7 +267,7 @@
Log.e(TAG, "Couldn't copy file: " + srcPackageFile);
return false;
}
- } else if (pPackageURI.getScheme().equals("content")) {
+ } else if (scheme.equals("content")) {
ParcelFileDescriptor fd = null;
try {
fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1e9c312..bd131e0 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -60,7 +60,7 @@
<!-- user interface sound effects -->
<integer name="def_power_sounds_enabled">1</integer>
- <string name="def_low_battery_sound" translatable="false">/system/media/ui/LowBattery.ogg</string>
+ <string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
<integer name="def_dock_sounds_enabled">0</integer>
<string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
<string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2738efbd..d5c9855a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -30,9 +30,9 @@
import java.util.Arrays;
import java.util.zip.CRC32;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
-import android.backup.BackupHelperAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelperAgent;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index d24814d..0e75fbc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -20,8 +20,8 @@
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
-import android.backup.BackupDataInput;
-import android.backup.IBackupManager;
+import android.app.backup.BackupDataInput;
+import android.app.backup.IBackupManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db802d3..7d648d3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -21,7 +21,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import android.backup.BackupManager;
+import android.app.backup.BackupManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -30,9 +30,11 @@
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQueryBuilder;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.provider.DrmStore;
@@ -48,6 +50,8 @@
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_OLD_FAVORITES = "old_favorites";
+ private static final String[] COLUMN_VALUE = new String[] { "value" };
+
protected DatabaseHelper mOpenHelper;
private BackupManager mBackupManager;
@@ -220,6 +224,44 @@
}
}
+ /**
+ * Fast path that avoids the use of chatty remoted Cursors.
+ */
+ @Override
+ public Bundle call(String method, String request, Bundle args) {
+ if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
+ return lookupValue("system", request);
+ }
+
+ if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
+ return lookupValue("secure", request);
+ }
+ return null;
+ }
+
+ // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
+ // possibly with a null value, or null on failure.
+ private Bundle lookupValue(String table, String key) {
+ // TODO: avoid database lookup and serve from in-process cache.
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor cursor = null;
+ try {
+ cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
+ null, null, null, null);
+ if (cursor != null && cursor.getCount() == 1) {
+ cursor.moveToFirst();
+ String value = cursor.getString(0);
+ return Bundle.forPair("value", value);
+ }
+ } catch (SQLiteException e) {
+ Log.w(TAG, "settings lookup error", e);
+ return null;
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ return Bundle.forPair("value", null);
+ }
+
@Override
public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
SqlArguments args = new SqlArguments(url, where, whereArgs);
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 2f5cfa3..b7acd96 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -551,7 +551,7 @@
return result;
}
- int bufSize = 10;
+ int bufSize = 12;
char buffer [bufSize];
sprintf(buffer, "%d", speechRate);
@@ -581,7 +581,7 @@
Mutex::Autolock l(engineMutex);
- int bufSize = 10;
+ int bufSize = 12;
char buffer [bufSize];
sprintf(buffer, "%d", pitch);
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 6255275..cd46c05 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -36,9 +36,9 @@
// Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
// the output of the synthesis. The low shelving filter removes it, leaving room for
// amplification.
- private final static float PICO_FILTER_GAIN = 5.5f; // linear gain
- private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
- private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f; // in Hz
+ private final static float PICO_FILTER_GAIN = 4.0f; // linear gain
+ private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -16.0f; // in dB
+ private final static float PICO_FILTER_TRANSITION_FREQ = 1000.0f; // in Hz
private final static float PICO_FILTER_SHELF_SLOPE = 1.0f; // Q
//
@@ -50,7 +50,7 @@
*/
public SynthProxy(String nativeSoLib) {
boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
- Log.v(TtsService.SERVICE_TAG, "about to load "+ nativeSoLib + ", applyFilter="+applyFilter);
+ Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
diff --git a/preloaded-classes b/preloaded-classes
index 28cbba3..5672c66 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -57,15 +57,15 @@
android.appwidget.AppWidgetManager
android.appwidget.AppWidgetProvider
android.appwidget.AppWidgetProviderInfo
-android.backup.BackupDataInput
-android.backup.BackupDataInput$EntityHeader
-android.backup.BackupDataOutput
-android.backup.BackupHelperAgent
-android.backup.BackupHelperDispatcher
-android.backup.BackupHelperDispatcher$Header
-android.backup.FileBackupHelperBase
-android.backup.IBackupManager$Stub
-android.backup.RestoreSet
+android.app.backup.BackupDataInput
+android.app.backup.BackupDataInput$EntityHeader
+android.app.backup.BackupDataOutput
+android.app.backup.BackupHelperAgent
+android.app.backup.BackupHelperDispatcher
+android.app.backup.BackupHelperDispatcher$Header
+android.app.backup.FileBackupHelperBase
+android.app.backup.IBackupManager$Stub
+android.app.backup.RestoreSet
android.bluetooth.BluetoothAdapter
android.bluetooth.BluetoothAudioGateway
android.bluetooth.BluetoothSocket
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index e5a5e03..5de68f9 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.net.Uri;
@@ -709,7 +710,10 @@
info.provider = component;
p.uid = activityInfo.applicationInfo.uid;
- TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+ Resources res = mPackageManager.getResourcesForApplication(
+ activityInfo.applicationInfo);
+
+ TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AppWidgetProviderInfo);
// These dimensions has to be resolved in the application's context.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 8b0c264..ffba4ad 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,10 +23,10 @@
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
-import android.backup.IBackupManager;
-import android.backup.IRestoreObserver;
-import android.backup.IRestoreSession;
-import android.backup.RestoreSet;
+import android.app.backup.RestoreSet;
+import android.app.backup.IBackupManager;
+import android.app.backup.IRestoreObserver;
+import android.app.backup.IRestoreSession;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -96,9 +96,9 @@
// the first backup pass.
private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
- private static final String RUN_BACKUP_ACTION = "android.backup.intent.RUN";
- private static final String RUN_INITIALIZE_ACTION = "android.backup.intent.INIT";
- private static final String RUN_CLEAR_ACTION = "android.backup.intent.CLEAR";
+ private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
+ private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
+ private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
private static final int MSG_RUN_RESTORE = 3;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 67b6200..a1c45dc 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1457,15 +1457,36 @@
}
// javadoc from interface
- public boolean tether(String iface) {
+ public int tether(String iface) {
enforceTetherChangePermission();
- return isTetheringSupported() && mTethering.tether(iface);
+
+ if (isTetheringSupported()) {
+ return mTethering.tether(iface);
+ } else {
+ return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+ }
}
// javadoc from interface
- public boolean untether(String iface) {
+ public int untether(String iface) {
enforceTetherChangePermission();
- return isTetheringSupported() && mTethering.untether(iface);
+
+ if (isTetheringSupported()) {
+ return mTethering.untether(iface);
+ } else {
+ return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+ }
+ }
+
+ // javadoc from interface
+ public int getLastTetherError(String iface) {
+ enforceTetherAccessPermission();
+
+ if (isTetheringSupported()) {
+ return mTethering.getLastTetherError(iface);
+ } else {
+ return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+ }
}
// TODO - proper iface API for selection by property, inspection, etc
@@ -1499,6 +1520,11 @@
return mTethering.getTetheredIfaces();
}
+ public String[] getTetheringErroredIfaces() {
+ enforceTetherAccessPermission();
+ return mTethering.getErroredIfaces();
+ }
+
// if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 25ebee4..bee8872 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -16,53 +16,24 @@
package com.android.server;
-import android.app.Activity;
-import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
-import android.app.IActivityManager;
-import android.app.IUiModeManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Binder;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.provider.Settings;
import android.server.BluetoothService;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.app.DisableCarModeActivity;
-import com.android.internal.widget.LockPatternUtils;
-
import java.io.FileNotFoundException;
import java.io.FileReader;
@@ -76,174 +47,22 @@
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
- private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
-
private static final int MSG_DOCK_STATE = 0;
- private static final int MSG_UPDATE_TWILIGHT = 1;
- private static final int MSG_ENABLE_LOCATION_UPDATES = 2;
-
- public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_MASK >> 4;
- public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
- public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
-
- private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
- private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
- // velocity for estimating a potential movement: 150km/h
- private static final float MAX_VELOCITY_M_MS = 150 / 3600;
- private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
-
- private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
- private int mNightMode = MODE_NIGHT_NO;
- private boolean mCarModeEnabled = false;
-
private boolean mSystemReady;
private final Context mContext;
private PowerManagerService mPowerManager;
- private NotificationManager mNotificationManager;
-
- private KeyguardManager.KeyguardLock mKeyguardLock;
- private boolean mKeyguardDisabled;
- private LockPatternUtils mLockPatternUtils;
-
- private AlarmManager mAlarmManager;
-
- private LocationManager mLocationManager;
- private Location mLocation;
- private StatusBarManager mStatusBarManager;
-
- // The broadcast receiver which receives the result of the ordered broadcast sent when
- // the dock state changes. The original ordered broadcast is sent with an initial result
- // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
- // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
- private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() != Activity.RESULT_OK) {
- return;
- }
-
- // Launch a dock activity
- String category;
- if (mCarModeEnabled) {
- // Only launch car home when car mode is enabled.
- category = Intent.CATEGORY_CAR_DOCK;
- } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
- category = Intent.CATEGORY_DESK_DOCK;
- } else {
- category = null;
- }
- if (category != null) {
- intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Slog.w(TAG, e.getCause());
- }
- }
- }
- };
-
- private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- };
-
- private final LocationListener mLocationListener = new LocationListener() {
-
- public void onLocationChanged(Location location) {
- updateLocation(location);
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- // If the network location is no longer available check for a GPS fix
- // and try to update the location.
- if (provider == LocationManager.NETWORK_PROVIDER &&
- status != LocationProvider.AVAILABLE) {
- updateLocation(mLocation);
- }
- }
-
- private void updateLocation(Location location) {
- location = DockObserver.chooseBestLocation(location,
- mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
- if (hasMoved(location)) {
- synchronized (this) {
- mLocation = location;
- }
- if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- }
-
- /*
- * The user has moved if the accuracy circles of the two locations
- * don't overlap.
- */
- private boolean hasMoved(Location location) {
- if (location == null) {
- return false;
- }
- if (mLocation == null) {
- return true;
- }
-
- /* if new location is older than the current one, the devices hasn't
- * moved.
- */
- if (location.getTime() < mLocation.getTime()) {
- return false;
- }
-
- /* Get the distance between the two points */
- float distance = mLocation.distanceTo(location);
-
- /* Get the total accuracy radius for both locations */
- float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
-
- /* If the distance is greater than the combined accuracy of the two
- * points then they can't overlap and hence the user has moved.
- */
- return distance > totalAccuracy;
- }
- };
public DockObserver(Context context, PowerManagerService pm) {
mContext = context;
mPowerManager = pm;
- mLockPatternUtils = new LockPatternUtils(context);
init(); // set initial status
- ServiceManager.addService("uimode", mBinder);
-
- mAlarmManager =
- (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
- mLocationManager =
- (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
- mContext.registerReceiver(mTwilightUpdateReceiver,
- new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
-
startObserving(DOCK_UEVENT_MATCH);
}
@@ -259,14 +78,6 @@
if (newState != mDockState) {
mPreviousDockState = mDockState;
mDockState = newState;
- boolean carModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
- if (mCarModeEnabled != carModeEnabled) {
- try {
- setCarMode(carModeEnabled);
- } catch (RemoteException e1) {
- Slog.w(TAG, "Unable to change car mode.", e1);
- }
- }
if (mSystemReady) {
// Don't force screen on when undocking from the desk dock.
// The change in power state will do this anyway.
@@ -302,24 +113,11 @@
void systemReady() {
synchronized (this) {
- KeyguardManager keyguardManager =
- (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mKeyguardLock = keyguardManager.newKeyguardLock(TAG);
-
- final boolean enableCarMode = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
- if (enableCarMode) {
- try {
- setCarMode(enableCarMode);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change car mode.", e);
- }
- }
// don't bother broadcasting undocked here
if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
update();
}
mSystemReady = true;
- mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
}
@@ -345,17 +143,7 @@
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- if (mCarModeEnabled && mDockState != Intent.EXTRA_DOCK_STATE_CAR) {
- // Pretend to be in DOCK_STATE_CAR.
- intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR);
- } else if (!mCarModeEnabled && mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
- // Pretend to be in DOCK_STATE_UNDOCKED.
- intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
- } else {
- intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
- }
- intent.putExtra(Intent.EXTRA_PHYSICAL_DOCK_STATE, mDockState);
- intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
// Check if this is Bluetooth Dock
String address = BluetoothService.readDockBluetoothAddress();
@@ -398,297 +186,10 @@
}
}
- // Send the ordered broadcast; the result receiver will receive after all
- // broadcasts have been sent. If any broadcast receiver changes the result
- // code from the initial value of RESULT_OK, then the result receiver will
- // not launch the corresponding dock application. This gives apps a chance
- // to override the behavior and stay in their app even when the device is
- // placed into a dock.
- mContext.sendStickyOrderedBroadcast(
- intent, mResultReceiver, null, Activity.RESULT_OK, null, null);
-
- }
- break;
- case MSG_UPDATE_TWILIGHT:
- synchronized (this) {
- if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
- try {
- DockObserver.this.updateTwilight();
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change night mode.", e);
- }
- }
- }
- break;
- case MSG_ENABLE_LOCATION_UPDATES:
- if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
- mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
- LOCATION_UPDATE_MS, LOCATION_UPDATE_DISTANCE_METER, mLocationListener);
- retrieveLocation();
- if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
- try {
- DockObserver.this.updateTwilight();
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to change night mode.", e);
- }
- }
- } else {
- long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
- interval *= 1.5;
- if (interval == 0) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
- } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
- }
- Bundle bundle = new Bundle();
- bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
- Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
- newMsg.setData(bundle);
- mHandler.sendMessageDelayed(newMsg, interval);
+ mContext.sendStickyBroadcast(intent);
}
break;
}
}
-
- private void retrieveLocation() {
- final Location gpsLocation =
- mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
- Location location;
- Criteria criteria = new Criteria();
- criteria.setSpeedRequired(false);
- criteria.setAltitudeRequired(false);
- criteria.setBearingRequired(false);
- final String bestProvider = mLocationManager.getBestProvider(criteria, true);
- if (LocationManager.GPS_PROVIDER.equals(bestProvider)) {
- location = gpsLocation;
- } else {
- location = DockObserver.chooseBestLocation(gpsLocation,
- mLocationManager.getLastKnownLocation(bestProvider));
- }
- // In the case there is no location available (e.g. GPS fix or network location
- // is not available yet), the longitude of the location is estimated using the timezone,
- // latitude and accuracy are set to get a good average.
- if (location == null) {
- Time currentTime = new Time();
- currentTime.set(System.currentTimeMillis());
- double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
- (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
- location = new Location("fake");
- location.setLongitude(lngOffset);
- location.setLatitude(59.95);
- location.setAccuracy(417000.0f);
- location.setTime(System.currentTimeMillis());
- }
- synchronized (this) {
- mLocation = location;
- }
- }
- };
-
- private void adjustStatusBarCarMode() {
- if (mStatusBarManager == null) {
- mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
- }
-
- // Fear not: StatusBarService manages a list of requests to disable
- // features of the status bar; these are ORed together to form the
- // active disabled list. So if (for example) the device is locked and
- // the status bar should be totally disabled, the calls below will
- // have no effect until the device is unlocked.
- if (mStatusBarManager != null) {
- long ident = Binder.clearCallingIdentity();
- mStatusBarManager.disable(mCarModeEnabled
- ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
- : StatusBarManager.DISABLE_NONE);
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- if (mNotificationManager != null) {
- long ident = Binder.clearCallingIdentity();
- if (mCarModeEnabled) {
- Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
-
- Notification n = new Notification();
- n.icon = R.drawable.stat_notify_car_mode;
- n.defaults = Notification.DEFAULT_LIGHTS;
- n.flags = Notification.FLAG_ONGOING_EVENT;
- n.when = 0;
- n.setLatestEventInfo(
- mContext,
- mContext.getString(R.string.car_mode_disable_notification_title),
- mContext.getString(R.string.car_mode_disable_notification_message),
- PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
- mNotificationManager.notify(0, n);
- } else {
- mNotificationManager.cancel(0);
- }
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void setCarMode(boolean enabled) throws RemoteException {
- mCarModeEnabled = enabled;
- if (enabled) {
- if (mNightMode == MODE_NIGHT_AUTO) {
- updateTwilight();
- } else {
- setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode << 4);
- }
- } else {
- // Disabling the car mode clears the night mode.
- setMode(Configuration.UI_MODE_TYPE_NORMAL,
- Configuration.UI_MODE_NIGHT_UNDEFINED);
- }
- adjustStatusBarCarMode();
- }
-
- private void setMode(int modeType, int modeNight) throws RemoteException {
- long ident = Binder.clearCallingIdentity();
- final IActivityManager am = ActivityManagerNative.getDefault();
- Configuration config = am.getConfiguration();
- if (config.uiMode != (modeType | modeNight)) {
- config.uiMode = modeType | modeNight;
- am.updateConfiguration(config);
- }
- Binder.restoreCallingIdentity(ident);
- }
-
- private void setNightMode(int mode) throws RemoteException {
- if (mNightMode != mode) {
- mNightMode = mode;
- switch (mode) {
- case MODE_NIGHT_NO:
- case MODE_NIGHT_YES:
- setMode(Configuration.UI_MODE_TYPE_CAR, mode << 4);
- break;
- case MODE_NIGHT_AUTO:
- long ident = Binder.clearCallingIdentity();
- updateTwilight();
- Binder.restoreCallingIdentity(ident);
- break;
- default:
- setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4);
- break;
- }
- }
- }
-
- private void updateTwilight() throws RemoteException {
- synchronized (this) {
- if (mLocation == null) {
- return;
- }
- final long currentTime = System.currentTimeMillis();
- int nightMode;
- // calculate current twilight
- TwilightCalculator tw = new TwilightCalculator();
- tw.calculateTwilight(currentTime,
- mLocation.getLatitude(), mLocation.getLongitude());
- if (tw.mState == TwilightCalculator.DAY) {
- nightMode = MODE_NIGHT_NO;
- } else {
- nightMode = MODE_NIGHT_YES;
- }
-
- // schedule next update
- long nextUpdate = 0;
- if (tw.mSunrise == -1 || tw.mSunset == -1) {
- // In the case the day or night never ends the update is scheduled 12 hours later.
- nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
- } else {
- final int mLastTwilightState = tw.mState;
- // add some extra time to be on the save side.
- nextUpdate += DateUtils.MINUTE_IN_MILLIS;
- if (currentTime > tw.mSunset) {
- // next update should be on the following day
- tw.calculateTwilight(currentTime
- + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
- mLocation.getLongitude());
- }
-
- if (mLastTwilightState == TwilightCalculator.NIGHT) {
- nextUpdate += tw.mSunrise;
- } else {
- nextUpdate += tw.mSunset;
- }
- }
-
- Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
- mAlarmManager.cancel(pendingIntent);
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
-
- // Make sure that we really set the new mode only if we're in car mode and
- // automatic switching is enables.
- if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
- setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
- }
- }
- }
-
- /**
- * Check which of two locations is better by comparing the distance a device
- * could have cover since the last timestamp of the location.
- *
- * @param location first location
- * @param otherLocation second location
- * @return one of the two locations
- */
- protected static Location chooseBestLocation(Location location, Location otherLocation) {
- if (location == null) {
- return otherLocation;
- }
- if (otherLocation == null) {
- return location;
- }
- final long currentTime = System.currentTimeMillis();
- float gpsPotentialMove = MAX_VELOCITY_M_MS * (currentTime - location.getTime())
- + location.getAccuracy();
- float otherPotentialMove = MAX_VELOCITY_M_MS * (currentTime - otherLocation.getTime())
- + otherLocation.getAccuracy();
- if (gpsPotentialMove < otherPotentialMove) {
- return location;
- } else {
- return otherLocation;
- }
- }
-
- /**
- * Wrapper class implementing the IUiModeManager interface.
- */
- private final IUiModeManager.Stub mBinder = new IUiModeManager.Stub() {
-
- public void disableCarMode() throws RemoteException {
- if (mCarModeEnabled) {
- setCarMode(false);
- update();
- }
- }
-
- public void enableCarMode() throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ENABLE_CAR_MODE,
- "Need ENABLE_CAR_MODE permission");
- if (!mCarModeEnabled) {
- setCarMode(true);
- update();
- }
- }
-
- public void setNightMode(int mode) throws RemoteException {
- if (mCarModeEnabled) {
- DockObserver.this.setNightMode(mode);
- }
- }
-
- public int getNightMode() throws RemoteException {
- return mNightMode;
- }
};
}
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 1c18d6f..07a74da 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -22,6 +22,8 @@
import android.view.Surface;
import android.view.WindowManagerPolicy;
+import java.io.PrintWriter;
+
public class InputDevice {
static final boolean DEBUG_POINTERS = false;
static final boolean DEBUG_HACKS = false;
@@ -32,6 +34,16 @@
/** Maximum number of pointers we will track and report. */
static final int MAX_POINTERS = 10;
+ /**
+ * Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value.
+ */
+ private static final int JUMPY_EPSILON_DIVISOR = 212;
+
+ /** Number of jumpy points to drop for touchscreens that need it. */
+ private static final int JUMPY_TRANSITION_DROPS = 3;
+ private static final int JUMPY_DROP_LIMIT = 3;
+
final int id;
final int classes;
final String name;
@@ -58,6 +70,7 @@
float yMoveScale;
MotionEvent currentMove = null;
boolean changed = false;
+ boolean everChanged = false;
long mDownTime = 0;
// The currently assigned pointer IDs, corresponding to the last data.
@@ -81,6 +94,9 @@
// Used to determine whether we dropped bad data, to avoid doing
// it repeatedly.
final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
+
+ // Used to count the number of jumpy points dropped.
+ private int mJumpyPointsDropped = 0;
// Used to perform averaging of reported coordinates, to smooth
// the data and filter out transients during a release.
@@ -103,6 +119,56 @@
int mAddingPointerOffset = 0;
final boolean[] mDown = new boolean[MAX_POINTERS];
+ void dumpIntArray(PrintWriter pw, int[] array) {
+ pw.print("[");
+ for (int i=0; i<array.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(array[i]);
+ }
+ pw.print("]");
+ }
+
+ void dumpBooleanArray(PrintWriter pw, boolean[] array) {
+ pw.print("[");
+ for (int i=0; i<array.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(array[i] ? "true" : "false");
+ }
+ pw.print("]");
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision);
+ pw.print(" yPrecision="); pw.println(yPrecision);
+ pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale);
+ pw.print(" yMoveScale="); pw.println(yMoveScale);
+ if (currentMove != null) {
+ pw.print(prefix); pw.print("currentMove="); pw.println(currentMove);
+ }
+ if (changed || mDownTime != 0) {
+ pw.print(prefix); pw.print("changed="); pw.print(changed);
+ pw.print(" mDownTime="); pw.println(mDownTime);
+ }
+ pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds);
+ pw.println("");
+ if (mSkipLastPointers || mLastNumPointers != 0) {
+ pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers);
+ pw.print(" mLastNumPointers="); pw.println(mLastNumPointers);
+ pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData);
+ pw.println("");
+ }
+ if (mNextNumPointers != 0) {
+ pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers);
+ pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData);
+ pw.println("");
+ }
+ pw.print(prefix); pw.print("mDroppedBadPoint=");
+ dumpBooleanArray(pw, mDroppedBadPoint); pw.println("");
+ pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset);
+ pw.print(prefix); pw.print("mDown=");
+ dumpBooleanArray(pw, mDown); pw.println("");
+ }
+
MotionState(int mx, int my) {
xPrecision = mx;
yPrecision = my;
@@ -179,6 +245,164 @@
}
}
+ void dropJumpyPoint(InputDevice dev) {
+ // We should always have absY, but let's be paranoid.
+ if (dev.absY == null) {
+ return;
+ }
+ final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR;
+
+ final int nextNumPointers = mNextNumPointers;
+ final int lastNumPointers = mLastNumPointers;
+ final int[] nextData = mNextData;
+ final int[] lastData = mLastData;
+
+ if (nextNumPointers != mLastNumPointers) {
+ if (DEBUG_HACKS) {
+ Slog.d("InputDevice", "Different pointer count " + lastNumPointers +
+ " -> " + nextNumPointers);
+ for (int i = 0; i < nextNumPointers; i++) {
+ int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+ Slog.d("InputDevice", "Pointer " + i + " (" +
+ mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
+ mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
+ }
+ }
+
+ // Just drop the first few events going from 1 to 2 pointers.
+ // They're bad often enough that they're not worth considering.
+ if (lastNumPointers == 1 && nextNumPointers == 2
+ && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ mNextNumPointers = 1;
+ mJumpyPointsDropped++;
+ } else if (lastNumPointers == 2 && nextNumPointers == 1
+ && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ // The event when we go from 2 -> 1 tends to be messed up too
+ System.arraycopy(lastData, 0, nextData, 0,
+ lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
+ mNextNumPointers = lastNumPointers;
+ mJumpyPointsDropped++;
+
+ if (DEBUG_HACKS) {
+ for (int i = 0; i < mNextNumPointers; i++) {
+ int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+ Slog.d("InputDevice", "Pointer " + i + " replaced (" +
+ mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
+ mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
+ }
+ }
+ } else {
+ mJumpyPointsDropped = 0;
+
+ if (DEBUG_HACKS) {
+ Slog.d("InputDevice", "Transition - drop limit reset");
+ }
+ }
+ return;
+ }
+
+ // A 'jumpy' point is one where the coordinate value for one axis
+ // has jumped to the other pointer's location. No need to do anything
+ // else if we only have one pointer.
+ if (nextNumPointers < 2) {
+ return;
+ }
+
+ int badPointerIndex = -1;
+ int badPointerReplaceXWith = 0;
+ int badPointerReplaceYWith = 0;
+ int badPointerDistance = Integer.MIN_VALUE;
+ for (int i = nextNumPointers - 1; i >= 0; i--) {
+ boolean dropx = false;
+ boolean dropy = false;
+
+ // Limit how many times a jumpy point can get dropped.
+ if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
+ final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+ final int x = nextData[ioff + MotionEvent.SAMPLE_X];
+ final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
+
+ if (DEBUG_HACKS) {
+ Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
+ }
+
+ // Check if a touch point is too close to another's coordinates
+ for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
+ if (j == i) {
+ continue;
+ }
+
+ final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+ final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
+ final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
+
+ dropx = Math.abs(x - xOther) <= jumpyEpsilon;
+ dropy = Math.abs(y - yOther) <= jumpyEpsilon;
+ }
+
+ if (dropx) {
+ int xreplace = lastData[MotionEvent.SAMPLE_X];
+ int yreplace = lastData[MotionEvent.SAMPLE_Y];
+ int distance = Math.abs(yreplace - y);
+ for (int j = 1; j < lastNumPointers; j++) {
+ final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+ int lasty = lastData[joff + MotionEvent.SAMPLE_Y];
+ int currDist = Math.abs(lasty - y);
+ if (currDist < distance) {
+ xreplace = lastData[joff + MotionEvent.SAMPLE_X];
+ yreplace = lasty;
+ distance = currDist;
+ }
+ }
+
+ int badXDelta = Math.abs(xreplace - x);
+ if (badXDelta > badPointerDistance) {
+ badPointerDistance = badXDelta;
+ badPointerIndex = i;
+ badPointerReplaceXWith = xreplace;
+ badPointerReplaceYWith = yreplace;
+ }
+ } else if (dropy) {
+ int xreplace = lastData[MotionEvent.SAMPLE_X];
+ int yreplace = lastData[MotionEvent.SAMPLE_Y];
+ int distance = Math.abs(xreplace - x);
+ for (int j = 1; j < lastNumPointers; j++) {
+ final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+ int lastx = lastData[joff + MotionEvent.SAMPLE_X];
+ int currDist = Math.abs(lastx - x);
+ if (currDist < distance) {
+ xreplace = lastx;
+ yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
+ distance = currDist;
+ }
+ }
+
+ int badYDelta = Math.abs(yreplace - y);
+ if (badYDelta > badPointerDistance) {
+ badPointerDistance = badYDelta;
+ badPointerIndex = i;
+ badPointerReplaceXWith = xreplace;
+ badPointerReplaceYWith = yreplace;
+ }
+ }
+ }
+ }
+ if (badPointerIndex >= 0) {
+ if (DEBUG_HACKS) {
+ Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
+ " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
+ ")");
+ }
+
+ final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
+ nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
+ nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
+ mJumpyPointsDropped++;
+ } else {
+ mJumpyPointsDropped = 0;
+ }
+ }
+
/**
* Special hack for devices that have bad screen data: aggregate and
* compute averages of the coordinate data, to reduce the amount of
@@ -775,6 +999,14 @@
int range;
int flat;
int fuzz;
+
+ final void dump(PrintWriter pw) {
+ pw.print("minValue="); pw.print(minValue);
+ pw.print(" maxValue="); pw.print(maxValue);
+ pw.print(" range="); pw.print(range);
+ pw.print(" flat="); pw.print(flat);
+ pw.print(" fuzz="); pw.print(fuzz);
+ }
};
InputDevice(int _id, int _classes, String _name,
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a6a3e27..e336a35 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1475,13 +1475,22 @@
final List<InputMethodInfo> immis = getEnabledInputMethodList();
- int N = (immis == null ? 0 : immis.size());
+ if (immis == null) {
+ return;
+ }
+
+ int N = immis.size();
mItems = new CharSequence[N];
mIms = new InputMethodInfo[N];
for (int i = 0; i < N; ++i) {
InputMethodInfo property = immis.get(i);
+ if (property == null) {
+ i--;
+ N--;
+ continue;
+ }
mItems[i] = property.loadLabel(pm);
mIms[i] = property;
}
@@ -1518,9 +1527,14 @@
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
synchronized (mMethodMap) {
+ if (mIms == null || mIms.length <= which) {
+ return;
+ }
InputMethodInfo im = mIms[which];
hideInputMethodMenu();
- setInputMethodLocked(im.getId());
+ if (im != null) {
+ setInputMethodLocked(im.getId());
+ }
}
}
});
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 0535d4c..8cd9578 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,10 +18,12 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Environment;
import android.os.LatencyTimer;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -43,6 +45,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.PrintWriter;
import java.util.ArrayList;
public abstract class KeyInputQueue {
@@ -58,6 +61,12 @@
*/
static boolean BAD_TOUCH_HACK = false;
+ /**
+ * Turn on some hacks to improve touch interaction with another device
+ * where touch coordinate data can get corrupted.
+ */
+ static boolean JUMPY_TOUCH_HACK = false;
+
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
@@ -282,8 +291,10 @@
lt = new LatencyTimer(100, 1000);
}
- BAD_TOUCH_HACK = context.getResources().getBoolean(
- com.android.internal.R.bool.config_filterTouchEvents);
+ Resources r = context.getResources();
+ BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
+
+ JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
mHapticFeedbackCallback = hapticFeedbackCallback;
@@ -738,6 +749,7 @@
InputDevice.MotionState ms = di.mAbs;
if (ms.changed) {
+ ms.everChanged = true;
ms.changed = false;
if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
@@ -755,6 +767,9 @@
if (BAD_TOUCH_HACK) {
ms.dropBadPoint(di);
}
+ if (JUMPY_TOUCH_HACK) {
+ ms.dropJumpyPoint(di);
+ }
boolean doMotion = !monitorVirtualKey(di,
ev, curTime, curTimeNano);
@@ -809,6 +824,7 @@
ms = di.mRel;
if (ms.changed) {
+ ms.everChanged = true;
ms.changed = false;
me = ms.generateRelMotion(di, curTime,
@@ -1280,4 +1296,91 @@
return null;
}
private static native boolean readEvent(RawInputEvent outEvent);
+
+ void dump(PrintWriter pw, String prefix) {
+ synchronized (mFirst) {
+ for (int i=0; i<mDevices.size(); i++) {
+ InputDevice dev = mDevices.valueAt(i);
+ pw.print(prefix); pw.print("Device #");
+ pw.print(mDevices.keyAt(i)); pw.print(" ");
+ pw.print(dev.name); pw.print(" (classes=0x");
+ pw.print(Integer.toHexString(dev.classes));
+ pw.println("):");
+ pw.print(prefix); pw.print(" mKeyDownTime=");
+ pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
+ pw.println(dev.mMetaKeysState);
+ if (dev.absX != null) {
+ pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
+ pw.println("");
+ }
+ if (dev.absY != null) {
+ pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
+ pw.println("");
+ }
+ if (dev.absPressure != null) {
+ pw.print(prefix); pw.print(" absPressure: ");
+ dev.absPressure.dump(pw); pw.println("");
+ }
+ if (dev.absSize != null) {
+ pw.print(prefix); pw.print(" absSize: ");
+ dev.absSize.dump(pw); pw.println("");
+ }
+ if (dev.mAbs.everChanged) {
+ pw.print(prefix); pw.println(" mAbs:");
+ dev.mAbs.dump(pw, prefix + " ");
+ }
+ if (dev.mRel.everChanged) {
+ pw.print(prefix); pw.println(" mRel:");
+ dev.mRel.dump(pw, prefix + " ");
+ }
+ }
+ pw.println(" ");
+ for (int i=0; i<mIgnoredDevices.size(); i++) {
+ InputDevice dev = mIgnoredDevices.valueAt(i);
+ pw.print(prefix); pw.print("Ignored Device #");
+ pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
+ pw.print(dev.name); pw.print(" (classes=0x");
+ pw.print(Integer.toHexString(dev.classes));
+ pw.println(")");
+ }
+ pw.println(" ");
+ for (int i=0; i<mVirtualKeys.size(); i++) {
+ VirtualKey vk = mVirtualKeys.get(i);
+ pw.print(prefix); pw.print("Virtual Key #");
+ pw.print(i); pw.println(":");
+ pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
+ pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
+ pw.print(" centery="); pw.print(vk.centery);
+ pw.print(" width="); pw.print(vk.width);
+ pw.print(" height="); pw.println(vk.height);
+ pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
+ pw.print(" hitTop="); pw.print(vk.hitTop);
+ pw.print(" hitRight="); pw.print(vk.hitRight);
+ pw.print(" hitBottom="); pw.println(vk.hitBottom);
+ if (vk.lastDevice != null) {
+ pw.print(prefix); pw.print(" lastDevice=#");
+ pw.println(vk.lastDevice.id);
+ }
+ if (vk.lastKeycode != 0) {
+ pw.print(prefix); pw.print(" lastKeycode=");
+ pw.println(vk.lastKeycode);
+ }
+ }
+ pw.println(" ");
+ pw.print(prefix); pw.print(" Default keyboard: ");
+ pw.println(SystemProperties.get("hw.keyboards.0.devname"));
+ pw.print(prefix); pw.print(" mGlobalMetaState=");
+ pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
+ pw.println(mHaveGlobalMetaState);
+ pw.print(prefix); pw.print(" mDisplayWidth=");
+ pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
+ pw.println(mDisplayHeight);
+ pw.print(prefix); pw.print(" mOrientation=");
+ pw.println(mOrientation);
+ if (mPressedVirtualKey != null) {
+ pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
+ pw.println(mPressedVirtualKey.scancode);
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index eaf4802..7aa092a 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -765,7 +765,9 @@
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
pw.println(prefix + "mUid=" + mUid);
pw.println(prefix + "mLastFixBroadcast:");
- mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
+ if (mLastFixBroadcast != null) {
+ mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
+ }
pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
}
}
@@ -1205,8 +1207,9 @@
// Remove expired alerts
if (intentsToRemove != null) {
for (PendingIntent i : intentsToRemove) {
- ProximityAlert alert = mProximityAlerts.remove(i);
+ ProximityAlert alert = mProximityAlerts.get(i);
mProximitiesEntered.remove(alert);
+ removeProximityAlertLocked(i);
}
}
}
@@ -1953,6 +1956,13 @@
i.getValue().dump(pw, " ");
}
}
+ for (LocationProviderInterface provider: mProviders) {
+ String state = provider.getInternalState();
+ if (state != null) {
+ pw.println(provider.getName() + " Internal State:");
+ pw.write(state);
+ }
+ }
}
}
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 41f3850..0974f7f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -523,6 +523,11 @@
Intent in = null;
+ if (oldState == VolumeState.Shared && newState != oldState) {
+ mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
+ Uri.parse("file://" + path)));
+ }
+
if (newState == VolumeState.Init) {
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
@@ -1171,13 +1176,6 @@
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mAsecMountSet) {
- if (!mAsecMountSet.contains(newId)) {
- mAsecMountSet.add(newId);
- }
- }
- }
return rc;
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 6d121c3..aa9ced8 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -447,7 +447,7 @@
String []tok = line.split(" ");
int code = Integer.parseInt(tok[0]);
if (code == NetdResponseCode.UsbRNDISStatusResult) {
- if (tok[2].equals("started"))
+ if (tok[3].equals("started"))
return true;
return false;
} else {
@@ -456,4 +456,23 @@
}
throw new IllegalStateException("Got an empty response");
}
+
+ public void startAccessPoint()
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ mConnector.doCommand(String.format("softap set"));
+ mConnector.doCommand(String.format("softap start"));
+ }
+
+ public void stopAccessPoint() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ mConnector.doCommand("softap stop");
+ }
+
}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index e45edfb..26b57bf 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -16,9 +16,9 @@
package com.android.server;
-import android.app.BackupAgent;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index bf2b1c7..1ff0244 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -31,7 +31,7 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
-import android.backup.IBackupManager;
+import android.app.backup.IBackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -318,6 +318,11 @@
// Set of pending broadcasts for aggregating enable/disable of components.
final HashMap<String, ArrayList<String>> mPendingBroadcasts
= new HashMap<String, ArrayList<String>>();
+ // Service Connection to remote media container service to copy
+ // package uri's from external media onto secure containers
+ // or internal storage.
+ private IMediaContainerService mContainerService = null;
+
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
static final int END_COPY = 4;
@@ -326,17 +331,23 @@
static final int START_CLEANING_PACKAGE = 7;
static final int FIND_INSTALL_LOC = 8;
static final int POST_INSTALL = 9;
+ static final int MCS_RECONNECT = 10;
+ static final int MCS_GIVE_UP = 11;
+
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
- private ServiceConnection mDefContainerConn = new ServiceConnection() {
+ final private DefaultContainerConnection mDefContainerConn =
+ new DefaultContainerConnection();
+ class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
IMediaContainerService imcs =
IMediaContainerService.Stub.asInterface(service);
- Message msg = mHandler.obtainMessage(MCS_BOUND, imcs);
- mHandler.sendMessage(msg);
+ mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
};
@@ -355,12 +366,27 @@
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
class PackageHandler extends Handler {
+ private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
new ArrayList<HandlerParams>();
- // Service Connection to remote media container service to copy
- // package uri's from external media onto secure containers
- // or internal storage.
- private IMediaContainerService mContainerService = null;
+
+ private boolean connectToService() {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
+ " DefaultContainerService");
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ if (mContext.bindService(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE)) {
+ mBound = true;
+ return true;
+ }
+ return false;
+ }
+
+ private void disconnectService() {
+ mContainerService = null;
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
PackageHandler(Looper looper) {
super(looper);
@@ -368,41 +394,99 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");
HandlerParams params = (HandlerParams) msg.obj;
- Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- if (mContainerService != null) {
- // No need to add to pending list. Use remote stub directly
- params.handleStartCopy(mContainerService);
- } else {
- if (mContext.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE)) {
- mPendingInstalls.add(params);
- } else {
+ int idx = mPendingInstalls.size();
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);
+ // If a bind was already initiated we dont really
+ // need to do anything. The pending install
+ // will be processed later on.
+ if (!mBound) {
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
Log.e(TAG, "Failed to bind to media container service");
- // Indicate service bind error
- params.handleServiceError();
+ params.serviceError();
+ return;
+ } else {
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mPendingInstalls.add(idx, params);
+ }
+ } else {
+ mPendingInstalls.add(idx, params);
+ // Already bound to the service. Just make
+ // sure we trigger off processing the first request.
+ if (idx == 0) {
+ mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
case MCS_BOUND: {
- // Initialize mContainerService if needed.
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
- if (mPendingInstalls.size() > 0) {
- HandlerParams params = mPendingInstalls.remove(0);
+ if (mContainerService == null) {
+ // Something seriously wrong. Bail out
+ Log.e(TAG, "Cannot bind to media container service");
+ for (HandlerParams params : mPendingInstalls) {
+ mPendingInstalls.remove(0);
+ // Indicate service bind error
+ params.serviceError();
+ }
+ mPendingInstalls.clear();
+ } else if (mPendingInstalls.size() > 0) {
+ HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
- params.handleStartCopy(mContainerService);
+ params.startCopy();
+ }
+ } else {
+ // Should never happen ideally.
+ Log.w(TAG, "Empty queue");
+ }
+ break;
+ }
+ case MCS_RECONNECT : {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_reconnect");
+ if (mPendingInstalls.size() > 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ if (!connectToService()) {
+ Log.e(TAG, "Failed to bind to media container service");
+ for (HandlerParams params : mPendingInstalls) {
+ mPendingInstalls.remove(0);
+ // Indicate service bind error
+ params.serviceError();
+ }
+ mPendingInstalls.clear();
}
}
break;
}
case MCS_UNBIND : {
- if (mPendingInstalls.size() == 0) {
- mContext.unbindService(mDefContainerConn);
- mContainerService = null;
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_unbind");
+ // Delete pending install
+ if (mPendingInstalls.size() > 0) {
+ mPendingInstalls.remove(0);
}
+ if (mPendingInstalls.size() == 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ } else {
+ // There are more pending requests in queue.
+ // Just post MCS_BOUND message to trigger processing
+ // of next pending install.
+ mHandler.sendEmptyMessage(MCS_BOUND);
+ }
+ break;
+ }
+ case MCS_GIVE_UP: {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries");
+ HandlerParams params = mPendingInstalls.remove(0);
break;
}
case SEND_PENDING_BROADCAST : {
@@ -456,6 +540,7 @@
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
PostInstallData data = mRunningInstalls.get(msg.arg1);
mRunningInstalls.delete(msg.arg1);
+ boolean deleteOld = false;
if (data != null) {
InstallArgs args = data.args;
@@ -479,13 +564,17 @@
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(true);
- }
+ deleteOld = true;
}
}
+ // Force a gc to clear up things
Runtime.getRuntime().gc();
-
+ // We delete after a gc for applications on sdcard.
+ if (deleteOld) {
+ synchronized (mInstallLock) {
+ res.removedInfo.args.doPostDeleteLI(true);
+ }
+ }
if (args.observer != null) {
try {
args.observer.packageInstalled(res.name, res.returnCode);
@@ -1266,6 +1355,10 @@
if(ps.pkg == null) {
ps.pkg = new PackageParser.Package(packageName);
ps.pkg.applicationInfo.packageName = packageName;
+ ps.pkg.applicationInfo.flags = ps.pkgFlags;
+ ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+ ps.pkg.applicationInfo.sourceDir = ps.codePathString;
+ ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
}
return generatePackageInfo(ps.pkg, flags);
}
@@ -2288,10 +2381,10 @@
synchronized (mPackages) {
// Look to see if we already know about this package.
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
- if (oldName != null && oldName.equals(pkg.mOriginalPackage)) {
+ if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
- ps = mSettings.peekPackageLP(pkg.mOriginalPackage);
+ ps = mSettings.peekPackageLP(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
@@ -2483,6 +2576,17 @@
}
return true;
}
+
+ private File getDataPathForPackage(PackageParser.Package pkg) {
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+ File dataPath;
+ if (useEncryptedFSDir) {
+ dataPath = new File(mSecureAppDataDir, pkg.packageName);
+ } else {
+ dataPath = new File(mAppDataDir, pkg.packageName);
+ }
+ return dataPath;
+ }
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode) {
@@ -2561,7 +2665,7 @@
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
// Only system apps can use these features.
- pkg.mOriginalPackage = null;
+ pkg.mOriginalPackages = null;
pkg.mRealPackage = null;
pkg.mAdoptPermissions = null;
}
@@ -2643,22 +2747,22 @@
}
if (false) {
- if (pkg.mOriginalPackage != null) {
+ if (pkg.mOriginalPackages != null) {
Log.w(TAG, "WAITING FOR DEBUGGER");
Debug.waitForDebugger();
- Log.i(TAG, "Package " + pkg.packageName + " from original package"
- + pkg.mOriginalPackage);
+ Log.i(TAG, "Package " + pkg.packageName + " from original packages"
+ + pkg.mOriginalPackages);
}
}
// Check if we are renaming from an original package name.
PackageSetting origPackage = null;
String realName = null;
- if (pkg.mOriginalPackage != null) {
+ if (pkg.mOriginalPackages != null) {
// This package may need to be renamed to a previously
// installed name. Let's check on that...
String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
- if (pkg.mOriginalPackage.equals(renamed)) {
+ if (pkg.mOriginalPackages.contains(renamed)) {
// This package had originally been installed as the
// original name, and we have already taken care of
// transitioning to the new one. Just update the new
@@ -2671,25 +2775,32 @@
pkg.setPackageName(renamed);
}
- } else if ((origPackage
- = mSettings.peekPackageLP(pkg.mOriginalPackage)) != null) {
- // We do have the package already installed under its
- // original name... should we use it?
- if (!verifyPackageUpdate(origPackage, pkg)) {
- // New package is not compatible with original.
- origPackage = null;
- } else if (origPackage.sharedUser != null) {
- // Make sure uid is compatible between packages.
- if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
- Log.w(TAG, "Unable to migrate data from " + origPackage.name
- + " to " + pkg.packageName + ": old uid "
- + origPackage.sharedUser.name
- + " differs from " + pkg.mSharedUserId);
- origPackage = null;
+ } else {
+ for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
+ if ((origPackage=mSettings.peekPackageLP(
+ pkg.mOriginalPackages.get(i))) != null) {
+ // We do have the package already installed under its
+ // original name... should we use it?
+ if (!verifyPackageUpdate(origPackage, pkg)) {
+ // New package is not compatible with original.
+ origPackage = null;
+ continue;
+ } else if (origPackage.sharedUser != null) {
+ // Make sure uid is compatible between packages.
+ if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
+ Log.w(TAG, "Unable to migrate data from " + origPackage.name
+ + " to " + pkg.packageName + ": old uid "
+ + origPackage.sharedUser.name
+ + " differs from " + pkg.mSharedUserId);
+ origPackage = null;
+ continue;
+ }
+ } else {
+ if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+ + pkg.packageName + " to old name " + origPackage.name);
+ }
+ break;
}
- } else {
- if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
- + pkg.packageName + " to old name " + origPackage.name);
}
}
}
@@ -2773,17 +2884,19 @@
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
- String names[] = p.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- if (mProviders.containsKey(names[j])) {
- PackageParser.Provider other = mProviders.get(names[j]);
- Log.w(TAG, "Can't install because provider name " + names[j] +
- " (in package " + pkg.applicationInfo.packageName +
- ") is already used by "
- + ((other != null && other.getComponentName() != null)
- ? other.getComponentName().getPackageName() : "?"));
- mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
- return null;
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ if (mProviders.containsKey(names[j])) {
+ PackageParser.Provider other = mProviders.get(names[j]);
+ Log.w(TAG, "Can't install because provider name " + names[j] +
+ " (in package " + pkg.applicationInfo.packageName +
+ ") is already used by "
+ + ((other != null && other.getComponentName() != null)
+ ? other.getComponentName().getPackageName() : "?"));
+ mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
+ return null;
+ }
}
}
}
@@ -2841,11 +2954,7 @@
} else {
// This is a normal package, need to make its data directory.
boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
- if (useEncryptedFSDir) {
- dataPath = new File(mSecureAppDataDir, pkgName);
- } else {
- dataPath = new File(mAppDataDir, pkgName);
- }
+ dataPath = getDataPathForPackage(pkg);
boolean uidError = false;
@@ -2996,38 +3105,40 @@
mProvidersByComponent.put(new ComponentName(p.info.packageName,
p.info.name), p);
p.syncable = p.info.isSyncable;
- String names[] = p.info.authority.split(";");
- p.info.authority = null;
- for (int j = 0; j < names.length; j++) {
- if (j == 1 && p.syncable) {
- // We only want the first authority for a provider to possibly be
- // syncable, so if we already added this provider using a different
- // authority clear the syncable flag. We copy the provider before
- // changing it because the mProviders object contains a reference
- // to a provider that we don't want to change.
- // Only do this for the second authority since the resulting provider
- // object can be the same for all future authorities for this provider.
- p = new PackageParser.Provider(p);
- p.syncable = false;
- }
- if (!mProviders.containsKey(names[j])) {
- mProviders.put(names[j], p);
- if (p.info.authority == null) {
- p.info.authority = names[j];
- } else {
- p.info.authority = p.info.authority + ";" + names[j];
+ if (p.info.authority != null) {
+ String names[] = p.info.authority.split(";");
+ p.info.authority = null;
+ for (int j = 0; j < names.length; j++) {
+ if (j == 1 && p.syncable) {
+ // We only want the first authority for a provider to possibly be
+ // syncable, so if we already added this provider using a different
+ // authority clear the syncable flag. We copy the provider before
+ // changing it because the mProviders object contains a reference
+ // to a provider that we don't want to change.
+ // Only do this for the second authority since the resulting provider
+ // object can be the same for all future authorities for this provider.
+ p = new PackageParser.Provider(p);
+ p.syncable = false;
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
- Log.d(TAG, "Registered content provider: " + names[j] +
- ", className = " + p.info.name +
- ", isSyncable = " + p.info.isSyncable);
- } else {
- PackageParser.Provider other = mProviders.get(names[j]);
- Log.w(TAG, "Skipping provider name " + names[j] +
- " (in package " + pkg.applicationInfo.packageName +
- "): name already used by "
- + ((other != null && other.getComponentName() != null)
- ? other.getComponentName().getPackageName() : "?"));
+ if (!mProviders.containsKey(names[j])) {
+ mProviders.put(names[j], p);
+ if (p.info.authority == null) {
+ p.info.authority = names[j];
+ } else {
+ p.info.authority = p.info.authority + ";" + names[j];
+ }
+ if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
+ Log.d(TAG, "Registered content provider: " + names[j] +
+ ", className = " + p.info.name +
+ ", isSyncable = " + p.info.isSyncable);
+ } else {
+ PackageParser.Provider other = mProviders.get(names[j]);
+ Log.w(TAG, "Skipping provider name " + names[j] +
+ " (in package " + pkg.applicationInfo.packageName +
+ "): name already used by "
+ + ((other != null && other.getComponentName() != null)
+ ? other.getComponentName().getPackageName() : "?"));
+ }
}
}
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
@@ -4407,12 +4518,41 @@
});
}
- interface HandlerParams {
- void handleStartCopy(IMediaContainerService imcs);
- void handleServiceError();
+ abstract class HandlerParams {
+ final static int MAX_RETRIES = 4;
+ int retry = 0;
+ final void startCopy() {
+ try {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "startCopy");
+ retry++;
+ if (retry > MAX_RETRIES) {
+ Log.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+ mHandler.sendEmptyMessage(MCS_GIVE_UP);
+ handleServiceError();
+ return;
+ } else {
+ handleStartCopy();
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_UNBIND");
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT");
+ mHandler.sendEmptyMessage(MCS_RECONNECT);
+ }
+ handleReturnCode();
+ }
+
+ final void serviceError() {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "serviceError");
+ handleServiceError();
+ handleReturnCode();
+ }
+ abstract void handleStartCopy() throws RemoteException;
+ abstract void handleServiceError();
+ abstract void handleReturnCode();
}
- class InstallParams implements HandlerParams {
+ class InstallParams extends HandlerParams {
final IPackageInstallObserver observer;
int flags;
final Uri packageURI;
@@ -4428,22 +4568,14 @@
this.installerPackageName = installerPackageName;
}
- private int getInstallLocation(IMediaContainerService imcs) {
- try {
- return imcs.getRecommendedInstallLocation(packageURI);
- } catch (RemoteException e) {
- }
- return -1;
- }
-
- public void handleStartCopy(IMediaContainerService imcs) {
+ public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// Dont need to invoke getInstallLocation for forward locked apps.
if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
flags &= ~PackageManager.INSTALL_EXTERNAL;
- } else if (imcs != null) {
+ } else {
// Remote call to find out default install location
- int loc = getInstallLocation(imcs);
+ int loc = mContainerService.getRecommendedInstallLocation(packageURI);
// Use install location to create InstallArgs and temporary
// install location
if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
@@ -4468,23 +4600,22 @@
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// Create copy only if we are not in an erroneous state.
// Remote call to initiate copy using temporary file
- ret = mArgs.copyApk(imcs, true);
+ ret = mArgs.copyApk(mContainerService, true);
}
mRet = ret;
- mHandler.sendEmptyMessage(MCS_UNBIND);
- handleReturnCode();
}
+ @Override
void handleReturnCode() {
processPendingInstall(mArgs, mRet);
}
- public void handleServiceError() {
+ @Override
+ void handleServiceError() {
mArgs = createInstallArgs(this);
mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- handleReturnCode();
}
- };
+ }
/*
* Utility class used in movePackage api.
@@ -4493,7 +4624,7 @@
* We probably want to return ErrorPrams for both failed installs
* and moves.
*/
- class MoveParams implements HandlerParams {
+ class MoveParams extends HandlerParams {
final IPackageMoveObserver observer;
final int flags;
final String packageName;
@@ -4515,25 +4646,36 @@
}
}
- public void handleStartCopy(IMediaContainerService imcs) {
+ public void handleStartCopy() throws RemoteException {
// Create the file args now.
- mRet = targetArgs.copyApk(imcs, false);
+ mRet = targetArgs.copyApk(mContainerService, false);
targetArgs.doPreInstall(mRet);
- mHandler.sendEmptyMessage(MCS_UNBIND);
- handleReturnCode();
+ if (DEBUG_SD_INSTALL) {
+ StringBuilder builder = new StringBuilder();
+ if (srcArgs != null) {
+ builder.append("src: ");
+ builder.append(srcArgs.getCodePath());
+ }
+ if (targetArgs != null) {
+ builder.append(" target : ");
+ builder.append(targetArgs.getCodePath());
+ }
+ Log.i(TAG, "Posting move MCS_UNBIND for " + builder.toString());
+ }
}
+ @Override
void handleReturnCode() {
targetArgs.doPostInstall(mRet);
// TODO invoke pending move
processPendingMove(this, mRet);
}
- public void handleServiceError() {
+ @Override
+ void handleServiceError() {
mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- handleReturnCode();
}
- };
+ }
private InstallArgs createInstallArgs(InstallParams params) {
if (installOnSd(params.flags)) {
@@ -4577,7 +4719,7 @@
}
abstract void createCopyFile();
- abstract int copyApk(IMediaContainerService imcs, boolean temp);
+ abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
abstract int doPreInstall(int status);
abstract boolean doRename(int status, String pkgName, String oldCodePath);
abstract int doPostInstall(int status);
@@ -4628,7 +4770,7 @@
created = true;
}
- int copyApk(IMediaContainerService imcs, boolean temp) {
+ int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (temp) {
// Generate temp file name
createCopyFile();
@@ -4662,7 +4804,6 @@
if (imcs.copyResource(packageURI, out)) {
ret = PackageManager.INSTALL_SUCCEEDED;
}
- } catch (RemoteException e) {
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
}
@@ -4818,16 +4959,13 @@
cid = getTempContainerId();
}
- int copyApk(IMediaContainerService imcs, boolean temp) {
+ int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (temp) {
createCopyFile();
}
- try {
- cachePath = imcs.copyResourceToContainer(
- packageURI, cid,
- getEncryptKey(), RES_FILE_NAME);
- } catch (RemoteException e) {
- }
+ cachePath = imcs.copyResourceToContainer(
+ packageURI, cid,
+ getEncryptKey(), RES_FILE_NAME);
return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
PackageManager.INSTALL_SUCCEEDED;
}
@@ -4862,67 +5000,34 @@
String oldCodePath) {
String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
String newCachePath = null;
- boolean enableRename = false;
- if (enableRename) {
- if (PackageHelper.isContainerMounted(cid)) {
- // Unmount the container
- if (!PackageHelper.unMountSdDir(cid)) {
- Log.i(TAG, "Failed to unmount " + cid + " before renaming");
- return false;
- }
- }
- if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
+ if (PackageHelper.isContainerMounted(cid)) {
+ // Unmount the container
+ if (!PackageHelper.unMountSdDir(cid)) {
+ Log.i(TAG, "Failed to unmount " + cid + " before renaming");
return false;
}
- if (!PackageHelper.isContainerMounted(newCacheId)) {
- Log.w(TAG, "Mounting container " + newCacheId);
- newCachePath = PackageHelper.mountSdDir(newCacheId,
- getEncryptKey(), Process.SYSTEM_UID);
- } else {
- newCachePath = PackageHelper.getSdDir(newCacheId);
- }
- if (newCachePath == null) {
- Log.w(TAG, "Failed to get cache path for " + newCacheId);
- return false;
- }
- // Mount old container?
- Log.i(TAG, "Succesfully renamed " + cid +
- " at path: " + cachePath + " to " + newCacheId +
- " at new path: " + newCachePath);
- cid = newCacheId;
- cachePath = newCachePath;
- return true;
- } else {
- // STOPSHIP work around for rename
- Log.i(TAG, "Copying instead of renaming");
- File srcFile = new File(getCodePath());
- // Create new container
- newCachePath = PackageHelper.createSdDir(srcFile, newCacheId,
- getEncryptKey(), Process.SYSTEM_UID);
- Log.i(TAG, "Created rename container " + newCacheId);
- File destFile = new File(newCachePath + "/" + RES_FILE_NAME);
- if (!FileUtils.copyFile(srcFile, destFile)) {
- Log.e(TAG, "Failed to copy " + srcFile + " to " + destFile);
- return false;
- }
- Log.i(TAG, "Successfully copied resource to " + newCachePath);
- if (!PackageHelper.finalizeSdDir(newCacheId)) {
- Log.e(TAG, "Failed to finalize " + newCacheId);
- PackageHelper.destroySdDir(newCacheId);
- return false;
- }
- Log.i(TAG, "Finalized " + newCacheId);
- Runtime.getRuntime().gc();
- // Unmount first
- PackageHelper.unMountSdDir(cid);
- // Delete old container
- PackageHelper.destroySdDir(cid);
- // Dont have to mount. Already mounted.
- cid = newCacheId;
- cachePath = newCachePath;
- return true;
}
+ if (!PackageHelper.renameSdDir(cid, newCacheId)) {
+ Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
+ return false;
+ }
+ if (!PackageHelper.isContainerMounted(newCacheId)) {
+ Log.w(TAG, "Mounting container " + newCacheId);
+ newCachePath = PackageHelper.mountSdDir(newCacheId,
+ getEncryptKey(), Process.SYSTEM_UID);
+ } else {
+ newCachePath = PackageHelper.getSdDir(newCacheId);
+ }
+ if (newCachePath == null) {
+ Log.w(TAG, "Failed to get cache path for " + newCacheId);
+ return false;
+ }
+ Log.i(TAG, "Succesfully renamed " + cid +
+ " at path: " + cachePath + " to " + newCacheId +
+ " at new path: " + newCachePath);
+ cid = newCacheId;
+ cachePath = newCachePath;
+ return true;
}
int doPostInstall(int status) {
@@ -5074,14 +5179,9 @@
int scanMode,
String installerPackageName, PackageInstalledInfo res) {
// Remember this for later, in case we need to rollback this install
- boolean dataDirExists;
String pkgName = pkg.packageName;
- if (useEncryptedFilesystemForPackage(pkg)) {
- dataDirExists = (new File(mSecureAppDataDir, pkgName)).exists();
- } else {
- dataDirExists = (new File(mAppDataDir, pkgName)).exists();
- }
+ boolean dataDirExists = getDataPathForPackage(pkg).exists();
res.name = pkgName;
synchronized(mPackages) {
if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
@@ -5401,13 +5501,14 @@
// Check if installing already existing package
if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.mRenamedPackages.get(pkgName);
- if (oldName != null && oldName.equals(pkg.mOriginalPackage)
+ if (pkg.mOriginalPackages != null
+ && pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
// name. We must continue using the original name, so
// rename the new package here.
- pkg.setPackageName(pkg.mOriginalPackage);
+ pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
} else if (mPackages.containsKey(pkgName)) {
@@ -5655,6 +5756,8 @@
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras);
}
}
+ // Force a gc here.
+ Runtime.getRuntime().gc();
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
if (info.args != null) {
@@ -7050,7 +7153,8 @@
void setFlags(int pkgFlags) {
this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
(pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
- (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
+ (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) |
+ (pkgFlags & ApplicationInfo.FLAG_NEVER_ENCRYPT);
}
}
@@ -8885,6 +8989,7 @@
* Return true if PackageManager does have packages to be updated.
*/
public boolean updateExternalMediaStatus(final boolean mediaStatus) {
+ final boolean ret;
synchronized (mPackages) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
mediaStatus+", mMediaMounted=" + mMediaMounted);
@@ -8892,72 +8997,77 @@
return false;
}
mMediaMounted = mediaStatus;
- boolean ret = false;
- synchronized (mPackages) {
- Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
- ret = appList != null && appList.size() > 0;
- }
- if (!ret) {
- // No packages will be effected by the sdcard update. Just return.
- return false;
- }
- // Queue up an async operation since the package installation may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- // If we are up here that means there are packages to be
- // enabled or disabled.
- final HashMap<SdInstallArgs, String> processCids =
- new HashMap<SdInstallArgs, String>();
- final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids);
- 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);
- }
- }
- });
- return true;
+ Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
+ ret = appList != null && appList.size() > 0;
}
+ // 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, ret);
+ }
+ });
+ return ret;
}
- private int[] getExternalMediaPackages(boolean mediaStatus,
- Map<SdInstallArgs, String> processCids) {
+ private void updateExternalMediaStatusInner(boolean mediaStatus,
+ boolean sendUpdateBroadcast) {
+ // If we are up here that means there are packages to be
+ // enabled or disabled.
final String list[] = PackageHelper.getSecureContainerList();
if (list == null || list.length == 0) {
- return null;
+ return;
}
int uidList[] = new int[list.length];
int num = 0;
+ HashSet<String> removeCids = new HashSet<String>();
+ HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+ /*HashMap<String, String> cidPathMap = new HashMap<String, String>();
+ // Don't hold any locks when getting cache paths
+ for (String cid : list) {
+ String cpath = PackageHelper.getSdDir(cid);
+ if (cpath == null) {
+ removeCids.add(cid);
+ } else {
+ cidPathMap.put(cid, cpath);
+ }
+ }*/
synchronized (mPackages) {
- Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
for (String cid : list) {
SdInstallArgs args = new SdInstallArgs(cid);
- String removeEntry = null;
- for (String app : appList) {
- if (args.matchContainer(app)) {
- removeEntry = app;
- break;
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
+ boolean failed = true;
+ try {
+ String pkgName = args.getPackageName();
+ if (pkgName == null) {
+ continue;
}
- }
- if (removeEntry == null) {
- // No matching app on device. Skip entry or may be cleanup?
- // Ignore default package
- continue;
- }
- appList.remove(removeEntry);
- PackageSetting ps = mSettings.mPackages.get(removeEntry);
- processCids.put(args, ps.codePathString);
- int uid = ps.userId;
- if (uid != -1) {
- uidList[num++] = uid;
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName);
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null && ps.codePathString != null &&
+ (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid +
+ " corresponds to pkg : " + pkgName +
+ " at code path: " + ps.codePathString);
+ // We do have a valid package installed on sdcard
+ processCids.put(args, ps.codePathString);
+ failed = false;
+ int uid = ps.userId;
+ if (uid != -1) {
+ uidList[num++] = uid;
+ }
+ }
+ } finally {
+ if (failed) {
+ // Stale container on sdcard. Just delete
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
+ removeCids.add(cid);
+ }
}
}
}
+ // Organize uids
int uidArr[] = null;
if (num > 0) {
// Sort uid list
@@ -8972,7 +9082,15 @@
}
}
}
- return uidArr;
+ // Process packages with valid entries.
+ if (mediaStatus) {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
+ loadMediaPackages(processCids, uidArr, sendUpdateBroadcast, removeCids);
+ startCleaningPackages();
+ } else {
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
+ unloadMediaPackages(processCids, uidArr, sendUpdateBroadcast);
+ }
}
private void sendResourcesChangedBroadcast(boolean mediaStatus,
@@ -8992,57 +9110,100 @@
}
}
- private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+ /*
+ * Look at potentially valid container ids from processCids
+ * If package information doesn't match the one on record
+ * or package scanning fails, the cid is added to list of
+ * removeCids and cleaned up. Since cleaning up containers
+ * involves destroying them, we do not want any parse
+ * references to such stale containers. So force gc's
+ * to avoid unnecessary crashes.
+ */
+ private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
+ int uidArr[], boolean sendUpdateBroadcast,
+ HashSet<String> removeCids) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<SdInstallArgs> keys = processCids.keySet();
+ boolean doGc = false;
for (SdInstallArgs args : keys) {
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.e(TAG, "Failed to install package: " + codePath + " from sdcard");
- continue;
- }
- // Parse package
- int parseFlags = PackageParser.PARSE_CHATTY |
- PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
- PackageParser pp = new PackageParser(codePath);
- pp.setSeparateProcesses(mSeparateProcesses);
- final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
- codePath, mMetrics, parseFlags);
- if (pkg == null) {
- Log.e(TAG, "Trying to install pkg : "
- + args.cid + " from " + args.cachePath);
- continue;
- }
- setApplicationInfoPaths(pkg, codePath, codePath);
+ if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
+ + args.cid);
int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- synchronized (mInstallLock) {
- // Scan the package
- if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
- synchronized (mPackages) {
- // Grant permissions
- grantPermissionsLP(pkg, false);
- // Persist settings
- mSettings.writeLP();
- retCode = PackageManager.INSTALL_SUCCEEDED;
- pkgList.add(pkg.packageName);
+ try {
+ // Make sure there are no container errors first.
+ if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
+ != PackageManager.INSTALL_SUCCEEDED) {
+ Log.e(TAG, "Failed to mount cid : " + args.cid +
+ " when installing from sdcard");
+ continue;
+ }
+ // Check code path here.
+ if (codePath == null || !codePath.equals(args.getCodePath())) {
+ Log.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
+ " does not match one in settings " + codePath);
+ continue;
+ }
+ // Parse package
+ int parseFlags = PackageParser.PARSE_CHATTY |
+ PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+ PackageParser pp = new PackageParser(codePath);
+ pp.setSeparateProcesses(mSeparateProcesses);
+ final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
+ codePath, mMetrics, parseFlags);
+ pp = null;
+ doGc = true;
+ // Check for parse errors
+ if (pkg == null) {
+ Log.e(TAG, "Parse error when installing install pkg : "
+ + args.cid + " from " + args.cachePath);
+ continue;
+ }
+ setApplicationInfoPaths(pkg, codePath, codePath);
+ synchronized (mInstallLock) {
+ // Scan the package
+ if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
+ synchronized (mPackages) {
+ // Grant permissions
+ grantPermissionsLP(pkg, false);
+ // Persist settings
+ mSettings.writeLP();
+ retCode = PackageManager.INSTALL_SUCCEEDED;
+ pkgList.add(pkg.packageName);
+ // Post process args
+ args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
+ }
+ } else {
+ Log.i(TAG, "Failed to install pkg: " +
+ pkg.packageName + " from sdcard");
}
- } else {
- Log.i(TAG, "Failed to install package: " + pkg.packageName + " from sdcard");
+ }
+
+ } finally {
+ if (retCode != PackageManager.INSTALL_SUCCEEDED) {
+ // Don't destroy container here. Wait till gc clears things up.
+ removeCids.add(args.cid);
}
}
- args.doPostInstall(retCode);
}
// Send a broadcast to let everyone know we are done processing
- sendResourcesChangedBroadcast(true, pkgList, uidArr);
- if (pkgList.size() > 0) {
+ if (sendUpdateBroadcast) {
+ sendResourcesChangedBroadcast(true, pkgList, uidArr);
+ }
+ if (doGc) {
Runtime.getRuntime().gc();
- // If something failed do we clean up here or next install?
+ }
+ // Delete any stale containers if needed.
+ if (removeCids != null) {
+ for (String cid : removeCids) {
+ Log.i(TAG, "Destroying stale container : " + cid);
+ PackageHelper.destroySdDir(cid);
+ }
}
}
- private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+ private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
+ int uidArr[], boolean sendUpdateBroadcast) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
ArrayList<String> pkgList = new ArrayList<String>();
ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
@@ -9064,13 +9225,14 @@
}
}
}
- sendResourcesChangedBroadcast(false, pkgList, uidArr);
// Send broadcasts
- if (pkgList.size() > 0) {
- Runtime.getRuntime().gc();
+ if (sendUpdateBroadcast) {
+ sendResourcesChangedBroadcast(false, pkgList, uidArr);
}
- // Do clean up. Just unmount
- for (SdInstallArgs args : failedList) {
+ // Force gc
+ Runtime.getRuntime().gc();
+ // Just unmount all valid containers.
+ for (SdInstallArgs args : keys) {
synchronized (mInstallLock) {
args.doPostDeleteLI(false);
}
@@ -9189,21 +9351,21 @@
}
}
}
- if (moveSucceeded) {
- // Delete older code
- synchronized (mInstallLock) {
- mp.srcArgs.cleanUpResourcesLI();
- }
- // Send resources available broadcast
- sendResourcesChangedBroadcast(true, pkgList, uidArr);
- Runtime.getRuntime().gc();
- }
+ // Send resources available broadcast
+ sendResourcesChangedBroadcast(true, pkgList, uidArr);
}
if (!moveSucceeded){
// Clean up failed installation
if (mp.targetArgs != null) {
mp.targetArgs.doPostInstall(
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ returnCode);
+ }
+ } else {
+ // Force a gc to clear things up.
+ Runtime.getRuntime().gc();
+ // Delete older code
+ synchronized (mInstallLock) {
+ mp.srcArgs.doPostDeleteLI(true);
}
}
IPackageMoveObserver observer = mp.observer;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 11b966e..0189c60 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -557,14 +557,6 @@
});
updateSettingsValues();
- if (mUseSoftwareAutoBrightness) {
- // turn the screen on
- setPowerState(SCREEN_BRIGHT);
- } else {
- // turn everything on
- setPowerState(ALL_BRIGHT);
- }
-
synchronized (mHandlerThread) {
mInitComplete = true;
mHandlerThread.notifyAll();
@@ -2486,9 +2478,21 @@
enableLightSensor(true);
}
+ // wait until sensors are enabled before turning on screen.
+ // some devices will not activate the light sensor properly on boot
+ // unless we do this.
+ if (mUseSoftwareAutoBrightness) {
+ // turn the screen on
+ setPowerState(SCREEN_BRIGHT);
+ } else {
+ // turn everything on
+ setPowerState(ALL_BRIGHT);
+ }
+
synchronized (mLocks) {
Slog.d(TAG, "system ready!");
mDoneBooting = true;
+
long identity = Binder.clearCallingIdentity();
try {
mBatteryStats.noteScreenBrightness(getPreferredBrightness());
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 186aebe..86b9df6 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -16,12 +16,12 @@
package com.android.server;
-import android.backup.AbsoluteFileBackupHelper;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataInputStream;
-import android.backup.BackupDataOutput;
-import android.backup.BackupHelper;
-import android.backup.BackupHelperAgent;
+import android.app.backup.AbsoluteFileBackupHelper;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
+import android.app.backup.BackupHelperAgent;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index deee7f3..b023958 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
DockObserver dock = null;
+ UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
// Critical services...
@@ -363,6 +364,14 @@
}
try {
+ Slog.i(TAG, "UI Mode Manager Service");
+ // Listen for dock station changes
+ uiMode = new UiModeManagerService(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting UiModeManagerService", e);
+ }
+
+ try {
Slog.i(TAG, "Backup Service");
ServiceManager.addService(Context.BACKUP_SERVICE,
new BackupManagerService(context));
@@ -441,6 +450,7 @@
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
+ final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
@@ -460,6 +470,7 @@
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
+ if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
new file mode 100644
index 0000000..5f23a90
--- /dev/null
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.KeyguardManager;
+import android.app.IUiModeManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.app.UiModeManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.R;
+import com.android.internal.app.DisableCarModeActivity;
+
+class UiModeManagerService extends IUiModeManager.Stub {
+ private static final String TAG = UiModeManager.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
+
+ private static final int MSG_UPDATE_TWILIGHT = 0;
+ private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+
+ private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+ private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
+ private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+
+ private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
+
+ private final Context mContext;
+
+ final Object mLock = new Object();
+
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+ private boolean mCarModeEnabled = false;
+ private boolean mCharging = false;
+ private final boolean mCarModeKeepsScreenOn;
+ private final boolean mDeskModeKeepsScreenOn;
+
+ private boolean mComputedNightMode;
+ private int mCurUiMode = 0;
+
+ private Configuration mConfiguration = new Configuration();
+
+ private boolean mSystemReady;
+
+ private NotificationManager mNotificationManager;
+
+ private AlarmManager mAlarmManager;
+
+ private LocationManager mLocationManager;
+ private Location mLocation;
+ private StatusBarManager mStatusBarManager;
+ private KeyguardManager.KeyguardLock mKeyguardLock;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // The broadcast receiver which receives the result of the ordered broadcast sent when
+ // the dock state changes. The original ordered broadcast is sent with an initial result
+ // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
+ // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
+ private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() != Activity.RESULT_OK) {
+ return;
+ }
+
+ // Launch a dock activity
+ String category;
+ if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+ // Only launch car home when car mode is enabled.
+ category = Intent.CATEGORY_CAR_DOCK;
+ } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
+ category = Intent.CATEGORY_DESK_DOCK;
+ } else {
+ category = null;
+ }
+ if (category != null) {
+ intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Slog.w(TAG, e.getCause());
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+ }
+ }
+ };
+
+ private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ updateDockState(state);
+ }
+ };
+
+ private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+ synchronized (mLock) {
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+ };
+
+ // A LocationListener to initialize the network location provider. The location updates
+ // are handled through the passive location provider.
+ private final LocationListener mEmptyLocationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+
+ private final LocationListener mLocationListener = new LocationListener() {
+
+ public void onLocationChanged(Location location) {
+ final boolean hasMoved = hasMoved(location);
+ final boolean hasBetterAccuracy = mLocation == null
+ || location.getAccuracy() < mLocation.getAccuracy();
+ if (hasMoved || hasBetterAccuracy) {
+ synchronized (mLock) {
+ mLocation = location;
+ if (hasMoved && isDoingNightMode()
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+ }
+ }
+ }
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ /*
+ * The user has moved if the accuracy circles of the two locations
+ * don't overlap.
+ */
+ private boolean hasMoved(Location location) {
+ if (location == null) {
+ return false;
+ }
+ if (mLocation == null) {
+ return true;
+ }
+
+ /* if new location is older than the current one, the devices hasn't
+ * moved.
+ */
+ if (location.getTime() < mLocation.getTime()) {
+ return false;
+ }
+
+ /* Get the distance between the two points */
+ float distance = mLocation.distanceTo(location);
+
+ /* Get the total accuracy radius for both locations */
+ float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
+
+ /* If the distance is greater than the combined accuracy of the two
+ * points then they can't overlap and hence the user has moved.
+ */
+ return distance >= totalAccuracy;
+ }
+ };
+
+ public UiModeManagerService(Context context) {
+ mContext = context;
+
+ ServiceManager.addService(Context.UI_MODE_SERVICE, this);
+
+ mAlarmManager =
+ (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mLocationManager =
+ (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
+ mContext.registerReceiver(mTwilightUpdateReceiver,
+ new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
+ mContext.registerReceiver(mDockModeReceiver,
+ new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ mContext.registerReceiver(mBatteryReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+ PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+
+ mConfiguration.setToDefaults();
+
+ mCarModeKeepsScreenOn = (context.getResources().getInteger(
+ com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
+ mDeskModeKeepsScreenOn = (context.getResources().getInteger(
+ com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
+ }
+
+ public void disableCarMode() {
+ synchronized (mLock) {
+ setCarModeLocked(false);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+
+ public void enableCarMode() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ENABLE_CAR_MODE,
+ "Need ENABLE_CAR_MODE permission");
+ synchronized (mLock) {
+ setCarModeLocked(true);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+
+ public int getCurrentModeType() {
+ synchronized (mLock) {
+ return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
+ }
+ }
+
+ public void setNightMode(int mode) throws RemoteException {
+ synchronized (mLock) {
+ switch (mode) {
+ case UiModeManager.MODE_NIGHT_NO:
+ case UiModeManager.MODE_NIGHT_YES:
+ case UiModeManager.MODE_NIGHT_AUTO:
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown mode: " + mode);
+ }
+ if (!isDoingNightMode()) {
+ return;
+ }
+
+ if (mNightMode != mode) {
+ mNightMode = mode;
+ updateLocked();
+ }
+ }
+ }
+
+ public int getNightMode() throws RemoteException {
+ return mNightMode;
+ }
+
+ void systemReady() {
+ synchronized (mLock) {
+ mSystemReady = true;
+ mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+ updateLocked();
+ mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
+ }
+ }
+
+ boolean isDoingNightMode() {
+ return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ }
+
+ void setCarModeLocked(boolean enabled) {
+ if (mCarModeEnabled != enabled) {
+ mCarModeEnabled = enabled;
+
+ // Disable keyguard when in car mode
+ if (mKeyguardLock == null) {
+ KeyguardManager km =
+ (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (km != null) {
+ mKeyguardLock = km.newKeyguardLock(TAG);
+ }
+ }
+ if (mKeyguardLock != null) {
+ long ident = Binder.clearCallingIdentity();
+ if (enabled) {
+ mKeyguardLock.disableKeyguard();
+ } else {
+ mKeyguardLock.reenableKeyguard();
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ void updateDockState(int newState) {
+ synchronized (mLock) {
+ if (newState != mDockState) {
+ mDockState = newState;
+ setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
+ if (mSystemReady) {
+ updateLocked();
+ }
+ }
+ }
+ }
+
+ final void updateLocked() {
+ long ident = Binder.clearCallingIdentity();
+
+ try {
+ int uiMode = 0;
+ if (mCarModeEnabled) {
+ uiMode = Configuration.UI_MODE_TYPE_CAR;
+ } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ uiMode = Configuration.UI_MODE_TYPE_DESK;
+ }
+ if (uiMode != 0) {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
+ : Configuration.UI_MODE_NIGHT_NO;
+ } else {
+ uiMode |= mNightMode << 4;
+ }
+ } else {
+ // Disabling the car mode clears the night mode.
+ uiMode = Configuration.UI_MODE_TYPE_NORMAL |
+ Configuration.UI_MODE_NIGHT_NO;
+ }
+
+ if (uiMode != mCurUiMode) {
+ mCurUiMode = uiMode;
+
+ try {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ mConfiguration.uiMode = uiMode;
+ am.updateConfiguration(mConfiguration);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure communicating with activity manager", e);
+ }
+ }
+
+ String action = null;
+ String oldAction = null;
+ if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+ oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
+ } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
+ oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
+ }
+
+ if (mCarModeEnabled) {
+ if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
+ adjustStatusBarCarModeLocked();
+
+ if (oldAction != null) {
+ mContext.sendBroadcast(new Intent(oldAction));
+ }
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
+ action = UiModeManager.ACTION_ENTER_CAR_MODE;
+ }
+ } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) {
+ if (oldAction != null) {
+ mContext.sendBroadcast(new Intent(oldAction));
+ }
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK;
+ action = UiModeManager.ACTION_ENTER_DESK_MODE;
+ }
+ } else {
+ if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+ adjustStatusBarCarModeLocked();
+ }
+
+ mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ action = oldAction;
+ }
+
+ if (action != null) {
+ // Send the ordered broadcast; the result receiver will receive after all
+ // broadcasts have been sent. If any broadcast receiver changes the result
+ // code from the initial value of RESULT_OK, then the result receiver will
+ // not launch the corresponding dock application. This gives apps a chance
+ // to override the behavior and stay in their app even when the device is
+ // placed into a dock.
+ mContext.sendOrderedBroadcast(new Intent(action), null,
+ mResultReceiver, null, Activity.RESULT_OK, null, null);
+ }
+
+ // keep screen on when charging and in car mode
+ boolean keepScreenOn = mCharging &&
+ ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
+ (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
+ if (keepScreenOn != mWakeLock.isHeld()) {
+ if (keepScreenOn) {
+ mWakeLock.acquire();
+ } else {
+ mWakeLock.release();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void adjustStatusBarCarModeLocked() {
+ if (mStatusBarManager == null) {
+ mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+ }
+
+ // Fear not: StatusBarService manages a list of requests to disable
+ // features of the status bar; these are ORed together to form the
+ // active disabled list. So if (for example) the device is locked and
+ // the status bar should be totally disabled, the calls below will
+ // have no effect until the device is unlocked.
+ if (mStatusBarManager != null) {
+ mStatusBarManager.disable(mCarModeEnabled
+ ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
+ : StatusBarManager.DISABLE_NONE);
+ }
+
+ if (mNotificationManager == null) {
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ if (mNotificationManager != null) {
+ if (mCarModeEnabled) {
+ Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
+
+ Notification n = new Notification();
+ n.icon = R.drawable.stat_notify_car_mode;
+ n.defaults = Notification.DEFAULT_LIGHTS;
+ n.flags = Notification.FLAG_ONGOING_EVENT;
+ n.when = 0;
+ n.setLatestEventInfo(
+ mContext,
+ mContext.getString(R.string.car_mode_disable_notification_title),
+ mContext.getString(R.string.car_mode_disable_notification_message),
+ PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
+ mNotificationManager.notify(0, n);
+ } else {
+ mNotificationManager.cancel(0);
+ }
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+
+ boolean mPassiveListenerEnabled;
+ boolean mNetworkListenerEnabled;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TWILIGHT:
+ synchronized (mLock) {
+ if (isDoingNightMode() && mLocation != null
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ updateLocked();
+ }
+ }
+ break;
+ case MSG_ENABLE_LOCATION_UPDATES:
+ // enable network provider to receive at least location updates for a given
+ // distance.
+ boolean networkLocationEnabled;
+ try {
+ networkLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if network location provider
+ // does not exist or is not yet installed.
+ networkLocationEnabled = false;
+ }
+ if (!mNetworkListenerEnabled && networkLocationEnabled) {
+ mNetworkListenerEnabled = true;
+ mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+ LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
+
+ if (mLocation == null) {
+ retrieveLocation();
+ }
+ synchronized (mLock) {
+ if (isDoingNightMode() && mLocation != null
+ && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateTwilightLocked();
+ updateLocked();
+ }
+ }
+ }
+ // enable passive provider to receive updates from location fixes (gps
+ // and network).
+ boolean passiveLocationEnabled;
+ try {
+ passiveLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if passive location provider
+ // does not exist or is not yet installed.
+ passiveLocationEnabled = false;
+ }
+ if (!mPassiveListenerEnabled && passiveLocationEnabled) {
+ mPassiveListenerEnabled = true;
+ mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
+ }
+ if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
+ long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
+ interval *= 1.5;
+ if (interval == 0) {
+ interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
+ } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
+ interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
+ Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
+ newMsg.setData(bundle);
+ mHandler.sendMessageDelayed(newMsg, interval);
+ }
+ break;
+ }
+ }
+
+ private void retrieveLocation() {
+ Location location;
+ Criteria criteria = new Criteria();
+ criteria.setSpeedRequired(false);
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingRequired(false);
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
+ final String bestProvider = mLocationManager.getBestProvider(criteria, true);
+ location = mLocationManager.getLastKnownLocation(bestProvider);
+ // In the case there is no location available (e.g. GPS fix or network location
+ // is not available yet), the longitude of the location is estimated using the timezone,
+ // latitude and accuracy are set to get a good average.
+ if (location == null) {
+ Time currentTime = new Time();
+ currentTime.set(System.currentTimeMillis());
+ double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+ (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
+ location = new Location("fake");
+ location.setLongitude(lngOffset);
+ location.setLatitude(0);
+ location.setAccuracy(417000.0f);
+ location.setTime(System.currentTimeMillis());
+ }
+ synchronized (mLock) {
+ mLocation = location;
+ }
+ }
+ };
+
+ void updateTwilightLocked() {
+ if (mLocation == null) {
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ boolean nightMode;
+ // calculate current twilight
+ TwilightCalculator tw = new TwilightCalculator();
+ tw.calculateTwilight(currentTime,
+ mLocation.getLatitude(), mLocation.getLongitude());
+ if (tw.mState == TwilightCalculator.DAY) {
+ nightMode = false;
+ } else {
+ nightMode = true;
+ }
+
+ // schedule next update
+ long nextUpdate = 0;
+ if (tw.mSunrise == -1 || tw.mSunset == -1) {
+ // In the case the day or night never ends the update is scheduled 12 hours later.
+ nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
+ } else {
+ final int mLastTwilightState = tw.mState;
+ // add some extra time to be on the save side.
+ nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+ if (currentTime > tw.mSunset) {
+ // next update should be on the following day
+ tw.calculateTwilight(currentTime
+ + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
+ mLocation.getLongitude());
+ }
+
+ if (mLastTwilightState == TwilightCalculator.NIGHT) {
+ nextUpdate += tw.mSunrise;
+ } else {
+ nextUpdate += tw.mSunset;
+ }
+ }
+
+ Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
+ mAlarmManager.cancel(pendingIntent);
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+
+ mComputedNightMode = nightMode;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ pw.println("Permission Denial: can't dump uimode service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("Current UI Mode Service state:");
+ pw.print(" mDockState="); pw.print(mDockState);
+ pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+ pw.print(" mNightMode="); pw.print(mNightMode);
+ pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
+ pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
+ pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
+ pw.print(" mSystemReady="); pw.println(mSystemReady);
+ if (mLocation != null) {
+ pw.print(" mLocation="); pw.println(mLocation);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 7a03ebd..1b51741 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -23,7 +23,7 @@
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
import android.app.WallpaperInfo;
-import android.backup.BackupManager;
+import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index ee69715..f97f50a 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -34,8 +34,10 @@
import android.provider.Settings;
import android.util.Config;
import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -804,19 +806,14 @@
// to timeout on is asleep as well and won't have a chance to run. Causing a false
// positive on when to kill things.
long start = SystemClock.uptimeMillis();
- do {
+ while (timeout > 0 && !mForceKillSystem) {
try {
- wait(timeout);
+ wait(timeout); // notifyAll() is called when mForceKillSystem is set
} catch (InterruptedException e) {
- if (SystemProperties.getBoolean("ro.secure", false)) {
- // If this is a secure build, just log the error.
- Slog.e("WatchDog", "Woof! Woof! Interrupter!");
- } else {
- throw new AssertionError("Someone interrupted the watchdog");
- }
+ Log.wtf(TAG, e);
}
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
- } while (timeout > 0 && !mForceKillSystem);
+ }
if (mCompleted && !mForceKillSystem) {
// The monitors have returned.
@@ -825,22 +822,24 @@
}
// If we got here, that means that the system is most likely hung.
- // First send a SIGQUIT so that we can see where it was hung. Then
- // kill this process so that the system will restart.
+ // First collect stack traces from all threads of the system process.
+ // Then kill this process so that the system will restart.
+
String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null";
EventLog.writeEvent(EventLogTags.WATCHDOG, name);
- Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
- // Wait a bit longer before killing so we can make sure that the stacks are captured.
- try {
- Thread.sleep(10*1000);
- } catch (InterruptedException e) {
- }
+ ArrayList pids = new ArrayList();
+ pids.add(Process.myPid());
+ File stack = ActivityManagerService.dumpStackTraces(pids);
+ mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null);
// Only kill the process if the debugger is not attached.
if (!Debug.isDebuggerConnected()) {
- Slog.i(TAG, "Watchdog is killing the system process");
+ Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
Process.killProcess(Process.myPid());
+ System.exit(10);
+ } else {
+ Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
}
}
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 1455973..8fa862d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -22,6 +22,12 @@
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothA2dp;
@@ -40,6 +46,8 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
import android.net.NetworkUtils;
@@ -47,6 +55,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -67,9 +76,10 @@
import java.util.regex.Pattern;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.UnknownHostException;
import com.android.internal.app.IBatteryStats;
-import android.backup.IBackupManager;
+import android.app.backup.IBackupManager;
import com.android.server.am.BatteryStatsService;
/**
@@ -87,6 +97,7 @@
private Context mContext;
private int mWifiState;
+ private int mWifiApState;
private AlarmManager mAlarmManager;
private PendingIntent mIdleIntent;
@@ -112,6 +123,10 @@
private final IBatteryStats mBatteryStats;
+ private INetworkManagementService nwService;
+ ConnectivityManager mCm;
+ private String[] mWifiRegexs;
+
/**
* See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
* Settings.Secure value is not present. This timeout value is chosen as
@@ -145,6 +160,9 @@
private static final int MESSAGE_START_WIFI = 3;
private static final int MESSAGE_RELEASE_WAKELOCK = 4;
private static final int MESSAGE_UPDATE_STATE = 5;
+ private static final int MESSAGE_START_ACCESS_POINT = 6;
+ private static final int MESSAGE_STOP_ACCESS_POINT = 7;
+
private final WifiHandler mWifiHandler;
@@ -180,6 +198,9 @@
mWifiStateTracker.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ nwService = INetworkManagementService.Stub.asInterface(b);
+
mScanResultCache = new LinkedHashMap<String, ScanResult>(
SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
/*
@@ -196,7 +217,9 @@
mWifiHandler = new WifiHandler(wifiThread.getLooper());
mWifiState = WIFI_STATE_DISABLED;
+ mWifiApState = WIFI_AP_STATE_DISABLED;
boolean wifiEnabled = getPersistedWifiEnabled();
+ boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled();
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
@@ -232,7 +255,70 @@
},
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ ArrayList<String> available = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ ArrayList<String> active = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ updateTetherState(available, active);
+
+ }
+ },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
+ setWifiApEnabledBlocking(wifiAPEnabled, true, Process.myUid(), null);
+ }
+
+ private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
+
+ boolean wifiTethered = false;
+ boolean wifiAvailable = false;
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWifiRegexs = mCm.getTetherableWifiRegexs();
+
+ for (String intf : available) {
+ for (String regex : mWifiRegexs) {
+ if (intf.matches(regex)) {
+
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = service.getInterfaceConfig(intf);
+ if (ifcg != null) {
+ /* IP/netmask: 169.254.2.1/255.255.255.0 */
+ ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
+ ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
+ ifcg.interfaceFlags = "up";
+
+ service.setInterfaceConfig(intf, ifcg);
+ }
+ } catch (Exception e) {
+ /**
+ * TODO: Add broadcast to indicate tether failed
+ */
+ Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+ return;
+ }
+
+ /**
+ * TODO: Add broadcast to indicate tether failed
+ */
+ if(mCm.tether(intf) == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Slog.d(TAG, "Tethered "+intf);
+ } else {
+ Slog.e(TAG, "Error tethering "+intf);
+ }
+ break;
+ }
+ }
+ }
}
private boolean getPersistedWifiEnabled() {
@@ -337,12 +423,16 @@
* Avoid doing a disable when the current Wifi state is UNKNOWN
* TODO: Handle driver load fail and supplicant lost as seperate states
*/
- if (mWifiState == WIFI_STATE_UNKNOWN && !enable) {
+ if ((mWifiState == WIFI_STATE_UNKNOWN) && !enable) {
return false;
}
setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
+ if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
+ setWifiApEnabledBlocking(false, true, Process.myUid(), null);
+ }
+
if (enable) {
synchronized (mWifiStateTracker) {
if (!WifiNative.loadDriver()) {
@@ -490,6 +580,154 @@
}
}
+ private boolean getPersistedWifiApEnabled() {
+ final ContentResolver cr = mContext.getContentResolver();
+ try {
+ return Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_ON) == 1;
+ } catch (Settings.SettingNotFoundException e) {
+ Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, 0);
+ return false;
+ }
+ }
+
+ private void persistWifiApEnabled(boolean enabled) {
+ final ContentResolver cr = mContext.getContentResolver();
+ Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, enabled ? 1 : 0);
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)}
+ * @param wifiConfig SSID, security and channel details as
+ * part of WifiConfiguration
+ * @return {@code true} if the start operation was
+ * started or is already in the queue.
+ */
+ public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
+ enforceChangePermission();
+ if (mWifiHandler == null) return false;
+
+ synchronized (mWifiHandler) {
+
+ long ident = Binder.clearCallingIdentity();
+ sWakeLock.acquire();
+ Binder.restoreCallingIdentity(ident);
+
+ mLastEnableUid = Binder.getCallingUid();
+
+ sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid());
+ }
+
+ return true;
+ }
+
+ /**
+ * Enables/disables Wi-Fi AP synchronously. The driver is loaded
+ * and soft access point configured as a single operation.
+ * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
+ * @param persist {@code true} if the setting should be persisted.
+ * @param uid The UID of the process making the request.
+ * @param config The WifiConfiguration for AP
+ * @return {@code true} if the operation succeeds (or if the existing state
+ * is the same as the requested state)
+ */
+ /**
+ * TODO: persist needs to go away in WifiService
+ * This will affect all persist related functions
+ * for Access Point
+ */
+ private boolean setWifiApEnabledBlocking(boolean enable,
+ boolean persist, int uid, WifiConfiguration wifiConfig) {
+ final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
+
+ if (mWifiApState == eventualWifiApState) {
+ return true;
+ }
+
+ setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : WIFI_AP_STATE_DISABLING, uid);
+
+ if (enable && (mWifiState == WIFI_STATE_ENABLED)) {
+ setWifiEnabledBlocking(false, true, Process.myUid());
+ }
+
+ if (enable) {
+ synchronized (mWifiStateTracker) {
+ if (!WifiNative.loadDriver()) {
+ Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+ return false;
+ }
+ }
+
+ try {
+ nwService.startAccessPoint();
+ } catch(Exception e) {
+ Slog.e(TAG, "Exception in startAccessPoint()");
+ }
+
+ } else {
+
+ try {
+ nwService.stopAccessPoint();
+ } catch(Exception e) {
+ Slog.e(TAG, "Exception in stopAccessPoint()");
+ }
+
+ synchronized (mWifiStateTracker) {
+ if (!WifiNative.unloadDriver()) {
+ Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+ return false;
+ }
+ }
+ }
+
+ // Success!
+ if (persist) {
+ persistWifiApEnabled(enable);
+ }
+ setWifiApEnabledState(eventualWifiApState, uid);
+ return true;
+ }
+
+ /**
+ * see {@link WifiManager#getWifiApState()}
+ * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ */
+ public int getWifiApEnabledState() {
+ enforceAccessPermission();
+ return mWifiApState;
+ }
+
+ private void setWifiApEnabledState(int wifiAPState, int uid) {
+ final int previousWifiApState = mWifiApState;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (wifiAPState == WIFI_AP_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(uid);
+ } else if (wifiAPState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(uid);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ // Update state
+ mWifiApState = wifiAPState;
+
+ // Broadcast
+ final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
/**
* see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
* @return the list of configured networks
@@ -1472,6 +1710,12 @@
Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
}
+ private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
+ Message.obtain(mWifiHandler,
+ (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
+ 0, uid, wifiConfig).sendToTarget();
+ }
+
private void updateWifiState() {
// send a message so it's all serialized
Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
@@ -1606,6 +1850,21 @@
}
}
break;
+
+ case MESSAGE_START_ACCESS_POINT:
+ setWifiApEnabledBlocking(true,
+ msg.arg1 == 1,
+ msg.arg2,
+ (WifiConfiguration) msg.obj);
+ break;
+
+ case MESSAGE_STOP_ACCESS_POINT:
+ setWifiApEnabledBlocking(false,
+ msg.arg1 == 1,
+ msg.arg2,
+ (WifiConfiguration) msg.obj);
+ sWakeLock.release();
+ break;
}
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 67eba3d..f5d3e8e 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -9516,6 +9516,9 @@
if (mPolicy.doesForceHide(w, attrs)) {
if (!wasAnimating && animating) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG,
+ "Animation done that could impact force hide: "
+ + w);
wallpaperForceHidingChanged = true;
mFocusMayChange = true;
} else if (w.isReadyForDisplay() && w.mAnimation == null) {
@@ -9525,19 +9528,31 @@
boolean changed;
if (forceHiding) {
changed = w.hideLw(false, false);
+ if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
+ "Now policy hidden: " + w);
} else {
changed = w.showLw(false, false);
- if (changed && wallpaperForceHidingChanged
- && w.isReadyForDisplay()) {
- // Assume we will need to animate. If
- // we don't (because the wallpaper will
- // stay with the lock screen), then we will
- // clean up later.
- Animation a = mPolicy.createForceHideEnterAnimation();
- if (a != null) {
- w.setAnimation(a);
+ if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
+ "Now policy shown: " + w);
+ if (changed) {
+ if (wallpaperForceHidingChanged
+ && w.isReadyForDisplay()) {
+ // Assume we will need to animate. If
+ // we don't (because the wallpaper will
+ // stay with the lock screen), then we will
+ // clean up later.
+ Animation a = mPolicy.createForceHideEnterAnimation();
+ if (a != null) {
+ w.setAnimation(a);
+ }
}
- mFocusMayChange = true;
+ if (mCurrentFocus == null ||
+ mCurrentFocus.mLayer < w.mLayer) {
+ // We are showing on to of the current
+ // focus, so re-evaluate focus to make
+ // sure it is correct.
+ mFocusMayChange = true;
+ }
}
}
if (changed && (attrs.flags
@@ -10851,6 +10866,10 @@
return;
}
+ pw.println("Input State:");
+ mQueue.dump(pw, " ");
+ pw.println(" ");
+
synchronized(mWindowMap) {
pw.println("Current Window Manager state:");
for (int i=mWindows.size()-1; i>=0; i--) {
@@ -11014,7 +11033,7 @@
if (mDimAnimator != null) {
mDimAnimator.printTo(pw);
} else {
- pw.print( " no DimAnimator ");
+ pw.println( " no DimAnimator ");
}
pw.print(" mInputMethodAnimLayerAdjustment=");
pw.print(mInputMethodAnimLayerAdjustment);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2c82d9c..b53100f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -46,7 +46,7 @@
import android.app.PendingIntent;
import android.app.ResultInfo;
import android.app.Service;
-import android.backup.IBackupManager;
+import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -1863,6 +1863,8 @@
+ " pid=" + (app != null ? app.pid : -1));
if (app != null && app.pid > 0) {
if (!knownToBeDead || app.thread == null) {
+ // We already have the app running, or are waiting for it to
+ // come up (we have a pid but not yet its thread), so keep it.
return app;
} else {
// An application record is attached to a previous process,
@@ -4660,7 +4662,7 @@
* @param pids of dalvik VM processes to dump stack traces for
* @return file containing stack traces, or null if no dump file is configured
*/
- private static File dumpStackTraces(ArrayList<Integer> pids) {
+ public static File dumpStackTraces(ArrayList<Integer> pids) {
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
return null;
@@ -8956,10 +8958,13 @@
* @param logFile to include in the report, null if none
* @param crashInfo giving an application stack trace, null if absent
*/
- private void addErrorToDropBox(String eventType,
+ public void addErrorToDropBox(String eventType,
ProcessRecord process, HistoryRecord activity, HistoryRecord parent,
String subject, String report, File logFile,
ApplicationErrorReport.CrashInfo crashInfo) {
+ // NOTE -- this must never acquire the ActivityManagerService lock,
+ // otherwise the watchdog may be prevented from resetting the system.
+
String dropboxTag;
if (process == null || process.pid == MY_PID) {
dropboxTag = "system_server_" + eventType;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 5f37a42..78329db 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -65,7 +65,6 @@
private Context mContext;
private final String TAG = "Tethering";
- private boolean mPlaySounds = false;
private boolean mBooted = false;
//used to remember if we got connected before boot finished
private boolean mDeferedUsbConnection = false;
@@ -77,8 +76,6 @@
private HashMap<String, TetherInterfaceSM> mIfaces;
- private ArrayList<String> mActiveTtys;
-
private BroadcastReceiver mStateReceiver;
private static final String USB_NEAR_IFACE_ADDR = "169.254.2.1";
@@ -111,7 +108,6 @@
}
mIfaces = new HashMap<String, TetherInterfaceSM>();
- mActiveTtys = new ArrayList<String>();
mTetherMasterSM = new TetherMasterSM("TetherMaster");
mTetherMasterSM.start();
@@ -236,7 +232,7 @@
}
}
- public boolean tether(String iface) {
+ public int tether(String iface) {
Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
synchronized (mIfaces) {
@@ -244,21 +240,17 @@
}
if (sm == null) {
Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
- return false;
+ return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
}
- if (sm.isErrored()) {
- Log.e(TAG, "Tried to Tether to an errored iface :" + iface + ", ignoring");
- return false;
- }
- if (!sm.isAvailable()) {
+ if (!sm.isAvailable() && !sm.isErrored()) {
Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
- return false;
+ return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED));
- return true;
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
- public boolean untether(String iface) {
+ public int untether(String iface) {
Log.d(TAG, "Untethering " + iface);
TetherInterfaceSM sm = null;
synchronized (mIfaces) {
@@ -266,14 +258,26 @@
}
if (sm == null) {
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
- return false;
+ return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
}
if (sm.isErrored()) {
Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
- return false;
+ return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED));
- return true;
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ }
+
+ public int getLastTetherError(String iface) {
+ TetherInterfaceSM sm = null;
+ synchronized (mIfaces) {
+ sm = mIfaces.get(iface);
+ }
+ if (sm == null) {
+ Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface + ", ignoring");
+ return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ }
+ return sm.getLastError();
}
private void sendTetherStateChangedBroadcast() {
@@ -314,120 +318,6 @@
mContext.sendStickyBroadcast(broadcast);
Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
activeList.size() + ", " + erroredList.size());
- // check if we need to send a USB notification
- // Check if the user wants to be bothered
- boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.TETHER_NOTIFY, 0) == 1);
- for (Object o : activeList) {
- String s = (String)o;
- for (Object regexObject : mTetherableUsbRegexs) {
- if (s.matches((String)regexObject)) {
- showTetheredNotification();
- return;
- }
- }
- }
- if (tellUser) {
- for (Object o : availableList) {
- String s = (String)o;
- for (String match : mTetherableUsbRegexs) {
- if (s.matches(match)) {
- showTetherAvailableNotification();
- return;
- }
- }
- }
- }
- clearNotification();
- }
-
- private void showTetherAvailableNotification() {
- NotificationManager notificationManager = (NotificationManager)mContext.
- getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null) {
- return;
- }
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
-
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(com.android.internal.R.string.
- tether_available_notification_title);
- CharSequence message = r.getText(com.android.internal.R.string.
- tether_available_notification_message);
-
- if(mTetheringNotification == null) {
- mTetheringNotification = new Notification();
- mTetheringNotification.when = 0;
- }
- mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
-
- boolean playSounds = false;
- //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
- if (playSounds) {
- mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
- } else {
- mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
- }
-
- mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mTetheringNotification.tickerText = title;
- mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
-
- notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
-
- }
-
- private void showTetheredNotification() {
- NotificationManager notificationManager = (NotificationManager)mContext.
- getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null) {
- return;
- }
-
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
-
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(com.android.internal.R.string.
- tether_stop_notification_title);
- CharSequence message = r.getText(com.android.internal.R.string.
- tether_stop_notification_message);
-
- if(mTetheringNotification == null) {
- mTetheringNotification = new Notification();
- mTetheringNotification.when = 0;
- }
- mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
-
- boolean playSounds = false;
- //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
- if (playSounds) {
- mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
- } else {
- mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
- }
-
- mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mTetheringNotification.tickerText = title;
- mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
-
- notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
- }
-
- private void clearNotification() {
- NotificationManager notificationManager = (NotificationManager)mContext.
- getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager != null && mTetheringNotification != null) {
- notificationManager.cancel(mTetheringNotification.icon);
- mTetheringNotification = null;
- }
}
private class StateReceiver extends BroadcastReceiver {
@@ -542,10 +432,9 @@
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
- // TODO - clean this up - maybe a better regex?
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
}
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
service.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
@@ -611,6 +500,24 @@
return retVal;
}
+ public String[] getErroredIfaces() {
+ ArrayList<String> list = new ArrayList<String>();
+ synchronized (mIfaces) {
+ Set keys = mIfaces.keySet();
+ for (Object key : keys) {
+ TetherInterfaceSM sm = mIfaces.get(key);
+ if (sm.isErrored()) {
+ list.add((String)key);
+ }
+ }
+ }
+ String[] retVal = new String[list.size()];
+ for (int i= 0; i< list.size(); i++) {
+ retVal[i] = list.get(i);
+ }
+ return retVal;
+ }
+
class TetherInterfaceSM extends HierarchicalStateMachine {
// notification from the master SM that it's in tether mode
@@ -637,8 +544,8 @@
static final int CMD_STOP_TETHERING_ERROR = 14;
// notification from the master SM that it had trouble setting the DNS forwarders
static final int CMD_SET_DNS_FORWARDERS_ERROR = 15;
- // a mechanism to transition self to error state from an enter function
- static final int CMD_TRANSITION_TO_ERROR = 16;
+ // a mechanism to transition self to another state from an enter function
+ static final int CMD_TRANSITION_TO_STATE = 16;
private HierarchicalState mDefaultState;
@@ -646,18 +553,11 @@
private HierarchicalState mStartingState;
private HierarchicalState mTetheredState;
- private HierarchicalState mMasterTetherErrorState;
- private HierarchicalState mTetherInterfaceErrorState;
- private HierarchicalState mUntetherInterfaceErrorState;
- private HierarchicalState mEnableNatErrorState;
- private HierarchicalState mDisableNatErrorState;
- private HierarchicalState mUsbConfigurationErrorState;
-
private HierarchicalState mUnavailableState;
private boolean mAvailable;
- private boolean mErrored;
private boolean mTethered;
+ int mLastError;
String mIfaceName;
boolean mUsb;
@@ -666,6 +566,7 @@
super(name);
mIfaceName = name;
mUsb = usb;
+ setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
mInitialState = new InitialState();
addState(mInitialState);
@@ -673,18 +574,6 @@
addState(mStartingState);
mTetheredState = new TetheredState();
addState(mTetheredState);
- mMasterTetherErrorState = new MasterTetherErrorState();
- addState(mMasterTetherErrorState);
- mTetherInterfaceErrorState = new TetherInterfaceErrorState();
- addState(mTetherInterfaceErrorState);
- mUntetherInterfaceErrorState = new UntetherInterfaceErrorState();
- addState(mUntetherInterfaceErrorState);
- mEnableNatErrorState = new EnableNatErrorState();
- addState(mEnableNatErrorState);
- mDisableNatErrorState = new DisableNatErrorState();
- addState(mDisableNatErrorState);
- mUsbConfigurationErrorState = new UsbConfigurationErrorState();
- addState(mUsbConfigurationErrorState);
mUnavailableState = new UnavailableState();
addState(mUnavailableState);
@@ -698,19 +587,29 @@
if (current == mInitialState) res += "InitialState";
if (current == mStartingState) res += "StartingState";
if (current == mTetheredState) res += "TetheredState";
- if (current == mMasterTetherErrorState) res += "MasterTetherErrorState";
- if (current == mTetherInterfaceErrorState) res += "TetherInterfaceErrorState";
- if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState";
- if (current == mEnableNatErrorState) res += "EnableNatErrorState";
- if (current == mDisableNatErrorState) res += "DisableNatErrorState";
- if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState";
if (current == mUnavailableState) res += "UnavailableState";
if (mAvailable) res += " - Available";
if (mTethered) res += " - Tethered";
- if (mErrored) res += " - ERRORED";
+ res += " - lastError =" + mLastError;
return res;
}
+ public synchronized int getLastError() {
+ return mLastError;
+ }
+
+ private synchronized void setLastError(int error) {
+ mLastError = error;
+
+ if (isErrored()) {
+ if (mUsb) {
+ // note everything's been unwound by this point so nothing to do on
+ // further error..
+ Tethering.this.configureUsbIface(false);
+ }
+ }
+ }
+
// synchronized between this getter and the following setter
public synchronized boolean isAvailable() {
return mAvailable;
@@ -731,18 +630,7 @@
// synchronized between this getter and the following setter
public synchronized boolean isErrored() {
- return mErrored;
- }
-
- private void setErrored(boolean errored) {
- synchronized (this) {
- mErrored = errored;
- }
- if (errored && mUsb) {
- // note everything's been unwound by this point so nothing to do on
- // further error..
- Tethering.this.configureUsbIface(false);
- }
+ return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
}
class InitialState extends HierarchicalState {
@@ -750,7 +638,6 @@
public void enter() {
setAvailable(true);
setTethered(false);
- setErrored(false);
sendTetherStateChangedBroadcast();
}
@@ -760,6 +647,7 @@
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
+ setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_REQUESTED);
m.obj = TetherInterfaceSM.this;
@@ -788,8 +676,10 @@
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
- m = obtainMessage(CMD_TRANSITION_TO_ERROR);
- m.obj = mUsbConfigurationErrorState;
+ setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
+
+ m = obtainMessage(CMD_TRANSITION_TO_STATE);
+ m.obj = mInitialState;
sendMessageAtFrontOfQueue(m);
return;
}
@@ -809,7 +699,8 @@
mTetherMasterSM.sendMessage(m);
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
- transitionTo(mUsbConfigurationErrorState);
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
break;
}
}
@@ -824,7 +715,8 @@
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
- transitionTo(mMasterTetherErrorState);
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
case CMD_INTERFACE_DOWN:
m = mTetherMasterSM.obtainMessage(
@@ -833,7 +725,7 @@
mTetherMasterSM.sendMessage(m);
transitionTo(mUnavailableState);
break;
- case CMD_TRANSITION_TO_ERROR:
+ case CMD_TRANSITION_TO_STATE:
HierarchicalState s = (HierarchicalState)(message.obj);
transitionTo(s);
break;
@@ -853,16 +745,24 @@
try {
service.tetherInterface(mIfaceName);
} catch (Exception e) {
- Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
- m.obj = mTetherInterfaceErrorState;
+ setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
+
+ Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+ m.obj = mInitialState;
sendMessageAtFrontOfQueue(m);
return;
}
try {
service.enableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
- Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
- m.obj = mEnableNatErrorState;
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
+
+ setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
+
+ Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+ m.obj = mInitialState;
sendMessageAtFrontOfQueue(m);
return;
}
@@ -890,13 +790,19 @@
try {
service.disableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
- transitionTo(mDisableNatErrorState);
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
+
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
break;
}
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {
- transitionTo(mUntetherInterfaceErrorState);
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
Message m = mTetherMasterSM.obtainMessage(
@@ -906,13 +812,11 @@
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
- transitionTo(mUsbConfigurationErrorState);
- } else {
- transitionTo(mInitialState);
+ setLastError(
+ ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
- } else {
- transitionTo(mInitialState);
}
+ transitionTo(mInitialState);
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
}
@@ -932,30 +836,36 @@
try {
service.disableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
- transitionTo(mDisableNatErrorState);
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
+
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
break;
}
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {
- transitionTo(mUntetherInterfaceErrorState);
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
if (error) {
- transitionTo(mMasterTetherErrorState);
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
}
Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
sendTetherStateChangedBroadcast();
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
- transitionTo(mUsbConfigurationErrorState);
- break;
+ setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
}
transitionTo(mInitialState);
break;
- case CMD_TRANSITION_TO_ERROR:
+ case CMD_TRANSITION_TO_STATE:
HierarchicalState s = (HierarchicalState)(message.obj);
transitionTo(s);
break;
@@ -971,7 +881,7 @@
@Override
public void enter() {
setAvailable(false);
- setErrored(false);
+ setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
setTethered(false);
sendTetherStateChangedBroadcast();
}
@@ -990,95 +900,11 @@
}
}
-
- class ErrorState extends HierarchicalState {
- int mErrorNotification;
- @Override
- public boolean processMessage(Message message) {
- boolean retValue = true;
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- sendTetherStateChangedBroadcast();
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
+ void setLastErrorAndTransitionToInitialState(int error) {
+ setLastError(error);
+ transitionTo(mInitialState);
}
- class MasterTetherErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in Master Tether state " + mIfaceName);
- setAvailable(false);
- setErrored(true);
- sendTetherStateChangedBroadcast();
- }
- }
-
- class TetherInterfaceErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to tether " + mIfaceName);
- setAvailable(false);
- setErrored(true);
- sendTetherStateChangedBroadcast();
- }
- }
-
- class UntetherInterfaceErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to untether " + mIfaceName);
- setAvailable(false);
- setErrored(true);
- sendTetherStateChangedBroadcast();
- }
- }
-
- class EnableNatErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to enable NAT " + mIfaceName);
- setAvailable(false);
- setErrored(true);
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- try {
- service.untetherInterface(mIfaceName);
- } catch (Exception e) {}
- sendTetherStateChangedBroadcast();
- }
- }
-
-
- class DisableNatErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to disable NAT " + mIfaceName);
- setAvailable(false);
- setErrored(true);
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- try {
- service.untetherInterface(mIfaceName);
- } catch (Exception e) {}
- sendTetherStateChangedBroadcast();
- }
- }
-
- class UsbConfigurationErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to configure USB " + mIfaceName);
- setAvailable(false);
- setErrored(true);
- }
- }
}
class TetherMasterSM extends HierarchicalStateMachine {
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 6797f78..32e7176 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1311,7 +1311,12 @@
number = extractNetworkPortionAlt(number);
// retrieve the list of emergency numbers
- String numbers = SystemProperties.get("ro.ril.ecclist");
+ // check read-write ecclist property first
+ String numbers = SystemProperties.get("ril.ecclist");
+ if (TextUtils.isEmpty(numbers)) {
+ // then read-only ecclist property since old RIL only uses this
+ numbers = SystemProperties.get("ro.ril.ecclist");
+ }
if (!TextUtils.isEmpty(numbers)) {
// searches through the comma-separated list for a match,
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 562a9f8..521072e 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -51,7 +51,7 @@
*
* DataConnection {
* + mDefaultState {
- * EVENT_RESET { clearSettings, >mInactiveState }.
+ * EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }.
* EVENT_CONNECT { notifyConnectCompleted(FailCause.UNKNOWN) }.
* EVENT_DISCONNECT { notifyDisconnectCompleted }.
*
@@ -60,8 +60,10 @@
* EVENT_GET_LAST_FAIL_DONE,
* EVENT_DEACTIVATE_DONE.
* }
- * ++ # mInactiveState {
- * EVENT_RESET.
+ * ++ # mInactiveState
+ * e(doNotifications)
+ * x(clearNotifications) {
+ * EVENT_RESET { notifiyDisconnectCompleted }.
* EVENT_CONNECT {startConnecting, >mActivatingState }.
* }
* ++ mActivatingState {
@@ -338,6 +340,8 @@
if (DBG) log("NotifyDisconnectCompleted");
Message msg = dp.onCompletedMsg;
+ log(String.format("msg.what=%d msg.obj=%s",
+ msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
AsyncResult.forMessage(msg);
msg.sendToTarget();
@@ -437,6 +441,9 @@
case EVENT_RESET:
if (DBG) log("DcDefaultState: msg.what=EVENT_RESET");
clearSettings();
+ if (msg.obj != null) {
+ notifyDisconnectCompleted((DisconnectParams) msg.obj);
+ }
transitionTo(mInactiveState);
break;
@@ -467,9 +474,48 @@
* The state machine is inactive and expects a EVENT_CONNECT.
*/
private class DcInactiveState extends HierarchicalState {
+ private ConnectionParams mConnectionParams = null;
+ private FailCause mFailCause = null;
+ private DisconnectParams mDisconnectParams = null;
+
+ public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
+ log("DcInactiveState: setEnterNoticationParams cp,cause");
+ mConnectionParams = cp;
+ mFailCause = cause;
+ }
+
+ public void setEnterNotificationParams(DisconnectParams dp) {
+ log("DcInactiveState: setEnterNoticationParams dp");
+ mDisconnectParams = dp;
+ }
+
@Override protected void enter() {
mTag += 1;
+
+ /**
+ * Now that we've transitioned to Inactive state we
+ * can send notifications. Previously we sent the
+ * notifications in the processMessage handler but
+ * that caused a race condition because the synchronous
+ * call to isInactive.
+ */
+ if ((mConnectionParams != null) && (mFailCause != null)) {
+ log("DcInactiveState: enter notifyConnectCompleted");
+ notifyConnectCompleted(mConnectionParams, mFailCause);
+ }
+ if (mDisconnectParams != null) {
+ log("DcInactiveState: enter notifyDisconnectCompleted");
+ notifyDisconnectCompleted(mDisconnectParams);
+ }
}
+
+ @Override protected void exit() {
+ // clear notifications
+ mConnectionParams = null;
+ mFailCause = null;
+ mDisconnectParams = null;
+ }
+
@Override protected boolean processMessage(Message msg) {
boolean retVal;
@@ -478,6 +524,9 @@
if (DBG) {
log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
}
+ if (msg.obj != null) {
+ notifyDisconnectCompleted((DisconnectParams) msg.obj);
+ }
retVal = true;
break;
@@ -526,12 +575,14 @@
switch (result) {
case SUCCESS:
// All is well
- notifyConnectCompleted(cp, FailCause.NONE);
+ mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
transitionTo(mActiveState);
break;
case ERR_BadCommand:
// Vendor ril rejected the command and didn't connect.
- notifyConnectCompleted(cp, result.mFailCause);
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
transitionTo(mInactiveState);
break;
case ERR_BadDns:
@@ -565,7 +616,9 @@
int rilFailCause = ((int[]) (ar.result))[0];
cause = getFailCauseFromRequest(rilFailCause);
}
- notifyConnectCompleted(cp, cause);
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp, cause);
transitionTo(mInactiveState);
} else {
if (DBG) {
@@ -591,6 +644,35 @@
* The state machine is connected, expecting an EVENT_DISCONNECT.
*/
private class DcActiveState extends HierarchicalState {
+ private ConnectionParams mConnectionParams = null;
+ private FailCause mFailCause = null;
+
+ public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
+ log("DcInactiveState: setEnterNoticationParams cp,cause");
+ mConnectionParams = cp;
+ mFailCause = cause;
+ }
+
+ @Override public void enter() {
+ /**
+ * Now that we've transitioned to Active state we
+ * can send notifications. Previously we sent the
+ * notifications in the processMessage handler but
+ * that caused a race condition because the synchronous
+ * call to isActive.
+ */
+ if ((mConnectionParams != null) && (mFailCause != null)) {
+ log("DcActiveState: enter notifyConnectCompleted");
+ notifyConnectCompleted(mConnectionParams, mFailCause);
+ }
+ }
+
+ @Override protected void exit() {
+ // clear notifications
+ mConnectionParams = null;
+ mFailCause = null;
+ }
+
@Override protected boolean processMessage(Message msg) {
boolean retVal;
@@ -627,7 +709,9 @@
AsyncResult ar = (AsyncResult) msg.obj;
DisconnectParams dp = (DisconnectParams) ar.userObj;
if (dp.tag == mTag) {
- notifyDisconnectCompleted((DisconnectParams) ar.userObj);
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
transitionTo(mInactiveState);
} else {
if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
@@ -660,7 +744,9 @@
ConnectionParams cp = (ConnectionParams) ar.userObj;
if (cp.tag == mTag) {
if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE");
- notifyConnectCompleted(cp, FailCause.UNKNOWN);
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN);
transitionTo(mInactiveState);
} else {
if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag="
@@ -683,9 +769,12 @@
/**
* Disconnect from the network.
+ *
+ * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+ * With AsyncResult.userObj set to the original msg.obj.
*/
- public void reset() {
- sendMessage(obtainMessage(EVENT_RESET));
+ public void reset(Message onCompletedMsg) {
+ sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg)));
}
/**
@@ -726,6 +815,9 @@
// ****** The following are used for debugging.
/**
+ * TODO: This should be an asynchronous call and we wouldn't
+ * have to use handle the notification in the DcInactiveState.enter.
+ *
* @return true if the state machine is in the inactive state.
*/
public boolean isInactive() {
@@ -734,7 +826,10 @@
}
/**
- * @return true if the state machine is in the inactive state.
+ * TODO: This should be an asynchronous call and we wouldn't
+ * have to use handle the notification in the DcActiveState.enter.
+ *
+ * @return true if the state machine is in the active state.
*/
public boolean isActive() {
boolean retVal = getCurrentState() == mActiveState;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index cab7b81..e8e18a1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -101,6 +101,7 @@
protected static final int EVENT_CDMA_OTA_PROVISION = 35;
protected static final int EVENT_RESTART_RADIO = 36;
protected static final int EVENT_SET_MASTER_DATA_ENABLE = 37;
+ protected static final int EVENT_RESET_DONE = 38;
/***** Constants *****/
@@ -265,6 +266,7 @@
protected abstract void onRadioOffOrNotAvailable();
protected abstract void onDataSetupComplete(AsyncResult ar);
protected abstract void onDisconnectDone(AsyncResult ar);
+ protected abstract void onResetDone(AsyncResult ar);
protected abstract void onVoiceCallStarted();
protected abstract void onVoiceCallEnded();
protected abstract void onCleanUpConnection(boolean tearDown, String reason);
@@ -331,6 +333,10 @@
onSetDataEnabled(enabled);
break;
+ case EVENT_RESET_DONE:
+ onResetDone((AsyncResult) msg.obj);
+ break;
+
default:
Log.e("DATA", "Unidentified event = " + msg.what);
break;
diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java
index 385b191..779f358 100644
--- a/telephony/java/com/android/internal/telephony/RetryManager.java
+++ b/telephony/java/com/android/internal/telephony/RetryManager.java
@@ -153,13 +153,17 @@
}
/**
- * Configure for using string which allow arbitary
+ * Configure for using string which allow arbitrary
* sequences of times. See class comments for the
* string format.
*
- * @return true if successfull
+ * @return true if successful
*/
public boolean configure(String configStr) {
+ // Strip quotes if present.
+ if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
+ configStr = configStr.substring(1, configStr.length()-1);
+ }
if (DBG) log("configure: '" + configStr + "'");
if (!TextUtils.isEmpty(configStr)) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index b5461bf..9218715 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -368,7 +368,7 @@
* @param reason reason for the clean up.
*/
private void cleanUpConnection(boolean tearDown, String reason) {
- if (DBG) log("Clean up connection due to " + reason);
+ if (DBG) log("cleanUpConnection: reason: " + reason);
// Clear the reconnect alarm, if set.
if (mReconnectIntent != null) {
@@ -380,25 +380,25 @@
setState(State.DISCONNECTING);
- for (DataConnection connBase : dataConnectionList) {
- CdmaDataConnection conn = (CdmaDataConnection) connBase;
-
+ boolean notificationDeferred = false;
+ for (DataConnection conn : dataConnectionList) {
if(conn != null) {
if (tearDown) {
- Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason);
- conn.disconnect(msg);
+ if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
+ conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
} else {
- conn.reset();
+ if (DBG) log("cleanUpConnection: !tearDown, call conn.reset");
+ conn.reset(obtainMessage(EVENT_RESET_DONE, reason));
}
+ notificationDeferred = true;
}
}
stopNetStatPoll();
- if (!tearDown) {
- setState(State.IDLE);
- phone.notifyDataConnection(reason);
- mIsApnActive = false;
+ if (!notificationDeferred) {
+ if (DBG) log("cleanupConnection: !tearDown && !resettingConn");
+ gotoIdleAndNotifyDataConnection(reason);
}
}
@@ -622,6 +622,13 @@
setState(State.FAILED);
}
+ private void gotoIdleAndNotifyDataConnection(String reason) {
+ if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
+ setState(State.IDLE);
+ phone.notifyDataConnection(reason);
+ mIsApnActive = false;
+ }
+
protected void onRecordsLoaded() {
if (state == State.FAILED) {
cleanUpConnection(false, null);
@@ -731,7 +738,7 @@
}
/**
- * @override com.android.internal.telephony.DataConnectionTracker
+ * Called when EVENT_DISCONNECT_DONE is received.
*/
protected void onDisconnectDone(AsyncResult ar) {
if(DBG) log("EVENT_DISCONNECT_DONE");
@@ -762,6 +769,20 @@
}
/**
+ * Called when EVENT_RESET_DONE is received so goto
+ * IDLE state and send notifications to those interested.
+ */
+ @Override
+ protected void onResetDone(AsyncResult ar) {
+ if (DBG) log("EVENT_RESET_DONE");
+ String reason = null;
+ if (ar.userObj instanceof String) {
+ reason = (String) ar.userObj;
+ }
+ gotoIdleAndNotifyDataConnection(reason);
+ }
+
+ /**
* @override com.android.internal.telephony.DataConnectionTracker
*/
protected void onVoiceCallStarted() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 30beaaa..f26e54e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -502,20 +502,22 @@
setState(State.DISCONNECTING);
+ boolean notificationDeferred = false;
for (DataConnection conn : pdpList) {
if (tearDown) {
- Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason);
- conn.disconnect(msg);
+ if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
+ conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
} else {
- conn.reset();
+ if (DBG) log("cleanUpConnection: !tearDown, call conn.reset");
+ conn.reset(obtainMessage(EVENT_RESET_DONE, reason));
}
+ notificationDeferred = true;
}
stopNetStatPoll();
- if (!tearDown) {
- setState(State.IDLE);
- phone.notifyDataConnection(reason);
- mActiveApn = null;
+ if (!notificationDeferred) {
+ if (DBG) log("cleanupConnection: !tearDown && !resettingConn");
+ gotoIdleAndNotifyDataConnection(reason);
}
}
@@ -749,6 +751,13 @@
mReregisterOnReconnectFailure = false;
}
+ private void gotoIdleAndNotifyDataConnection(String reason) {
+ if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
+ setState(State.IDLE);
+ phone.notifyDataConnection(reason);
+ mActiveApn = null;
+ }
+
/**
* This is a kludge to deal with the fact that
* the PDP state change notification doesn't always work
@@ -1172,6 +1181,9 @@
}
}
+ /**
+ * Called when EVENT_DISCONNECT_DONE is received.
+ */
protected void onDisconnectDone(AsyncResult ar) {
String reason = null;
if(DBG) log("EVENT_DISCONNECT_DONE");
@@ -1186,6 +1198,19 @@
}
}
+ /**
+ * Called when EVENT_RESET_DONE is received.
+ */
+ @Override
+ protected void onResetDone(AsyncResult ar) {
+ if (DBG) log("EVENT_RESET_DONE");
+ String reason = null;
+ if (ar.userObj instanceof String) {
+ reason = (String) ar.userObj;
+ }
+ gotoIdleAndNotifyDataConnection(reason);
+ }
+
protected void onPollPdp() {
if (state == State.CONNECTED) {
// only poll when connected
@@ -1487,5 +1512,4 @@
protected void log(String s) {
Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
}
-
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 6352b41..e4fcf6c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -1630,7 +1630,7 @@
notificationId = PS_NOTIFICATION;
break;
case CS_ENABLED:
- details = context.getText(com.android.internal.R.string.RestrictedOnAll);;
+ details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);;
break;
case CS_NORMAL_ENABLED:
details = context.getText(com.android.internal.R.string.RestrictedOnNormal);;
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
index aa2981b..02590d3 100644
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
@@ -401,7 +401,7 @@
PhoneNumberUtils.convertKeypadLettersToDigits("(800) ABC-DEFG"));
}
- @SmallTest
+ // To run this test, the device has to be registered with network
public void testCheckAndProcessPlusCode() {
assertEquals("0118475797000",
PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index 1ea1285..2d6977c 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -33,7 +33,7 @@
assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg");
assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo");
assertEquals(MccTable.defaultTimeZoneForMcc(441), "Asia/Tokyo");
- assertEquals(MccTable.defaultTimeZoneForMcc(525), "Singapore");
+ assertEquals(MccTable.defaultTimeZoneForMcc(525), "Asia/Singapore");
assertEquals(MccTable.defaultTimeZoneForMcc(240), null); // tz not defined, hence default
assertEquals(MccTable.defaultTimeZoneForMcc(0), null); // mcc not defined, hence default
assertEquals(MccTable.defaultTimeZoneForMcc(2000), null); // mcc not defined, hence default
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java
index bf0c88b..3757017 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java
@@ -146,15 +146,15 @@
}
/**
- * Test string configuration using all options.
+ * Test string configuration using all options and with quotes.
*/
@SmallTest
public void testRetryManageString() throws Exception {
RetryManager rm = new RetryManager();
int time;
- assertTrue(rm.configure("max_retries=4,"
- + "default_randomization=100,1000, 2000 :200 , 3000"));
+ assertTrue(rm.configure(
+ "\"max_retries=4, default_randomization=100,1000, 2000 :200 , 3000\""));
assertTrue(rm.isRetryNeeded());
time = rm.getRetryTimer();
assertTrue((time >= 1000) && (time < 1100));
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 4078622..3fd71c8 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -32,6 +32,7 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -113,6 +114,15 @@
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
+ /**
+ * @hide
+ */
+ @SuppressWarnings("unused")
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ return MockContentProvider.this.call(method, request, args);
+ }
+
public IBinder asBinder() {
throw new UnsupportedOperationException();
}
@@ -205,6 +215,14 @@
}
/**
+ * @hide
+ */
+ @Override
+ public Bundle call(String method, String request, Bundle args) {
+ throw new UnsupportedOperationException("unimplemented mock method call");
+ }
+
+ /**
* Returns IContentProvider which calls back same methods in this class.
* By overriding this class, we avoid the mechanism hidden behind ContentProvider
* (IPC, etc.)
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 7c0a1e2..0be5bea 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -27,6 +27,7 @@
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -38,7 +39,7 @@
* {@link java.lang.UnsupportedOperationException}. Tests can extend this class to
* implement behavior needed for tests.
*
- * @hide - @hide because this exposes bulkQuery(), which must also be hidden.
+ * @hide - @hide because this exposes bulkQuery() and call(), which must also be hidden.
*/
public class MockIContentProvider implements IContentProvider {
public int bulkInsert(Uri url, ContentValues[] initialValues) {
@@ -93,6 +94,11 @@
throw new UnsupportedOperationException("unimplemented mock method");
}
+ public Bundle call(String method, String request, Bundle args)
+ throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
public IBinder asBinder() {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
index 5aaf13b..c7bacd4 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
@@ -346,21 +346,20 @@
}
}
- public void testIsContainerMountedAfterRename() {
+ public void testContainerSize() {
+ IMountService ms = getMs();
try {
Assert.assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testRenameContainer.1", 4, "none"));
+ createContainer("testContainerSize", 1, "none"));
+ String path = ms.getSecureContainerPath("com.android.unittests.AsecTests.testUnmountBusyContainer");
- Assert.assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testRenameContainer.1", false));
-
- Assert.assertEquals(StorageResultCode.OperationSucceeded,
- renameContainer("testRenameContainer.1", "testRenameContainer.2"));
-
- Assert.assertEquals(false, containerExists("testRenameContainer.1"));
- Assert.assertEquals(true, containerExists("testRenameContainer.2"));
- // Check if isContainerMounted returns valid value
- Assert.assertEquals(true, isContainerMounted("testRenameContainer.2"));
+ byte[] buf = new byte[4096];
+ File f = new File(path, "reference");
+ FileOutputStream fos = new FileOutputStream(f);
+ for (int i = 0; i < (1024 * 1024); i+= buf.length) {
+ fos.write(buf);
+ }
+ fos.close();
} catch (Exception e) {
failStr(e);
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 5e3895a..50eca02 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -263,6 +263,9 @@
if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
return true;
}
+ if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ return false;
+ }
// TODO Out of memory checks here.
boolean checkSd = false;
int setLoc = 0;
@@ -403,7 +406,7 @@
return ip;
} finally {
if (cleanUp) {
- //cleanUpInstall(ip);
+ cleanUpInstall(ip);
}
}
}
@@ -931,9 +934,9 @@
public void testManifestInstallLocationFwdLockedSdcard() {
installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_FORWARD_LOCK, true, true,
- PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- PackageInfo.INSTALL_LOCATION_AUTO);
+ PackageManager.INSTALL_FORWARD_LOCK, true, false,
+ -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
public void xxxtestClearAllSecureContainers() {
@@ -1050,6 +1053,21 @@
}
}
+ private int getInstallLoc() {
+ boolean userSetting = false;
+ int origDefaultLoc = PackageInfo.INSTALL_LOCATION_AUTO;
+ try {
+ userSetting = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION) != 0;
+ origDefaultLoc = Settings.System.getInt(mContext.getContentResolver(), Settings.System.DEFAULT_INSTALL_LOCATION);
+ } catch (SettingNotFoundException e1) {
+ }
+ return origDefaultLoc;
+ }
+
+ private void setInstallLoc(int loc) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.DEFAULT_INSTALL_LOCATION, loc);
+ }
/*
* Utility function that reads a apk bundled as a raw resource
* copies it into own data directory and invokes
@@ -1058,6 +1076,8 @@
*/
public void moveFromRawResource(int installFlags, int moveFlags,
int expRetCode) {
+ int origDefaultLoc = getInstallLoc();
+ setInstallLoc(PackageInfo.INSTALL_LOCATION_AUTO);
// Install first
InstallParams ip = sampleInstallFromRawResource(installFlags, false);
ApplicationInfo oldAppInfo = null;
@@ -1091,6 +1111,8 @@
failStr("Failed with exception : " + e);
} finally {
cleanUpInstall(ip);
+ // Restore default install location
+ setInstallLoc(origDefaultLoc);
}
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 191b5e9..100a466 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -98,11 +98,6 @@
ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found
ignoreResultList.add("storage/database-lock-after-reload.html"); // failure
- ignoreResultList.add("storage/domstorage/localstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8
- ignoreResultList.add("storage/domstorage/sessionstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8
- ignoreResultList.add("storage/statement-error-callback.html"); // expected line number diff in V8 only
- ignoreResultList.add("storage/transaction-error-callback.html"); // expected line number diff in V8 only
- ignoreResultList.add("storage/transaction-callback-exception-crash.html"); // expected line number diff in V8 only
ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707
ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us
diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
index afbc703..e11ecad 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
@@ -17,11 +17,11 @@
package com.android.backuptest;
import android.app.ListActivity;
-import android.backup.BackupHelperDispatcher;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
-import android.backup.BackupManager;
-import android.backup.FileBackupHelper;
+import android.app.backup.BackupHelperDispatcher;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManager;
+import android.app.backup.FileBackupHelper;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index 8e4fd39..3fdd96b 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -16,9 +16,9 @@
package com.android.backuptest;
-import android.backup.BackupHelperAgent;
-import android.backup.FileBackupHelper;
-import android.backup.SharedPreferencesBackupHelper;
+import android.app.backup.BackupHelperAgent;
+import android.app.backup.FileBackupHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
public class BackupTestAgent extends BackupHelperAgent
{
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6e7a66d..fc655a7 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -820,7 +820,12 @@
if (strcmp(name, kWildcardName) == 0) {
if (out) out->uiMode =
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_NORMAL;
+ | ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_DESK;
return true;
} else if (strcmp(name, "car") == 0) {
if (out) out->uiMode =
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7142b1c..b7580b3 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -447,7 +447,7 @@
static bool applyFileOverlay(Bundle *bundle,
const sp<AaptAssets>& assets,
- const sp<ResourceTypeSet>& baseSet,
+ sp<ResourceTypeSet> *baseSet,
const char *resType)
{
if (bundle->getVerbose()) {
@@ -475,13 +475,16 @@
if (bundle->getVerbose()) {
printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
}
- size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
+ size_t baseIndex = UNKNOWN_ERROR;
+ if (baseSet->get() != NULL) {
+ baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
+ }
if (baseIndex < UNKNOWN_ERROR) {
// look for same flavor. For a given file (strings.xml, for example)
// there may be a locale specific or other flavors - we want to match
// the same flavor.
sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
- sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
+ sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
overlayGroup->getFiles();
@@ -520,8 +523,12 @@
assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
}
} else {
+ if (baseSet->get() == NULL) {
+ *baseSet = new ResourceTypeSet();
+ assets->getResources()->add(String8(resType), *baseSet);
+ }
// this group doesn't exist (a file that's only in the overlay)
- baseSet->add(overlaySet->keyAt(overlayIndex),
+ (*baseSet)->add(overlaySet->keyAt(overlayIndex),
overlaySet->valueAt(overlayIndex));
// make sure all flavors are defined in the resources.
sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
@@ -637,6 +644,7 @@
sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
if (application != NULL) {
fullyQualifyClassName(origPackage, application, String16("name"));
+ fullyQualifyClassName(origPackage, application, String16("backupAgent"));
Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
for (size_t i = 0; i < children.size(); i++) {
@@ -750,13 +758,13 @@
current = current->getOverlay();
}
// apply the overlay files to the base set
- if (!applyFileOverlay(bundle, assets, drawables, "drawable") ||
- !applyFileOverlay(bundle, assets, layouts, "layout") ||
- !applyFileOverlay(bundle, assets, anims, "anim") ||
- !applyFileOverlay(bundle, assets, xmls, "xml") ||
- !applyFileOverlay(bundle, assets, raws, "raw") ||
- !applyFileOverlay(bundle, assets, colors, "color") ||
- !applyFileOverlay(bundle, assets, menus, "menu")) {
+ if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
+ !applyFileOverlay(bundle, assets, &layouts, "layout") ||
+ !applyFileOverlay(bundle, assets, &anims, "anim") ||
+ !applyFileOverlay(bundle, assets, &xmls, "xml") ||
+ !applyFileOverlay(bundle, assets, &raws, "raw") ||
+ !applyFileOverlay(bundle, assets, &colors, "color") ||
+ !applyFileOverlay(bundle, assets, &menus, "menu")) {
return UNKNOWN_ERROR;
}
@@ -1778,6 +1786,40 @@
rules.editValueAt(index).add(where);
}
+void
+addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
+ const char* pkg, const String8& srcName, int line)
+{
+ String8 className(inClassName);
+ if (pkg != NULL) {
+ // asdf --> package.asdf
+ // .asdf .a.b --> package.asdf package.a.b
+ // asdf.adsf --> asdf.asdf
+ const char* p = className.string();
+ const char* q = strchr(p, '.');
+ if (p == q) {
+ className = pkg;
+ className.append(inClassName);
+ } else if (q == NULL) {
+ className = pkg;
+ className.append(".");
+ className.append(inClassName);
+ }
+ }
+
+ String8 rule("-keep class ");
+ rule += className;
+ rule += " { <init>(...); }";
+
+ String8 location("view ");
+ location += srcName;
+ char lineno[20];
+ sprintf(lineno, ":%d", line);
+ location += lineno;
+
+ keep->add(rule, location);
+}
+
status_t
writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
{
@@ -1839,6 +1881,13 @@
if (tag == "application") {
inApplication = true;
keepTag = true;
+
+ String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+ "backupAgent", &error);
+ if (agent.length() > 0) {
+ addProguardKeepRule(keep, agent, pkg.string(),
+ assFile->getPrintableSource(), tree.getLineNumber());
+ }
} else if (tag == "instrumentation") {
keepTag = true;
}
@@ -1856,31 +1905,8 @@
return -1;
}
if (name.length() > 0) {
- // asdf --> package.asdf
- // .asdf .a.b --> package.asdf package.a.b
- // asdf.adsf --> asdf.asdf
- String8 rule("-keep class ");
- const char* p = name.string();
- const char* q = strchr(p, '.');
- if (p == q) {
- rule += pkg;
- rule += name;
- } else if (q == NULL) {
- rule += pkg;
- rule += ".";
- rule += name;
- } else {
- rule += name;
- }
-
- String8 location = tag;
- location += " ";
- location += assFile->getSourceFile();
- char lineno[20];
- sprintf(lineno, ":%d", tree.getLineNumber());
- location += lineno;
-
- keep->add(rule, location);
+ addProguardKeepRule(keep, name, pkg.string(),
+ assFile->getPrintableSource(), tree.getLineNumber());
}
}
}
@@ -1889,7 +1915,8 @@
}
status_t
-writeProguardForLayout(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile)
+writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
+ const char* startTag, const char* altTag)
{
status_t err;
ResXMLTree tree;
@@ -1903,6 +1930,23 @@
tree.restart();
+ if (startTag != NULL) {
+ bool haveStart = false;
+ while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code != ResXMLTree::START_TAG) {
+ continue;
+ }
+ String8 tag(tree.getElementName(&len));
+ if (tag == startTag) {
+ haveStart = true;
+ }
+ break;
+ }
+ if (!haveStart) {
+ return NO_ERROR;
+ }
+ }
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
continue;
@@ -1911,17 +1955,19 @@
// If there is no '.', we'll assume that it's one of the built in names.
if (strchr(tag.string(), '.')) {
- String8 rule("-keep class ");
- rule += tag;
- rule += " { <init>(...); }";
-
- String8 location("view ");
- location += layoutFile->getSourceFile();
- char lineno[20];
- sprintf(lineno, ":%d", tree.getLineNumber());
- location += lineno;
-
- keep->add(rule, location);
+ addProguardKeepRule(keep, tag, NULL,
+ layoutFile->getPrintableSource(), tree.getLineNumber());
+ } else if (altTag != NULL && tag == altTag) {
+ ssize_t classIndex = tree.indexOfAttribute(NULL, "class");
+ if (classIndex < 0) {
+ fprintf(stderr, "%s:%d: <view> does not have class attribute.\n",
+ layoutFile->getPrintableSource().string(), tree.getLineNumber());
+ } else {
+ size_t len;
+ addProguardKeepRule(keep,
+ String8(tree.getAttributeStringValue(classIndex, &len)), NULL,
+ layoutFile->getPrintableSource(), tree.getLineNumber());
+ }
}
}
@@ -1937,10 +1983,16 @@
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
- if ((dirName != String8("layout")) && (strncmp(dirName.string(), "layout-", 7) != 0)) {
+ const char* startTag = NULL;
+ const char* altTag = NULL;
+ if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
+ altTag = "view";
+ } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
+ startTag = "PreferenceScreen";
+ } else {
continue;
}
-
+
const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
const size_t N = groups.size();
for (size_t i=0; i<N; i++) {
@@ -1948,7 +2000,7 @@
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
- err = writeProguardForLayout(keep, files.valueAt(j));
+ err = writeProguardForXml(keep, files.valueAt(j), startTag, altTag);
if (err < 0) {
return err;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0b531c2..1f9d152 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2369,7 +2369,7 @@
if (configSet.find(region) == configSet.end()) {
if (configSet.count(defaultLocale) == 0) {
fprintf(stdout, "aapt: warning: "
- "*** string '%s' has no default or required localization "
+ "**** string '%s' has no default or required localization "
"for '%s' in %s\n",
String8(nameIter->first).string(),
config.string(),
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index f3738e3..d833e33 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -29,7 +29,7 @@
interface IWifiManager
{
List<WifiConfiguration> getConfiguredNetworks();
-
+
int addOrUpdateNetwork(in WifiConfiguration config);
boolean removeNetwork(int netId);
@@ -47,7 +47,7 @@
boolean disconnect();
boolean reconnect();
-
+
boolean reassociate();
WifiInfo getConnectionInfo();
@@ -61,7 +61,7 @@
boolean setNumAllowedChannels(int numChannels, boolean persist);
int[] getValidChannelCounts();
-
+
boolean saveConfiguration();
DhcpInfo getDhcpInfo();
@@ -77,5 +77,9 @@
void acquireMulticastLock(IBinder binder, String tag);
void releaseMulticastLock();
+
+ boolean setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable);
+
+ int getWifiApEnabledState();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 178f76e..9ef8ba1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -123,7 +123,88 @@
* @see #getWifiState()
*/
public static final int WIFI_STATE_UNKNOWN = 4;
-
+
+ /**
+ * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled,
+ * enabling, disabling, or failed.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_AP_STATE_CHANGED_ACTION =
+ "android.net.wifi.WIFI_AP_STATE_CHANGED";
+
+ /**
+ * The lookup key for an int that indicates whether Wi-Fi AP is enabled,
+ * disabled, enabling, disabling, or failed. Retrieve it with
+ * {@link android.content.Intent#getIntExtra(String,int)}.
+ *
+ * @see #WIFI_AP_STATE_DISABLED
+ * @see #WIFI_AP_STATE_DISABLING
+ * @see #WIFI_AP_STATE_ENABLED
+ * @see #WIFI_AP_STATE_ENABLING
+ * @see #WIFI_AP_STATE_FAILED
+ *
+ * @hide
+ */
+ public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
+ /**
+ * The previous Wi-Fi state.
+ *
+ * @see #EXTRA_WIFI_AP_STATE
+ *
+ * @hide
+ */
+ public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
+ /**
+ * Wi-Fi AP is currently being disabled. The state will change to
+ * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully.
+ *
+ * @see #WIFI_AP_STATE_CHANGED_ACTION
+ * @see #getWifiApState()
+ *
+ * @hide
+ */
+ public static final int WIFI_AP_STATE_DISABLING = 0;
+ /**
+ * Wi-Fi AP is disabled.
+ *
+ * @see #WIFI_AP_STATE_CHANGED_ACTION
+ * @see #getWifiState()
+ *
+ * @hide
+ */
+ public static final int WIFI_AP_STATE_DISABLED = 1;
+ /**
+ * Wi-Fi AP is currently being enabled. The state will change to
+ * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
+ *
+ * @see #WIFI_AP_STATE_CHANGED_ACTION
+ * @see #getWifiApState()
+ *
+ * @hide
+ */
+ public static final int WIFI_AP_STATE_ENABLING = 2;
+ /**
+ * Wi-Fi AP is enabled.
+ *
+ * @see #WIFI_AP_STATE_CHANGED_ACTION
+ * @see #getWifiApState()
+ *
+ * @hide
+ */
+ public static final int WIFI_AP_STATE_ENABLED = 3;
+ /**
+ * Wi-Fi AP is in a failed state. This state will occur when an error occurs during
+ * enabling or disabling
+ *
+ * @see #WIFI_AP_STATE_CHANGED_ACTION
+ * @see #getWifiApState()
+ *
+ * @hide
+ */
+ public static final int WIFI_AP_STATE_FAILED = 4;
+
/**
* Broadcast intent action indicating that a connection to the supplicant has
* been established (and it is now possible
@@ -681,6 +762,54 @@
}
/**
+ * Start AccessPoint mode with the specified
+ * configuration. If the radio is already running in
+ * AP mode, update the new configuration
+ * Note that starting in access point mode disables station
+ * mode operation
+ * @param wifiConfig SSID, security and channel details as
+ * part of WifiConfiguration
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * @hide Dont open up yet
+ */
+ public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
+ try {
+ return mService.setWifiApEnabled(wifiConfig, enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the Wi-Fi enabled state.
+ * @return One of {@link #WIFI_AP_STATE_DISABLED},
+ * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
+ * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
+ * @see #isWifiApEnabled()
+ *
+ * @hide Dont open yet
+ */
+ public int getWifiApState() {
+ try {
+ return mService.getWifiApEnabledState();
+ } catch (RemoteException e) {
+ return WIFI_AP_STATE_FAILED;
+ }
+ }
+
+ /**
+ * Return whether Wi-Fi AP is enabled or disabled.
+ * @return {@code true} if Wi-Fi AP is enabled
+ * @see #getWifiApState()
+ *
+ * @hide Dont open yet
+ */
+ public boolean isWifiApEnabled() {
+ return getWifiApState() == WIFI_AP_STATE_ENABLED;
+ }
+
+ /**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
* Acquiring a WifiLock will keep the radio on until the lock is released. Multiple