summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.xml736
-rw-r--r--cmds/stagefright/stagefright.cpp61
-rw-r--r--core/java/android/accounts/AccountManager.java4
-rw-r--r--core/java/android/accounts/AccountManagerService.java4
-rw-r--r--core/java/android/accounts/IAccountManager.aidl5
-rw-r--r--core/java/android/app/ApplicationContext.java13
-rw-r--r--core/java/android/app/DeviceAdmin.java221
-rw-r--r--core/java/android/app/DeviceAdminInfo.java186
-rw-r--r--core/java/android/app/DevicePolicyManager.java425
-rw-r--r--core/java/android/app/Dialog.java3
-rw-r--r--core/java/android/app/IDevicePolicyManager.aidl49
-rw-r--r--core/java/android/app/SearchManager.java4
-rw-r--r--core/java/android/app/WallpaperInfo.java16
-rw-r--r--core/java/android/content/Context.java23
-rw-r--r--core/java/android/content/DialogInterface.java1
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java204
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java49
-rwxr-xr-xcore/java/android/gesture/Gesture.java29
-rw-r--r--core/java/android/gesture/GesturePoint.java7
-rw-r--r--core/java/android/gesture/GestureStroke.java29
-rwxr-xr-xcore/java/android/gesture/GestureUtilities.java202
-rwxr-xr-xcore/java/android/gesture/Instance.java2
-rw-r--r--core/java/android/os/IMountService.aidl5
-rw-r--r--core/java/android/pim/vcard/VCardParser_V21.java2
-rw-r--r--core/java/android/provider/CallLog.java54
-rw-r--r--core/java/android/provider/ContactsContract.java292
-rw-r--r--core/java/android/provider/Downloads.java570
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java8
-rw-r--r--core/java/android/view/ScaleGestureDetector.java360
-rw-r--r--core/java/android/view/TransformGestureDetector.java316
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java9
-rw-r--r--core/java/android/webkit/JWebCoreJavaBridge.java1
-rw-r--r--core/java/android/webkit/WebView.java11
-rw-r--r--core/java/android/webkit/WebViewCore.java12
-rw-r--r--core/java/android/widget/LinearLayout.java20
-rw-r--r--core/res/AndroidManifest.xml16
-rw-r--r--core/res/res/values/arrays.xml3
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--keystore/java/android/security/SystemKeyStore.java29
-rw-r--r--keystore/tests/src/android/security/SystemKeyStoreTest.java13
-rw-r--r--media/libstagefright/omx/OMX.cpp44
-rw-r--r--media/libstagefright/omx/tests/OMXHarness.cpp4
-rw-r--r--media/sdutils/sdutil.cpp15
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java112
-rw-r--r--opengl/libagl/Android.mk5
-rw-r--r--opengl/libs/Android.mk11
-rw-r--r--services/java/com/android/server/ConnectivityService.java13
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java471
-rw-r--r--services/java/com/android/server/MountService.java17
-rw-r--r--services/java/com/android/server/PackageManagerService.java44
-rw-r--r--services/java/com/android/server/SystemServer.java27
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java67
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java14
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/TimeTest.java30
-rw-r--r--tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java76
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap.java38
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java566
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas.java44
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Matrix.java8
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java1
63 files changed, 4758 insertions, 858 deletions
diff --git a/Android.mk b/Android.mk
index ded8173..7c7bb62 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,6 +75,7 @@ LOCAL_SRC_FILES += \
core/java/android/app/IActivityWatcher.aidl \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IBackupAgent.aidl \
+ core/java/android/app/IDevicePolicyManager.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/ISearchManager.aidl \
diff --git a/api/current.xml b/api/current.xml
index 96e7fd6..ee7d330 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -177,6 +177,17 @@
visibility="public"
>
</field>
+<field name="BIND_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BIND_INPUT_METHOD"
type="java.lang.String"
transient="false"
@@ -188,6 +199,17 @@
visibility="public"
>
</field>
+<field name="BIND_WALLPAPER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_WALLPAPER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BLUETOOTH"
type="java.lang.String"
transient="false"
@@ -14581,6 +14603,25 @@
<parameter name="key" type="java.lang.String">
</parameter>
</method>
+<method name="hasFeatures"
+ return="android.accounts.AccountManagerFuture&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
<method name="invalidateAuthToken"
return="void"
abstract="false"
@@ -14690,25 +14731,6 @@
<parameter name="value" type="java.lang.String">
</parameter>
</method>
-<method name="testHasFeatures"
- return="android.accounts.AccountManagerFuture&lt;java.lang.Boolean&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="account" type="android.accounts.Account">
-</parameter>
-<parameter name="features" type="java.lang.String[]">
-</parameter>
-<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;java.lang.Boolean&gt;">
-</parameter>
-<parameter name="handler" type="android.os.Handler">
-</parameter>
-</method>
<method name="updateCredentials"
return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
abstract="false"
@@ -19831,6 +19853,600 @@
</parameter>
</method>
</interface>
+<class name="DeviceAdmin"
+ extends="android.content.BroadcastReceiver"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DeviceAdmin"
+ type="android.app.DeviceAdmin"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getManager"
+ return="android.app.DevicePolicyManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getWho"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="onDisabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordFailed"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordSucceeded"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onReceive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<field name="ACTION_DEVICE_ADMIN_DISABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.DEVICE_ADMIN_DISABLED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DEVICE_ADMIN_ENABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.DEVICE_ADMIN_ENABLED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_FAILED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_FAILED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_SUCCEEDED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_SUCCEEDED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DEVICE_ADMIN_META_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.device_admin&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DeviceAdminInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="DeviceAdminInfo"
+ type="android.app.DeviceAdminInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="receiver" type="android.content.pm.ResolveInfo">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException">
+</exception>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pw" type="android.util.Printer">
+</parameter>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+</method>
+<method name="getActivityInfo"
+ return="android.content.pm.ActivityInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getComponent"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPackageName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getReceiverName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="loadLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" 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="DevicePolicyManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getActiveMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getActivePasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentFailedPasswordAttempts"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMaximumTimeToLock"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isAdminActive"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="removeActiveAdmin"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="setMaximumTimeToLock"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="timeMs" type="long">
+</parameter>
+</method>
+<method name="setMinimumPasswordLength"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="wipeData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ACTION_ADD_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ADD_DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_SET_NEW_PASSWORD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.SET_NEW_PASSWORD&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.extra.DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_ALPHANUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_NUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_UNSPECIFIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_EXTERNAL_STORAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_LOW_LEVEL_FORMAT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="Dialog"
extends="java.lang.Object"
abstract="false"
@@ -20739,6 +21355,19 @@
<parameter name="onKeyListener" type="android.content.DialogInterface.OnKeyListener">
</parameter>
</method>
+<method name="setOnShowListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.content.DialogInterface.OnShowListener">
+</parameter>
+</method>
<method name="setOwnerActivity"
return="void"
abstract="false"
@@ -32563,6 +33192,17 @@
visibility="public"
>
</field>
+<field name="DEVICE_POLICY_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_policy&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="DROPBOX_SERVICE"
type="java.lang.String"
transient="false"
@@ -34002,6 +34642,27 @@
</parameter>
</method>
</interface>
+<interface name="DialogInterface.OnShowListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onShow"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dialog" type="android.content.DialogInterface">
+</parameter>
+</method>
+</interface>
<class name="Entity"
extends="java.lang.Object"
abstract="false"
@@ -51735,17 +52396,6 @@
<exception name="SQLException" type="android.database.SQLException">
</exception>
</method>
-<method name="resetCompiledSqlCache"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="setLocale"
return="void"
abstract="false"
@@ -51772,19 +52422,6 @@
<parameter name="lockingEnabled" type="boolean">
</parameter>
</method>
-<method name="setMaxSqlCacheSize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cacheSize" type="int">
-</parameter>
-</method>
<method name="setMaximumSize"
return="long"
abstract="false"
@@ -119426,6 +120063,19 @@
visibility="public"
>
</constructor>
+<method name="getLastOutgoingCall"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
<field name="CACHED_NAME"
type="java.lang.String"
transient="false"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ad6540a..e65cdf1 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -34,12 +34,14 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/mediametadataretriever.h>
using namespace android;
static long gNumRepetitions;
static long gMaxNumFrames; // 0 means decode all available.
static long gReproduceBug; // if not -1.
+static bool gPreferSoftwareCodec;
static int64_t getNowUs() {
struct timeval tv;
@@ -59,7 +61,9 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) {
rawSource = source;
} else {
rawSource = OMXCodec::Create(
- client->interface(), meta, false /* createEncoder */, source);
+ client->interface(), meta, false /* createEncoder */, source,
+ NULL /* matchComponentName */,
+ gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
if (rawSource == NULL) {
fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
@@ -219,6 +223,8 @@ static void usage(const char *me) {
fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
fprintf(stderr, " -b bug to reproduce\n");
fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
+ fprintf(stderr, " -t(humbnail) extract video thumbnail\n");
+ fprintf(stderr, " -s(oftware) prefer software codec\n");
}
int main(int argc, char **argv) {
@@ -227,12 +233,14 @@ int main(int argc, char **argv) {
bool audioOnly = false;
bool listComponents = false;
bool dumpProfiles = false;
+ bool extractThumbnail = false;
gNumRepetitions = 1;
gMaxNumFrames = 0;
gReproduceBug = -1;
+ gPreferSoftwareCodec = false;
int res;
- while ((res = getopt(argc, argv, "han:lm:b:p")) >= 0) {
+ while ((res = getopt(argc, argv, "han:lm:b:pts")) >= 0) {
switch (res) {
case 'a':
{
@@ -274,6 +282,18 @@ int main(int argc, char **argv) {
break;
}
+ case 't':
+ {
+ extractThumbnail = true;
+ break;
+ }
+
+ case 's':
+ {
+ gPreferSoftwareCodec = true;
+ break;
+ }
+
case '?':
case 'h':
default:
@@ -288,6 +308,34 @@ int main(int argc, char **argv) {
argc -= optind;
argv += optind;
+ if (extractThumbnail) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ sp<IMediaMetadataRetriever> retriever =
+ service->createMetadataRetriever(getpid());
+
+ CHECK(retriever != NULL);
+
+ for (int k = 0; k < argc; ++k) {
+ const char *filename = argv[k];
+
+ CHECK_EQ(retriever->setDataSource(filename), OK);
+ CHECK_EQ(retriever->setMode(METADATA_MODE_FRAME_CAPTURE_ONLY), OK);
+
+ sp<IMemory> mem = retriever->captureFrame();
+
+ printf("captureFrame(%s) => %s\n",
+ filename, mem != NULL ? "OK" : "FAILED");
+ }
+
+ return 0;
+ }
+
if (dumpProfiles) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -389,7 +437,8 @@ int main(int argc, char **argv) {
sp<MetaData> meta;
size_t i;
for (i = 0; i < numTracks; ++i) {
- meta = extractor->getTrackMetaData(i);
+ meta = extractor->getTrackMetaData(
+ i, MediaExtractor::kIncludeExtensiveMetaData);
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
@@ -403,6 +452,12 @@ int main(int argc, char **argv) {
}
}
+ int64_t thumbTimeUs;
+ if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+ printf("thumbnailTime: %lld us (%.2f secs)\n",
+ thumbTimeUs, thumbTimeUs / 1E6);
+ }
+
mediaSource = extractor->getTrack(i);
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 3bbfce8..414d963 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -254,12 +254,12 @@ public class AccountManager {
* The future result is a {@link Boolean} that is true if the account exists and has the
* specified features.
*/
- public AccountManagerFuture<Boolean> testHasFeatures(final Account account,
+ public AccountManagerFuture<Boolean> hasFeatures(final Account account,
final String[] features,
AccountManagerCallback<Boolean> callback, Handler handler) {
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
- mService.testHasFeatures(mResponse, account, features);
+ mService.hasFeatures(mResponse, account, features);
}
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index f5166c2..ee26d3c 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -449,7 +449,7 @@ public class AccountManagerService
return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
}
- public void testHasFeatures(IAccountManagerResponse response,
+ public void hasFeatures(IAccountManagerResponse response,
Account account, String[] features) {
checkReadAccountsPermission();
long identityToken = clearCallingIdentity();
@@ -501,7 +501,7 @@ public class AccountManagerService
}
protected String toDebugString(long now) {
- return super.toDebugString(now) + ", testHasFeatures"
+ return super.toDebugString(now) + ", hasFeatures"
+ ", " + mAccount
+ ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index cbd26ee..36a5653 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -31,8 +31,7 @@ interface IAccountManager {
String getUserData(in Account account, String key);
AuthenticatorDescription[] getAuthenticatorTypes();
Account[] getAccounts(String accountType);
- void testHasFeatures(in IAccountManagerResponse response, in Account account,
- in String[] features);
+ void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features);
void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
boolean addAccount(in Account account, String password, in Bundle extras);
void removeAccount(in IAccountManagerResponse response, in Account account);
@@ -47,7 +46,7 @@ interface IAccountManager {
String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
in Bundle options);
void addAcount(in IAccountManagerResponse response, String accountType,
- String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+ String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
in Bundle options);
void updateCredentials(in IAccountManagerResponse response, in Account account,
String authTokenType, boolean expectActivityLaunch, in Bundle options);
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index d89b877..fe05393 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -186,6 +186,7 @@ class ApplicationContext extends Context {
private boolean mRestricted;
private AccountManager mAccountManager; // protected by mSync
private DropBoxManager mDropBoxManager = null;
+ private DevicePolicyManager mDevicePolicyManager = null;
private final Object mSync = new Object();
@@ -895,6 +896,8 @@ class ApplicationContext extends Context {
return getWallpaperManager();
} else if (DROPBOX_SERVICE.equals(name)) {
return getDropBoxManager();
+ } else if (DEVICE_POLICY_SERVICE.equals(name)) {
+ return getDevicePolicyManager();
}
return null;
@@ -1064,6 +1067,16 @@ class ApplicationContext extends Context {
return mDropBoxManager;
}
+ private DevicePolicyManager getDevicePolicyManager() {
+ synchronized (mSync) {
+ if (mDevicePolicyManager == null) {
+ mDevicePolicyManager = new DevicePolicyManager(this,
+ mMainThread.getHandler());
+ }
+ }
+ return mDevicePolicyManager;
+ }
+
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
new file mode 100644
index 0000000..4da3fee
--- /dev/null
+++ b/core/java/android/app/DeviceAdmin.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Base class for implementing a device administration component. This
+ * class provides a convenience for interpreting the raw intent actions
+ * that are sent by the system.
+ *
+ * <p>When publishing your DeviceAdmin subclass as a receiver, it must
+ * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission. A typical
+ * manifest entry would look like:</p>
+ *
+ * <pre>{@include development/samples/ApiDemos/AndroidManifest.xml
+ * device_admin_declaration}</pre>
+ *
+ * <p>The meta-data referenced here provides addition information specific
+ * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
+ * A typical file would be:</p>
+ *
+ * <pre>{@include development/samples/ApiDemos/res/xml/sample_device_admin.xml
+ * meta_data}</pre>
+ */
+public class DeviceAdmin extends BroadcastReceiver {
+ private static String TAG = "DevicePolicy";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ /**
+ * This is the primary action that a device administrator must implement to be
+ * allowed to manage a device. This will be set to the receiver
+ * when the user enables it for administration. You will generally
+ * handle this in {@link DeviceAdmin#onEnabled(Context, Intent)}. To be
+ * supported, the receiver must also require the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission so
+ * that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_ADMIN_ENABLED
+ = "android.app.action.DEVICE_ADMIN_ENABLED";
+
+ /**
+ * Action sent to a device administrator when the user has disabled
+ * it. Upon return, the application no longer has access to the
+ * protected device policy manager APIs. You will generally
+ * handle this in {@link DeviceAdmin#onDisabled(Context, Intent)}. Note
+ * that this action will be
+ * sent the receiver regardless of whether it is explicitly listed in
+ * its intent filter.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_ADMIN_DISABLED
+ = "android.app.action.DEVICE_ADMIN_DISABLED";
+
+ /**
+ * Action sent to a device administrator when the user has changed the
+ * password of their device. You can at this point check the characteristics
+ * of the new password with {@link DevicePolicyManager#getActivePasswordMode()
+ * DevicePolicyManager.getActivePasswordMode()} and
+ * {@link DevicePolicyManager#getActiveMinimumPasswordLength()
+ * DevicePolicyManager.getActiveMinimumPasswordLength()}. You will generally
+ * handle this in {@link DeviceAdmin#onPasswordChanged(Context, Intent)}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_CHANGED
+ = "android.app.action.ACTION_PASSWORD_CHANGED";
+
+ /**
+ * Action sent to a device administrator when the user has failed at
+ * attempted to enter the password. You can at this point check the
+ * number of failed password attempts there have been with
+ * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+ * DevicePolicyManager.getCurrentFailedPasswordAttempts()}. You will generally
+ * handle this in {@link DeviceAdmin#onPasswordFailed(Context, Intent)}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_FAILED
+ = "android.app.action.ACTION_PASSWORD_FAILED";
+
+ /**
+ * Action sent to a device administrator when the user has successfully
+ * entered their password, after failing one or more times.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PASSWORD_SUCCEEDED
+ = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
+
+ /**
+ * Name under which an DevicePolicy component publishes information
+ * about itself. This meta-data must reference an XML resource containing
+ * a device-admin tag. XXX TO DO: describe syntax.
+ */
+ public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
+
+ private DevicePolicyManager mManager;
+ private ComponentName mWho;
+
+ /**
+ * Retrieve the DevicePolicyManager interface for this administrator to work
+ * with the system.
+ */
+ public DevicePolicyManager getManager(Context context) {
+ if (mManager != null) {
+ return mManager;
+ }
+ mManager = (DevicePolicyManager)context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return mManager;
+ }
+
+ /**
+ * Retrieve the ComponentName describing who this device administrator is, for
+ * use in {@link DevicePolicyManager} APIs that require the administrator to
+ * identify itself.
+ */
+ public ComponentName getWho(Context context) {
+ if (mWho != null) {
+ return mWho;
+ }
+ mWho = new ComponentName(context, getClass());
+ return mWho;
+ }
+
+ /**
+ * Called after the administrator is first enabled, as a result of
+ * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}. At this point you
+ * can use {@link DevicePolicyManager} to set your desired policies.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onEnabled(Context context, Intent intent) {
+ }
+
+ /**
+ * Called prior to the administrator being disabled, as a result of
+ * receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}. Upon return, you
+ * can no longer use the protected parts of the {@link DevicePolicyManager}
+ * API.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onDisabled(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has changed their password, as a result of
+ * receiving {@link #ACTION_PASSWORD_CHANGED}. At this point you
+ * can use {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+ * DevicePolicyManager.getCurrentFailedPasswordAttempts()}
+ * to retrieve the active password characteristics.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordChanged(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has failed at entering their current password, as a result of
+ * receiving {@link #ACTION_PASSWORD_FAILED}. At this point you
+ * can use {@link DevicePolicyManager} to retrieve the number of failed
+ * password attempts.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordFailed(Context context, Intent intent) {
+ }
+
+ /**
+ * Called after the user has succeeded at entering their current password,
+ * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}. This will
+ * only be received the first time they succeed after having previously
+ * failed.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onPasswordSucceeded(Context context, Intent intent) {
+ }
+
+ /**
+ * Intercept standard device administrator broadcasts. Implementations
+ * should not override this method; it is better to implement the
+ * convenience callbacks for each action.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_PASSWORD_CHANGED.equals(action)) {
+ onPasswordChanged(context, intent);
+ } else if (ACTION_PASSWORD_FAILED.equals(action)) {
+ onPasswordFailed(context, intent);
+ } else if (ACTION_PASSWORD_SUCCEEDED.equals(action)) {
+ onPasswordSucceeded(context, intent);
+ } else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
+ onEnabled(context, intent);
+ } else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {
+ onDisabled(context, intent);
+ }
+ }
+}
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
new file mode 100644
index 0000000..eac6e46
--- /dev/null
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a device administrator
+ * component.
+ */
+public final class DeviceAdminInfo implements Parcelable {
+ static final String TAG = "DeviceAdminInfo";
+
+ /**
+ * The BroadcastReceiver that implements this device admin component.
+ */
+ final ResolveInfo mReceiver;
+
+ /**
+ * Constructor.
+ *
+ * @param context The Context in which we are parsing the device admin.
+ * @param receiver The ResolveInfo returned from the package manager about
+ * this device admin's component.
+ */
+ public DeviceAdminInfo(Context context, ResolveInfo receiver)
+ throws XmlPullParserException, IOException {
+ mReceiver = receiver;
+ ActivityInfo ai = receiver.activityInfo;
+
+ PackageManager pm = context.getPackageManager();
+
+ XmlResourceParser parser = null;
+ try {
+ parser = ai.loadXmlMetaData(pm, DeviceAdmin.DEVICE_ADMIN_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No "
+ + DeviceAdmin.DEVICE_ADMIN_META_DATA + " meta-data");
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"device-admin".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with device-admin tag");
+ }
+
+ TypedArray sa = context.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.Wallpaper);
+
+ sa.recycle();
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ DeviceAdminInfo(Parcel source) {
+ mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Return the .apk package that implements this device admin.
+ */
+ public String getPackageName() {
+ return mReceiver.activityInfo.packageName;
+ }
+
+ /**
+ * Return the class name of the receiver component that implements
+ * this device admin.
+ */
+ public String getReceiverName() {
+ return mReceiver.activityInfo.name;
+ }
+
+ /**
+ * Return the raw information about the receiver implementing this
+ * device admin. Do not modify the returned object.
+ */
+ public ActivityInfo getActivityInfo() {
+ return mReceiver.activityInfo;
+ }
+
+ /**
+ * Return the component of the receiver that implements this device admin.
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mReceiver.activityInfo.packageName,
+ mReceiver.activityInfo.name);
+ }
+
+ /**
+ * Load the user-displayed label for this device admin.
+ *
+ * @param pm Supply a PackageManager used to load the device admin's
+ * resources.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mReceiver.loadLabel(pm);
+ }
+
+ /**
+ * Load the user-displayed icon for this device admin.
+ *
+ * @param pm Supply a PackageManager used to load the device admin's
+ * resources.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mReceiver.loadIcon(pm);
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "Receiver:");
+ mReceiver.dump(pw, prefix + " ");
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ mReceiver.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<DeviceAdminInfo> CREATOR =
+ new Parcelable.Creator<DeviceAdminInfo>() {
+ public DeviceAdminInfo createFromParcel(Parcel source) {
+ return new DeviceAdminInfo(source);
+ }
+
+ public DeviceAdminInfo[] newArray(int size) {
+ return new DeviceAdminInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
new file mode 100644
index 0000000..4fdfe0a
--- /dev/null
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Public interface for managing policies enforced on a device. Most clients
+ * of this class must have published a {@link DeviceAdmin} that the user
+ * has currently enabled.
+ */
+public class DevicePolicyManager {
+ private static String TAG = "DevicePolicyManager";
+ private static boolean DEBUG = false;
+ private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final IDevicePolicyManager mService;
+
+ /*package*/ DevicePolicyManager(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mService = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ }
+
+ /**
+ * Activity action: ask the user to add a new device administrator to the system.
+ * The desired policy is the ComponentName of the policy in the
+ * {@link #EXTRA_DEVICE_ADMIN} extra field. This will invoke a UI to
+ * bring the user through adding the device administrator to the system (or
+ * allowing them to reject it).
+ *
+ * <p>Note: the current platform can only have one device administrator
+ * active at a time. If you make this request while there is already
+ * an active administrator, this new request will be canceled automatically.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ADD_DEVICE_ADMIN
+ = "android.app.action.ADD_DEVICE_ADMIN";
+
+ /**
+ * The ComponentName of the administrator component.
+ *
+ * @see #ACTION_ADD_DEVICE_ADMIN
+ */
+ public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+
+ /**
+ * Activity action: have the user enter a new password. This activity
+ * should be launched after using {@link #setPasswordMode(ComponentName, int)}
+ * or {@link #setMinimumPasswordLength(ComponentName, int)} to have the
+ * user enter a new password that meets the current requirements. If the
+ * current password is sufficient, the activity will exit immediately without
+ * being displayed to the user. Upon receiving a result from this activity,
+ * you can check the new password characteristics to see if they are
+ * sufficient.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SET_NEW_PASSWORD
+ = "android.app.action.SET_NEW_PASSWORD";
+
+ /**
+ * Return true if the given administrator component is currently
+ * active (enabled) in the system.
+ */
+ public boolean isAdminActive(ComponentName who) {
+ if (mService != null) {
+ try {
+ return who.equals(mService.getActiveAdmin());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a current administration component. This can only be called
+ * by the application that owns the administration component; if you
+ * try to remove someone else's component, a security exception will be
+ * thrown.
+ */
+ public void removeActiveAdmin(ComponentName who) {
+ if (mService != null) {
+ try {
+ mService.removeActiveAdmin(who);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Constant for {@link #setPasswordMode}: the policy has no requirements
+ * for the password.
+ */
+ public static final int PASSWORD_MODE_UNSPECIFIED = 0;
+
+ /**
+ * Constant for {@link #setPasswordMode}: the user must have at least a
+ * numeric password.
+ */
+ public static final int PASSWORD_MODE_NUMERIC = 1000;
+
+ /**
+ * Constant for {@link #setPasswordMode}: the user must have at least an
+ * alphanumeric password.
+ */
+ public static final int PASSWORD_MODE_ALPHANUMERIC = 2000;
+
+ /**
+ * Called by an application that is administering the device to set the
+ * password restrictions it is imposing. After setting this, the user
+ * will not be able to enter a new password that is not at least as
+ * restrictive as what has been set. Note that the current password
+ * will remain until the user has set a new one, so the change does not
+ * take place immediately. To prompt the user for a new password, use
+ * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param mode The new desired mode. One of
+ * {@link #PASSWORD_MODE_UNSPECIFIED}, {@link #PASSWORD_MODE_NUMERIC},
+ * or {@link #PASSWORD_MODE_ALPHANUMERIC}.
+ */
+ public void setPasswordMode(ComponentName admin, int mode) {
+ if (mService != null) {
+ try {
+ mService.setPasswordMode(admin, mode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current password mode that is in effect due to all
+ * device admins.
+ */
+ public int getPasswordMode() {
+ if (mService != null) {
+ try {
+ return mService.getPasswordMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return PASSWORD_MODE_UNSPECIFIED;
+ }
+
+ /**
+ * Retrieve the password mode associated with the last password the
+ * user selected.
+ */
+ public int getActivePasswordMode() {
+ if (mService != null) {
+ try {
+ return mService.getActivePasswordMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return PASSWORD_MODE_UNSPECIFIED;
+ }
+
+ /**
+ * Called by an application that is administering the device to set the
+ * minimum allowed password length. After setting this, the user
+ * will not be able to enter a new password that is not at least as
+ * restrictive as what has been set. Note that the current password
+ * will remain until the user has set a new one, so the change does not
+ * take place immediately. To prompt the user for a new password, use
+ * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+ * constraint is only imposed if the administrator has also requested either
+ * {@link #PASSWORD_MODE_NUMERIC} or {@link #PASSWORD_MODE_ALPHANUMERIC}
+ * with {@link #setPasswordMode}.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param length The new desired minimum password length. A value of 0
+ * means there is no restriction.
+ */
+ public void setMinimumPasswordLength(ComponentName admin, int length) {
+ if (mService != null) {
+ try {
+ mService.setMinimumPasswordLength(admin, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current minimum password length that is in effect due to all
+ * device admins.
+ */
+ public int getMinimumPasswordLength() {
+ if (mService != null) {
+ try {
+ return mService.getMinimumPasswordLength();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the password length associated with the last password the
+ * user selected.
+ */
+ public int getActiveMinimumPasswordLength() {
+ if (mService != null) {
+ try {
+ return mService.getActiveMinimumPasswordLength();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the number of times the user has failed at entering a
+ * password since that last successful password entry.
+ */
+ public int getCurrentFailedPasswordAttempts() {
+ if (mService != null) {
+ try {
+ return mService.getCurrentFailedPasswordAttempts();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Called by an application that is administering the device to set the
+ * maximum time for user activity until the device will lock. This limits
+ * the length that the user can set. It takes effect immediately.
+ *
+ * @param admin Which {@link DeviceAdmin} this request is associated with.
+ * @param timeMs The new desired maximum time to lock in milliseconds.
+ * A value of 0 means there is no restriction.
+ */
+ public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
+ if (mService != null) {
+ try {
+ mService.setMaximumTimeToLock(admin, timeMs);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current maximum time to lock that is in effect due to all
+ * device admins. Returns 0 if no maximum is set.
+ */
+ public long getMaximumTimeToLock() {
+ if (mService != null) {
+ try {
+ return mService.getMaximumTimeToLock();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Constant for {@link #wipeData}: perform a low-level format of data
+ * storage.
+ */
+ public static final int WIPE_LOW_LEVEL_FORMAT = 0x0001;
+
+ /**
+ * Constant for {@link #wipeData}: also wipe any external storage.
+ */
+ public static final int WIPE_EXTERNAL_STORAGE = 0x0002;
+
+ /**
+ * Ask the user date be wiped. This will cause the device to reboot,
+ * erasing all user data while next booting up.
+ *
+ * @param flags Bit mask of additional options: currently
+ * {@link #WIPE_LOW_LEVEL_FORMAT} and {@link #WIPE_EXTERNAL_STORAGE}.
+ */
+ public void wipeData(int flags) {
+ if (mService != null) {
+ try {
+ mService.wipeData(flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setActiveAdmin(ComponentName policyReceiver) {
+ if (mService != null) {
+ try {
+ mService.setActiveAdmin(policyReceiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ComponentName getActiveAdmin() {
+ if (mService != null) {
+ try {
+ return mService.getActiveAdmin();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public DeviceAdminInfo getActiveAdminInfo() {
+ ComponentName cn = getActiveAdmin();
+ if (cn == null) {
+ return null;
+ }
+
+ ActivityInfo ai;
+ try {
+ ai = mContext.getPackageManager().getReceiverInfo(cn,
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+ return null;
+ }
+
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+
+ try {
+ return new DeviceAdminInfo(mContext, ri);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Unable to parse device policy " + cn, e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to parse device policy " + cn, e);
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setActivePasswordState(int mode, int length) {
+ if (mService != null) {
+ try {
+ mService.setActivePasswordState(mode, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportFailedPasswordAttempt() {
+ if (mService != null) {
+ try {
+ mService.reportFailedPasswordAttempt();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportSuccessfulPasswordAttempt() {
+ if (mService != null) {
+ try {
+ mService.reportSuccessfulPasswordAttempt();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index fa5d4a8..ed38240 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -995,8 +995,7 @@ public class Dialog implements DialogInterface, Window.Callback,
/**
* Sets a listener to be invoked when the dialog is shown.
- *
- * @hide Pending API council approval
+ * @param listener The {@link DialogInterface.OnShowListener} to use.
*/
public void setOnShowListener(OnShowListener listener) {
if (listener != null) {
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
new file mode 100644
index 0000000..f62647f
--- /dev/null
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app;
+
+import android.content.ComponentName;
+
+/**
+ * Internal IPC interface to the device policy service.
+ * {@hide}
+ */
+interface IDevicePolicyManager {
+ void setPasswordMode(in ComponentName who, int mode);
+ int getPasswordMode();
+ int getActivePasswordMode();
+
+ void setMinimumPasswordLength(in ComponentName who, int length);
+ int getMinimumPasswordLength();
+ int getActiveMinimumPasswordLength();
+
+ int getCurrentFailedPasswordAttempts();
+
+ void setMaximumTimeToLock(in ComponentName who, long timeMs);
+ long getMaximumTimeToLock();
+
+ void wipeData(int flags);
+
+ void setActiveAdmin(in ComponentName policyReceiver);
+ ComponentName getActiveAdmin();
+ void removeActiveAdmin(in ComponentName policyReceiver);
+
+ void setActivePasswordState(int mode, int length);
+ void reportFailedPasswordAttempt();
+ void reportSuccessfulPasswordAttempt();
+}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 981145b..d25d670 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1838,8 +1838,10 @@ public class SearchManager
*
* TODO: Doing this every time we start global search is inefficient. Will fix that once
* we have settled on the right mechanism for finding the global search activity.
+ *
+ * @hide
*/
- private ComponentName getGlobalSearchActivity() {
+ public ComponentName getGlobalSearchActivity() {
Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> activities =
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 1034fab..1612ac9 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.app;
import org.xmlpull.v1.XmlPullParser;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0fafe5d..0b83f03 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1132,6 +1132,7 @@ public abstract class Context {
* you're running long tasks.
*/
public static final String POWER_SERVICE = "power";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.WindowManager} for accessing the system's window
@@ -1141,6 +1142,7 @@ public abstract class Context {
* @see android.view.WindowManager
*/
public static final String WINDOW_SERVICE = "window";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.LayoutInflater} for inflating layout resources in this
@@ -1150,6 +1152,7 @@ public abstract class Context {
* @see android.view.LayoutInflater
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.accounts.AccountManager} for receiving intents at a
@@ -1159,6 +1162,7 @@ public abstract class Context {
* @see android.accounts.AccountManager
*/
public static final String ACCOUNT_SERVICE = "account";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.ActivityManager} for interacting with the global
@@ -1168,6 +1172,7 @@ public abstract class Context {
* @see android.app.ActivityManager
*/
public static final String ACTIVITY_SERVICE = "activity";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.AlarmManager} for receiving intents at a
@@ -1177,6 +1182,7 @@ public abstract class Context {
* @see android.app.AlarmManager
*/
public static final String ALARM_SERVICE = "alarm";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.NotificationManager} for informing the user of
@@ -1186,6 +1192,7 @@ public abstract class Context {
* @see android.app.NotificationManager
*/
public static final String NOTIFICATION_SERVICE = "notification";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.view.accessibility.AccessibilityManager} for giving the user
@@ -1195,6 +1202,7 @@ public abstract class Context {
* @see android.view.accessibility.AccessibilityManager
*/
public static final String ACCESSIBILITY_SERVICE = "accessibility";
+
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.NotificationManager} for controlling keyguard.
@@ -1203,6 +1211,7 @@ public abstract class Context {
* @see android.app.KeyguardManager
*/
public static final String KEYGUARD_SERVICE = "keyguard";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.location.LocationManager} for controlling location
@@ -1212,6 +1221,7 @@ public abstract class Context {
* @see android.location.LocationManager
*/
public static final String LOCATION_SERVICE = "location";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.SearchManager} for handling searches.
@@ -1220,6 +1230,7 @@ public abstract class Context {
* @see android.app.SearchManager
*/
public static final String SEARCH_SERVICE = "search";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.hardware.SensorManager} for accessing sensors.
@@ -1228,6 +1239,7 @@ public abstract class Context {
* @see android.hardware.SensorManager
*/
public static final String SENSOR_SERVICE = "sensor";
+
/**
* Use with {@link #getSystemService} to retrieve a
* com.android.server.WallpaperService for accessing wallpapers.
@@ -1235,6 +1247,7 @@ public abstract class Context {
* @see #getSystemService
*/
public static final String WALLPAPER_SERVICE = "wallpaper";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.os.Vibrator} for interacting with the vibration hardware.
@@ -1243,6 +1256,7 @@ public abstract class Context {
* @see android.os.Vibrator
*/
public static final String VIBRATOR_SERVICE = "vibrator";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.StatusBarManager} for interacting with the status bar.
@@ -1340,6 +1354,15 @@ public abstract class Context {
public static final String DROPBOX_SERVICE = "dropbox";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.app.DevicePolicyManager} for working with global
+ * device policy management.
+ *
+ * @see #getSystemService
+ */
+ public static final String DEVICE_POLICY_SERVICE = "device_policy";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/DialogInterface.java b/core/java/android/content/DialogInterface.java
index 9f1036e..947eac6 100644
--- a/core/java/android/content/DialogInterface.java
+++ b/core/java/android/content/DialogInterface.java
@@ -94,7 +94,6 @@ public interface DialogInterface {
/**
* Interface used to allow the creator of a dialog to run some code when the
* dialog is shown.
- * @hide Pending API council approval
*/
interface OnShowListener {
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b27cd6c..3e8a55f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -242,9 +242,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
* {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
* {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
- * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}.
- * {@link #FLAG_FWD_LOCKED},
- * {@link #FLAG_ON_SDCARD}
+ * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}
*/
public int flags = 0;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index f310586..fb44a62 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -243,9 +243,12 @@ public class SQLiteDatabase extends SQLiteClosable {
* (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
* most of the apps don't use "?" syntax in their sql, caching is not useful for them.
*/
- private Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
- private int mMaxSqlCacheSize = 0; // no caching by default
- private static final int MAX_SQL_CACHE_SIZE = 1000;
+ /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
+ /**
+ * @hide
+ */
+ public static final int MAX_SQL_CACHE_SIZE = 250;
+ private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
/** maintain stats about number of cache hits and misses */
private int mNumCacheHits;
@@ -828,6 +831,15 @@ public class SQLiteDatabase extends SQLiteClosable {
}
private void closeClosable() {
+ /* deallocate all compiled sql statement objects from mCompiledQueries cache.
+ * this should be done before de-referencing all {@link SQLiteClosable} objects
+ * from this database object because calling
+ * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
+ * to be closed. sqlite doesn't let a database close if there are
+ * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
+ */
+ deallocCachedSqlStatements();
+
Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<SQLiteClosable, Object> entry = iter.next();
@@ -836,13 +848,6 @@ public class SQLiteDatabase extends SQLiteClosable {
program.onAllReferencesReleasedFromContainer();
}
}
-
- // finalize all compiled sql statement objects in compiledQueries cache
- synchronized (mCompiledQueries) {
- for (SQLiteCompiledSql compiledStatement : mCompiledQueries.values()) {
- compiledStatement.releaseSqlStatement();
- }
- }
}
/**
@@ -1781,30 +1786,61 @@ public class SQLiteDatabase extends SQLiteClosable {
return mPath;
}
- /**
- * set the max size of the compiled sql cache for this database after purging the cache.
- * (size of the cache = number of compiled-sql-statements stored in the cache)
- *
- * synchronized because we don't want t threads to change cache size at the same time.
- * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
- */
- public void setMaxSqlCacheSize(int cacheSize) {
- synchronized(mCompiledQueries) {
- resetCompiledSqlCache();
- mMaxSqlCacheSize = (cacheSize > MAX_SQL_CACHE_SIZE) ? MAX_SQL_CACHE_SIZE
- : (cacheSize < 0) ? 0 : cacheSize;
+
+
+ /* package */ void logTimeStat(String sql, long beginNanos) {
+ // Sample fast queries in proportion to the time taken.
+ // Quantize the % first, so the logged sampling probability
+ // exactly equals the actual sampling rate for this query.
+
+ int samplePercent;
+ long nanos = Debug.threadCpuTimeNanos() - beginNanos;
+ if (nanos >= QUERY_LOG_TIME_IN_NANOS) {
+ samplePercent = 100;
+ } else {
+ samplePercent = (int) (100 * nanos / QUERY_LOG_TIME_IN_NANOS) + 1;
+ if (mRandom.nextInt(100) >= samplePercent) return;
}
+
+ if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
+
+ // ActivityThread.currentPackageName() only returns non-null if the
+ // current thread is an application main thread. This parameter tells
+ // us whether an event loop is blocked, and if so, which app it is.
+ //
+ // Sadly, there's no fast way to determine app name if this is *not* a
+ // main thread, or when we are invoked via Binder (e.g. ContentProvider).
+ // Hopefully the full path to the database will be informative enough.
+
+ String blockingPackage = ActivityThread.currentPackageName();
+ if (blockingPackage == null) blockingPackage = "";
+
+ int millis = (int) (nanos / 1000000);
+ EventLog.writeEvent(EVENT_DB_OPERATION, mPath, sql, millis, blockingPackage, samplePercent);
}
/**
- * remove everything from the compiled sql cache
+ * Sets the locale for this database. Does nothing if this database has
+ * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
+ * @throws SQLException if the locale could not be set. The most common reason
+ * for this is that there is no collator available for the locale you requested.
+ * In this case the database remains unchanged.
*/
- public void resetCompiledSqlCache() {
- synchronized(mCompiledQueries) {
- mCompiledQueries.clear();
+ public void setLocale(Locale locale) {
+ lock();
+ try {
+ native_setLocale(locale.toString(), mFlags);
+ } finally {
+ unlock();
}
}
+ /*
+ * ============================================================================
+ *
+ * The following methods deal with compiled-sql cache
+ * ============================================================================
+ */
/**
* adds the given sql and its compiled-statement-id-returned-by-sqlite to the
* cache of compiledQueries attached to 'this'.
@@ -1812,16 +1848,14 @@ public class SQLiteDatabase extends SQLiteClosable {
* if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
* the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
* mapping is NOT replaced with the new mapping).
- *
- * @return true if the given obj is added to cache. false otherwise.
*/
- /* package */ boolean addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
+ /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
if (mMaxSqlCacheSize == 0) {
// for this database, there is no cache of compiled sql.
if (SQLiteDebug.DEBUG_SQL_CACHE) {
Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
}
- return false;
+ return;
}
SQLiteCompiledSql compiledSql = null;
@@ -1829,30 +1863,42 @@ public class SQLiteDatabase extends SQLiteClosable {
// don't insert the new mapping if a mapping already exists
compiledSql = mCompiledQueries.get(sql);
if (compiledSql != null) {
- return false;
+ return;
}
// 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 be cause a big GC activity
+ * 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.
*/
+ Log.w(TAG, "Too many sql statements in database cache. Make sure your sql " +
+ "statements are using prepared-sql-statement syntax with '?' for " +
+ "bindargs, instead of using actual values");
Set<String> keySet = mCompiledQueries.keySet();
for (String s : keySet) {
mCompiledQueries.remove(s);
break;
}
}
- compiledSql = new SQLiteCompiledSql(this, sql);
- mCompiledQueries.put(sql, compiledSql);
+ mCompiledQueries.put(sql, compiledStatement);
}
if (SQLiteDebug.DEBUG_SQL_CACHE) {
Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" +
sql);
}
- return true;
+ return;
+ }
+
+
+ private void deallocCachedSqlStatements() {
+ synchronized (mCompiledQueries) {
+ for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
+ compiledSql.releaseSqlStatement();
+ }
+ mCompiledQueries.clear();
+ }
}
/**
@@ -1887,51 +1933,67 @@ public class SQLiteDatabase extends SQLiteClosable {
return compiledStatement;
}
- /* package */ void logTimeStat(String sql, long beginNanos) {
- // Sample fast queries in proportion to the time taken.
- // Quantize the % first, so the logged sampling probability
- // exactly equals the actual sampling rate for this query.
-
- int samplePercent;
- long nanos = Debug.threadCpuTimeNanos() - beginNanos;
- if (nanos >= QUERY_LOG_TIME_IN_NANOS) {
- samplePercent = 100;
- } else {
- samplePercent = (int) (100 * nanos / QUERY_LOG_TIME_IN_NANOS) + 1;
- if (mRandom.nextInt(100) >= samplePercent) return;
+ /**
+ * returns true if the given sql is cached in compiled-sql cache.
+ * @hide
+ */
+ public boolean isInCompiledSqlCache(String sql) {
+ synchronized(mCompiledQueries) {
+ return mCompiledQueries.containsKey(sql);
}
+ }
- if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
-
- // ActivityThread.currentPackageName() only returns non-null if the
- // current thread is an application main thread. This parameter tells
- // us whether an event loop is blocked, and if so, which app it is.
- //
- // Sadly, there's no fast way to determine app name if this is *not* a
- // main thread, or when we are invoked via Binder (e.g. ContentProvider).
- // Hopefully the full path to the database will be informative enough.
+ /**
+ * purges the given sql from the compiled-sql cache.
+ * @hide
+ */
+ public void purgeFromCompiledSqlCache(String sql) {
+ synchronized(mCompiledQueries) {
+ mCompiledQueries.remove(sql);
+ }
+ }
- String blockingPackage = ActivityThread.currentPackageName();
- if (blockingPackage == null) blockingPackage = "";
+ /**
+ * remove everything from the compiled sql cache
+ * @hide
+ */
+ public void resetCompiledSqlCache() {
+ synchronized(mCompiledQueries) {
+ mCompiledQueries.clear();
+ }
+ }
- int millis = (int) (nanos / 1000000);
- EventLog.writeEvent(EVENT_DB_OPERATION, mPath, sql, millis, blockingPackage, samplePercent);
+ /**
+ * return the current maxCacheSqlCacheSize
+ * @hide
+ */
+ public synchronized int getMaxSqlCacheSize() {
+ return mMaxSqlCacheSize;
}
/**
- * Sets the locale for this database. Does nothing if this database has
- * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
- * @throws SQLException if the locale could not be set. The most common reason
- * for this is that there is no collator available for the locale you requested.
- * In this case the database remains unchanged.
+ * set the max size of the compiled sql cache for this database after purging the cache.
+ * (size of the cache = number of compiled-sql-statements stored in the cache).
+ *
+ * max cache size can ONLY be increased from its current size (default = 0).
+ * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
+ * then IllegalStateException is thrown
+ *
+ * synchronized because we don't want t threads to change cache size at the same time.
+ * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
+ * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
+ * < the value set with previous setMaxSqlCacheSize() call.
+ *
+ * @hide
*/
- public void setLocale(Locale locale) {
- lock();
- try {
- native_setLocale(locale.toString(), mFlags);
- } finally {
- unlock();
+ public synchronized void setMaxSqlCacheSize(int cacheSize) {
+ if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+ throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+ } else if (cacheSize < mMaxSqlCacheSize) {
+ throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+ "set with previous setMaxSqlCacheSize() call.");
}
+ mMaxSqlCacheSize = cacheSize;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index edc15cb..00b0a86 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -37,15 +37,13 @@ public abstract class SQLiteProgram extends SQLiteClosable {
protected int nHandle = 0;
/**
- * the compiledSql object for the given sql statement.
+ * the SQLiteCompiledSql object for the given sql statement.
*/
- private SQLiteCompiledSql compiledSql;
- private boolean myCompiledSqlIsInCache;
+ private SQLiteCompiledSql mCompiledSql;
/**
- * compiledSql statement id is populated with the corresponding object from the above
- * member compiledSql.
- * this member is used by the native_bind_* methods
+ * SQLiteCompiledSql statement id is populated with the corresponding object from the above
+ * member. This member is used by the native_bind_* methods
*/
protected int nStatement = 0;
@@ -60,47 +58,50 @@ public abstract class SQLiteProgram extends SQLiteClosable {
db.addSQLiteClosable(this);
this.nHandle = db.mNativeHandle;
- compiledSql = db.getCompiledStatementForSql(sql);
- if (compiledSql == null) {
+ mCompiledSql = db.getCompiledStatementForSql(sql);
+ if (mCompiledSql == null) {
// create a new compiled-sql obj
- compiledSql = new SQLiteCompiledSql(db, sql);
+ mCompiledSql = new SQLiteCompiledSql(db, sql);
// add it to the cache of compiled-sqls
- myCompiledSqlIsInCache = db.addToCompiledQueries(sql, compiledSql);
- } else {
- myCompiledSqlIsInCache = true;
+ db.addToCompiledQueries(sql, mCompiledSql);
}
- nStatement = compiledSql.nStatement;
+ nStatement = mCompiledSql.nStatement;
}
@Override
protected void onAllReferencesReleased() {
- // release the compiled sql statement used by me if it is NOT in cache
- if (!myCompiledSqlIsInCache && compiledSql != null) {
- compiledSql.releaseSqlStatement();
- compiledSql = null; // so that GC doesn't call finalize() on it
- }
+ releaseCompiledSqlIfInCache();
mDatabase.releaseReference();
mDatabase.removeSQLiteClosable(this);
}
@Override
protected void onAllReferencesReleasedFromContainer() {
- // release the compiled sql statement used by me if it is NOT in cache
- if (!myCompiledSqlIsInCache && compiledSql != null) {
- compiledSql.releaseSqlStatement();
- compiledSql = null; // so that GC doesn't call finalize() on it
- }
+ releaseCompiledSqlIfInCache();
mDatabase.releaseReference();
}
+ private void releaseCompiledSqlIfInCache() {
+ if (mCompiledSql == null) {
+ return;
+ }
+ synchronized(mDatabase.mCompiledQueries) {
+ if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+ mCompiledSql.releaseSqlStatement();
+ mCompiledSql = null; // so that GC doesn't call finalize() on it
+ nStatement = 0;
+ }
+ }
+ }
+
/**
* Returns a unique identifier for this program.
*
* @return a unique identifier for this program
*/
public final int getUniqueId() {
- return compiledSql.nStatement;
+ return nStatement;
}
/* package */ String getSqlString() {
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 62330e1..d71344c 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -34,7 +34,9 @@ import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * A gesture can have a single or multiple strokes
+ * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
+ * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by
+ * a GestureLibrary and a built-in alphabet gesture can be recognized by a LetterRecognizer.
*/
public class Gesture implements Parcelable {
@@ -58,6 +60,19 @@ public class Gesture implements Parcelable {
mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
}
+ @Override
+ public Object clone() {
+ Gesture gesture = new Gesture();
+ gesture.mBoundingBox.set(mBoundingBox.left, mBoundingBox.top,
+ mBoundingBox.right, mBoundingBox.bottom);
+ final int count = mStrokes.size();
+ for (int i = 0; i < count; i++) {
+ GestureStroke stroke = mStrokes.get(i);
+ gesture.mStrokes.add((GestureStroke)stroke.clone());
+ }
+ return gesture;
+ }
+
/**
* @return all the strokes of the gesture
*/
@@ -73,7 +88,7 @@ public class Gesture implements Parcelable {
}
/**
- * Add a stroke to the gesture
+ * Adds a stroke to the gesture.
*
* @param stroke
*/
@@ -83,8 +98,8 @@ public class Gesture implements Parcelable {
}
/**
- * Get the total length of the gesture. When there are multiple strokes in
- * the gesture, this returns the sum of the lengths of all the strokes
+ * Calculates the total length of the gesture. When there are multiple strokes in
+ * the gesture, this returns the sum of the lengths of all the strokes.
*
* @return the length of the gesture
*/
@@ -142,7 +157,7 @@ public class Gesture implements Parcelable {
}
/**
- * Set the id of the gesture
+ * Sets the id of the gesture.
*
* @param id
*/
@@ -158,7 +173,7 @@ public class Gesture implements Parcelable {
}
/**
- * Create a bitmap of the gesture with a transparent background
+ * Creates a bitmap of the gesture with a transparent background.
*
* @param width width of the target bitmap
* @param height height of the target bitmap
@@ -194,7 +209,7 @@ public class Gesture implements Parcelable {
}
/**
- * Create a bitmap of the gesture with a transparent background
+ * Creates a bitmap of the gesture with a transparent background.
*
* @param width
* @param height
diff --git a/core/java/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java
index 3698011..4cb7707 100644
--- a/core/java/android/gesture/GesturePoint.java
+++ b/core/java/android/gesture/GesturePoint.java
@@ -20,7 +20,7 @@ import java.io.DataInputStream;
import java.io.IOException;
/**
- * A timed point of a gesture stroke
+ * A timed point of a gesture stroke. Multiple points form a stroke.
*/
public class GesturePoint {
@@ -43,4 +43,9 @@ public class GesturePoint {
final long timeStamp = in.readLong();
return new GesturePoint(x, y, timeStamp);
}
+
+ @Override
+ public Object clone() {
+ return new GesturePoint(x, y, timestamp);
+ }
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 598eb85..68dc5a6 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -27,7 +27,8 @@ import java.io.DataInputStream;
import java.util.ArrayList;
/**
- * A gesture stroke started on a touch down and ended on a touch up.
+ * A gesture stroke started on a touch down and ended on a touch up. A stroke
+ * consists of a sequence of timed points. One or multiple strokes form a gesture.
*/
public class GestureStroke {
static final float TOUCH_TOLERANCE = 8;
@@ -41,7 +42,7 @@ public class GestureStroke {
private Path mCachedPath;
/**
- * Construct a gesture stroke from a list of gesture points
+ * A constructor that constructs a gesture stroke from a list of gesture points.
*
* @param points
*/
@@ -82,7 +83,22 @@ public class GestureStroke {
}
/**
- * Draw the gesture with a given canvas and paint
+ * A faster constructor specially for cloning a stroke.
+ */
+ private GestureStroke(RectF bbx, float len, float[] pts, long[] times) {
+ boundingBox = new RectF(bbx.left, bbx.top, bbx.right, bbx.bottom);
+ length = len;
+ points = pts.clone();
+ timestamps = times.clone();
+ }
+
+ @Override
+ public Object clone() {
+ return new GestureStroke(boundingBox, length, points, timestamps);
+ }
+
+ /**
+ * Draws the stroke with a given canvas and paint.
*
* @param canvas
*/
@@ -134,7 +150,7 @@ public class GestureStroke {
}
/**
- * Convert the stroke to a Path based on the number of points
+ * Converts the stroke to a Path of a given number of points.
*
* @param width the width of the bounding box of the target path
* @param height the height of the bounding box of the target path
@@ -213,14 +229,15 @@ public class GestureStroke {
}
/**
- * Invalidate the cached path that is used to render the stroke
+ * Invalidates the cached path that is used to render the stroke.
*/
public void clearPath() {
if (mCachedPath != null) mCachedPath.rewind();
}
/**
- * Compute an oriented bounding box of the stroke
+ * Computes an oriented bounding box of the stroke.
+ *
* @return OrientedBoundingBox
*/
public OrientedBoundingBox computeOrientedBoundingBox() {
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index f1dcd89..dfe1d00 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -27,8 +27,10 @@ import java.io.IOException;
import static android.gesture.GestureConstants.*;
final class GestureUtilities {
- private static final int TEMPORAL_SAMPLING_RATE = 16;
-
+
+ private static final float SCALING_THRESHOLD = 0.26f;
+ private static final float NONUNIFORM_SCALE = (float) Math.sqrt(2);
+
private GestureUtilities() {
}
@@ -46,64 +48,87 @@ final class GestureUtilities {
}
}
}
-
+
static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) {
+ return spatialSampling(gesture, sampleMatrixDimension, false);
+ }
+
+ static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension,
+ boolean uniformScaling) {
final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive
float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension];
Arrays.fill(sample, 0);
-
+
RectF rect = gesture.getBoundingBox();
- float sx = targetPatchSize / rect.width();
- float sy = targetPatchSize / rect.height();
- float scale = sx < sy ? sx : sy;
+ final float gestureWidth = rect.width();
+ final float gestureHeight = rect.height();
+ float sx = targetPatchSize / gestureWidth;
+ float sy = targetPatchSize / gestureHeight;
+
+ if (uniformScaling) {
+ float scale = sx < sy ? sx : sy;
+ sx = scale;
+ sy = scale;
+ } else {
+ float aspectRatio = gestureWidth / gestureHeight;
+ if (aspectRatio > 1) {
+ aspectRatio = 1 / aspectRatio;
+ }
+ if (aspectRatio < SCALING_THRESHOLD) {
+ float scale = sx < sy ? sx : sy;
+ sx = scale;
+ sy = scale;
+ } else {
+ if (sx > sy) {
+ float scale = sy * NONUNIFORM_SCALE;
+ if (scale < sx) {
+ sx = scale;
+ }
+ } else {
+ float scale = sx * NONUNIFORM_SCALE;
+ if (scale < sy) {
+ sy = scale;
+ }
+ }
+ }
+ }
float preDx = -rect.centerX();
float preDy = -rect.centerY();
float postDx = targetPatchSize / 2;
float postDy = targetPatchSize / 2;
-
final ArrayList<GestureStroke> strokes = gesture.getStrokes();
final int count = strokes.size();
-
int size;
float xpos;
float ypos;
-
for (int index = 0; index < count; index++) {
final GestureStroke stroke = strokes.get(index);
float[] strokepoints = stroke.points;
size = strokepoints.length;
-
final float[] pts = new float[size];
-
for (int i = 0; i < size; i += 2) {
- pts[i] = (strokepoints[i] + preDx) * scale + postDx;
- pts[i + 1] = (strokepoints[i + 1] + preDy) * scale + postDy;
+ pts[i] = (strokepoints[i] + preDx) * sx + postDx;
+ pts[i + 1] = (strokepoints[i + 1] + preDy) * sy + postDy;
}
-
float segmentEndX = -1;
float segmentEndY = -1;
-
for (int i = 0; i < size; i += 2) {
-
float segmentStartX = pts[i] < 0 ? 0 : pts[i];
float segmentStartY = pts[i + 1] < 0 ? 0 : pts[i + 1];
-
if (segmentStartX > targetPatchSize) {
segmentStartX = targetPatchSize;
}
-
if (segmentStartY > targetPatchSize) {
segmentStartY = targetPatchSize;
}
-
plot(segmentStartX, segmentStartY, sample, sampleMatrixDimension);
-
if (segmentEndX != -1) {
// evaluate horizontally
if (segmentEndX > segmentStartX) {
xpos = (float) Math.ceil(segmentStartX);
- float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX);
+ float slope = (segmentEndY - segmentStartY) /
+ (segmentEndX - segmentStartX);
while (xpos < segmentEndX) {
ypos = slope * (xpos - segmentStartX) + segmentStartY;
plot(xpos, ypos, sample, sampleMatrixDimension);
@@ -111,18 +136,19 @@ final class GestureUtilities {
}
} else if (segmentEndX < segmentStartX){
xpos = (float) Math.ceil(segmentEndX);
- float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX);
+ float slope = (segmentEndY - segmentStartY) /
+ (segmentEndX - segmentStartX);
while (xpos < segmentStartX) {
ypos = slope * (xpos - segmentStartX) + segmentStartY;
plot(xpos, ypos, sample, sampleMatrixDimension);
xpos++;
}
}
-
// evaluating vertically
if (segmentEndY > segmentStartY) {
ypos = (float) Math.ceil(segmentStartY);
- float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY);
+ float invertSlope = (segmentEndX - segmentStartX) /
+ (segmentEndY - segmentStartY);
while (ypos < segmentEndY) {
xpos = invertSlope * (ypos - segmentStartY) + segmentStartX;
plot(xpos, ypos, sample, sampleMatrixDimension);
@@ -130,7 +156,8 @@ final class GestureUtilities {
}
} else if (segmentEndY < segmentStartY) {
ypos = (float) Math.ceil(segmentEndY);
- float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY);
+ float invertSlope = (segmentEndX - segmentStartX) /
+ (segmentEndY - segmentStartY);
while (ypos < segmentStartY) {
xpos = invertSlope * (ypos - segmentStartY) + segmentStartX;
plot(xpos, ypos, sample, sampleMatrixDimension);
@@ -138,16 +165,13 @@ final class GestureUtilities {
}
}
}
-
segmentEndX = segmentStartX;
segmentEndY = segmentStartY;
}
}
-
-
return sample;
}
-
+
private static void plot(float x, float y, float[] sample, int sampleSize) {
x = x < 0 ? 0 : x;
y = y < 0 ? 0 : y;
@@ -163,40 +187,44 @@ final class GestureUtilities {
sample[index] = 1;
}
} else {
- double topLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yFloor - y, 2));
- double topRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yFloor - y, 2));
- double btmLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yCeiling - y, 2));
- double btmRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yCeiling - y, 2));
- double sum = topLeft + topRight + btmLeft + btmRight;
+ final double xFloorSq = Math.pow(xFloor - x, 2);
+ final double yFloorSq = Math.pow(yFloor - y, 2);
+ final double xCeilingSq = Math.pow(xCeiling - x, 2);
+ final double yCeilingSq = Math.pow(yCeiling - y, 2);
+ float topLeft = (float) Math.sqrt(xFloorSq + yFloorSq);
+ float topRight = (float) Math.sqrt(xCeilingSq + yFloorSq);
+ float btmLeft = (float) Math.sqrt(xFloorSq + yCeilingSq);
+ float btmRight = (float) Math.sqrt(xCeilingSq + yCeilingSq);
+ float sum = topLeft + topRight + btmLeft + btmRight;
- double value = topLeft / sum;
+ float value = topLeft / sum;
int index = yFloor * sampleSize + xFloor;
if (value > sample[index]){
- sample[index] = (float) value;
+ sample[index] = value;
}
value = topRight / sum;
index = yFloor * sampleSize + xCeiling;
if (value > sample[index]){
- sample[index] = (float) value;
+ sample[index] = value;
}
value = btmLeft / sum;
index = yCeiling * sampleSize + xFloor;
if (value > sample[index]){
- sample[index] = (float) value;
+ sample[index] = value;
}
value = btmRight / sum;
index = yCeiling * sampleSize + xCeiling;
if (value > sample[index]){
- sample[index] = (float) value;
+ sample[index] = value;
}
}
}
-
+
/**
- * Featurize a stroke into a vector of a given number of elements
+ * Featurizes a stroke into a vector of a given number of elements
*
* @param stroke
* @param sampleSize
@@ -286,8 +314,8 @@ final class GestureUtilities {
* @param points
* @return the covariance matrix
*/
- private static double[][] computeCoVariance(float[] points) {
- double[][] array = new double[2][2];
+ private static float[][] computeCoVariance(float[] points) {
+ float[][] array = new float[2][2];
array[0][0] = 0;
array[0][1] = 0;
array[1][0] = 0;
@@ -321,17 +349,17 @@ final class GestureUtilities {
return sum;
}
- static double computeStraightness(float[] points) {
+ static float computeStraightness(float[] points) {
float totalLen = computeTotalLength(points);
float dx = points[2] - points[0];
float dy = points[3] - points[1];
- return Math.sqrt(dx * dx + dy * dy) / totalLen;
+ return (float) Math.sqrt(dx * dx + dy * dy) / totalLen;
}
- static double computeStraightness(float[] points, float totalLen) {
+ static float computeStraightness(float[] points, float totalLen) {
float dx = points[2] - points[0];
float dy = points[3] - points[1];
- return Math.sqrt(dx * dx + dy * dy) / totalLen;
+ return (float) Math.sqrt(dx * dx + dy * dy) / totalLen;
}
/**
@@ -341,8 +369,8 @@ final class GestureUtilities {
* @param vector2
* @return the distance
*/
- static double squaredEuclideanDistance(float[] vector1, float[] vector2) {
- double squaredDistance = 0;
+ static float squaredEuclideanDistance(float[] vector1, float[] vector2) {
+ float squaredDistance = 0;
int size = vector1.length;
for (int i = 0; i < size; i++) {
float difference = vector1[i] - vector2[i];
@@ -358,13 +386,13 @@ final class GestureUtilities {
* @param vector2
* @return the distance between 0 and Math.PI
*/
- static double cosineDistance(float[] vector1, float[] vector2) {
+ static float cosineDistance(float[] vector1, float[] vector2) {
float sum = 0;
int len = vector1.length;
for (int i = 0; i < len; i++) {
sum += vector1[i] * vector2[i];
}
- return Math.acos(sum);
+ return (float) Math.acos(sum);
}
/**
@@ -375,46 +403,58 @@ final class GestureUtilities {
* @param numOrientations the maximum number of orientation allowed
* @return the distance between the two instances (between 0 and Math.PI)
*/
- static double minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
+ static float minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
final int len = vector1.length;
- double a = 0;
- double b = 0;
+ float a = 0;
+ float b = 0;
for (int i = 0; i < len; i += 2) {
a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1];
b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i];
}
if (a != 0) {
- final double tan = b/a;
+ final float tan = b/a;
final double angle = Math.atan(tan);
if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) {
- return Math.acos(a);
+ return (float) Math.acos(a);
} else {
final double cosine = Math.cos(angle);
final double sine = cosine * tan;
- return Math.acos(a * cosine + b * sine);
+ return (float) Math.acos(a * cosine + b * sine);
}
} else {
- return Math.PI / 2;
+ return (float) Math.PI / 2;
}
}
- static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> pts) {
- GestureStroke stroke = new GestureStroke(pts);
- float[] points = temporalSampling(stroke, TEMPORAL_SAMPLING_RATE);
- return computeOrientedBoundingBox(points);
+ static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> originalPoints) {
+ final int count = originalPoints.size();
+ float[] points = new float[count * 2];
+ for (int i = 0; i < count; i++) {
+ GesturePoint point = originalPoints.get(i);
+ int index = i * 2;
+ points[index] = point.x;
+ points[index + 1] = point.y;
+ }
+ float[] meanVector = computeCentroid(points);
+ return computeOrientedBoundingBox(points, meanVector);
}
- static OrientedBoundingBox computeOrientedBoundingBox(float[] points) {
+ static OrientedBoundingBox computeOrientedBoundingBox(float[] originalPoints) {
+ int size = originalPoints.length;
+ float[] points = new float[size];
+ for (int i = 0; i < size; i++) {
+ points[i] = originalPoints[i];
+ }
float[] meanVector = computeCentroid(points);
return computeOrientedBoundingBox(points, meanVector);
}
- static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
+ private static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
translate(points, -centroid[0], -centroid[1]);
- double[][] array = computeCoVariance(points);
- double[] targetVector = computeOrientation(array);
+ float[][] array = computeCoVariance(points);
+ float[] targetVector = computeOrientation(array);
float angle;
if (targetVector[0] == 0 && targetVector[1] == 0) {
@@ -448,25 +488,25 @@ final class GestureUtilities {
return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny);
}
- private static double[] computeOrientation(double[][] covarianceMatrix) {
- double[] targetVector = new double[2];
+ private static float[] computeOrientation(float[][] covarianceMatrix) {
+ float[] targetVector = new float[2];
if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) {
targetVector[0] = 1;
targetVector[1] = 0;
}
- double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1];
- double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1]
+ float a = -covarianceMatrix[0][0] - covarianceMatrix[1][1];
+ float b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1]
* covarianceMatrix[1][0];
- double value = a / 2;
- double rightside = Math.sqrt(Math.pow(value, 2) - b);
- double lambda1 = -value + rightside;
- double lambda2 = -value - rightside;
+ float value = a / 2;
+ float rightside = (float) Math.sqrt(Math.pow(value, 2) - b);
+ float lambda1 = -value + rightside;
+ float lambda2 = -value - rightside;
if (lambda1 == lambda2) {
targetVector[0] = 0;
targetVector[1] = 0;
} else {
- double lambda = lambda1 > lambda2 ? lambda1 : lambda2;
+ float lambda = lambda1 > lambda2 ? lambda1 : lambda2;
targetVector[0] = 1;
targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1];
}
@@ -474,13 +514,13 @@ final class GestureUtilities {
}
- static float[] rotate(float[] points, double angle) {
- double cos = Math.cos(angle);
- double sin = Math.sin(angle);
+ static float[] rotate(float[] points, float angle) {
+ float cos = (float) Math.cos(angle);
+ float sin = (float) Math.sin(angle);
int size = points.length;
for (int i = 0; i < size; i += 2) {
- float x = (float) (points[i] * cos - points[i + 1] * sin);
- float y = (float) (points[i] * sin + points[i + 1] * cos);
+ float x = points[i] * cos - points[i + 1] * sin;
+ float y = points[i] * sin + points[i + 1] * cos;
points[i] = x;
points[i + 1] = y;
}
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index 68a2985..bb0b340 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -84,7 +84,7 @@ class Instance {
}
private static float[] spatialSampler(Gesture gesture) {
- return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE);
+ return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
}
private static float[] temporalSampler(int orientationType, Gesture gesture) {
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index e73569a..c0c2d03 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -94,6 +94,11 @@ interface IMountService
String mountSecureContainer(String id, String key, int ownerUid);
/*
+ * Unount a secure container.
+ */
+ void unmountSecureContainer(String id);
+
+ /*
* Returns the filesystem path of a mounted secure container.
*/
String getSecureContainerPath(String id);
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index c2928cb..fe8cfb0 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -522,7 +522,7 @@ public class VCardParser_V21 extends VCardParser {
protected void handleParams(String params) throws VCardException {
String[] strArray = params.split("=", 2);
if (strArray.length == 2) {
- String paramName = strArray[0].trim();
+ final String paramName = strArray[0].trim().toUpperCase();
String paramValue = strArray[1].trim();
if (paramName.equals("TYPE")) {
handleType(paramValue);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 7854423..d52632b 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -22,6 +22,7 @@ import com.android.internal.telephony.Connection;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
@@ -111,25 +112,25 @@ public class CallLog {
* <P>Type: TEXT</P>
*/
public static final String CACHED_NAME = "name";
-
+
/**
* The cached number type (Home, Work, etc) associated with the
* phone number, if it exists.
* This value is not guaranteed to be current, if the contact information
* associated with this number has changed.
- * <P>Type: INTEGER</P>
+ * <P>Type: INTEGER</P>
*/
public static final String CACHED_NUMBER_TYPE = "numbertype";
-
+
/**
* The cached number label, for a custom number type, associated with the
* phone number, if it exists.
* This value is not guaranteed to be current, if the contact information
* associated with this number has changed.
- * <P>Type: TEXT</P>
+ * <P>Type: TEXT</P>
*/
public static final String CACHED_NUMBER_LABEL = "numberlabel";
-
+
/**
* Adds a call to the call log.
*
@@ -137,15 +138,15 @@ public class CallLog {
* if the contact is unknown.
* @param context the context used to get the ContentResolver
* @param number the phone number to be added to the calls db
- * @param presentation the number presenting rules set by the network for
+ * @param presentation the number presenting rules set by the network for
* "allowed", "payphone", "restricted" or "unknown"
* @param callType enumerated values for "incoming", "outgoing", or "missed"
* @param start time stamp for the call in milliseconds
* @param duration call duration in seconds
- *
+ *
* {@hide}
*/
- public static Uri addCall(CallerInfo ci, Context context, String number,
+ public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, long start, int duration) {
final ContentResolver resolver = context.getContentResolver();
@@ -175,22 +176,47 @@ public class CallLog {
values.put(CACHED_NUMBER_TYPE, ci.numberType);
values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
}
-
+
if ((ci != null) && (ci.person_id > 0)) {
ContactsContract.Contacts.markAsContacted(resolver, ci.person_id);
}
-
+
Uri result = resolver.insert(CONTENT_URI, values);
-
+
removeExpiredEntries(context);
-
+
return result;
}
-
+
+ /**
+ * Query the call log database for the last dialed number.
+ * @param context Used to get the content resolver.
+ * @return The last phone number dialed (outgoing) or an empty
+ * string if none exist yet.
+ */
+ public static String getLastOutgoingCall(Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ Cursor c = null;
+ try {
+ c = resolver.query(
+ CONTENT_URI,
+ new String[] {NUMBER},
+ TYPE + " = " + OUTGOING_TYPE,
+ null,
+ DEFAULT_SORT_ORDER + " LIMIT 1");
+ if (c == null || !c.moveToFirst()) {
+ return "";
+ }
+ return c.getString(0);
+ } finally {
+ if (c != null) c.close();
+ }
+ }
+
private static void removeExpiredEntries(Context context) {
final ContentResolver resolver = context.getContentResolver();
resolver.delete(CONTENT_URI, "_id IN " +
- "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
+ "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
+ " LIMIT -1 OFFSET 500)", null);
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 7fb9daf..93b5b4d 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -55,19 +55,21 @@ import java.io.InputStream;
* </p>
* <ul>
* <li>
- * The {@link Data} table contains all kinds of personal data: phone numbers,
- * email addresses etc. The list of data kinds that can be stored in this table
- * is open-ended. There is a predefined set of common kinds, but any application
- * can add its own data kinds.
+ * A row in the {@link Data} table can store any kind of personal data, such
+ * as a phone number or email addresses. The set of data kinds that can be
+ * stored in this table is open-ended. There is a predefined set of common
+ * kinds, but any application can add its own data kinds.
* </li>
* <li>
- * A row in the {@link RawContacts} table represents a set of Data describing a
- * person and associated with a single account (for example, a single Gmail
- * account).
+ * A row in the {@link RawContacts} table represents a set of data describing a
+ * person and associated with a single account (for example, one of the user's
+ * Gmail accounts).
* </li>
* <li>
* A row in the {@link Contacts} table represents an aggregate of one or more
- * RawContacts presumably describing the same person.
+ * RawContacts presumably describing the same person. When data in or associated with
+ * the RawContacts table is changed, the affected aggregate contacts are updated as
+ * necessary.
* </li>
* </ul>
* <p>
@@ -75,7 +77,8 @@ import java.io.InputStream;
* </p>
* <ul>
* <li>
- * {@link Groups}, which contains information about raw contact groups - the
+ * {@link Groups}, which contains information about raw contact groups
+ * such as Gmail contact groups. The
* current API does not support the notion of groups spanning multiple accounts.
* </li>
* <li>
@@ -106,11 +109,15 @@ public final class ContactsContract {
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
/**
- * An optional insert, update or delete URI parameter that allows the caller
+ * An optional URI parameter for insert, update, or delete queries
+ * that allows the caller
* to specify that it is a sync adapter. The default value is false. If true
- * the dirty flag is not automatically set and the "syncToNetwork" parameter
- * is set to false when calling
- * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+ * {@link RawContacts#DIRTY} is not automatically set and the
+ * "syncToNetwork" parameter is set to false when calling
+ * {@link
+ * ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+ * This prevents an unnecessary extra synchronization, see the discussion of
+ * the delete operation in {@link RawContacts}.
*/
public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
@@ -459,19 +466,23 @@ public final class ContactsContract {
protected interface ContactNameColumns {
/**
- * The kind of data that is used as the display name for the contact, see
- * DisplayNameSources.
+ * The kind of data that is used as the display name for the contact, such as
+ * structured name or email address. See DisplayNameSources.
+ *
+ * TODO: convert DisplayNameSources to a link after it is un-hidden
*/
public static final String DISPLAY_NAME_SOURCE = "display_name_source";
/**
* The default text shown as the contact's display name. It is based on
* available data, see {@link #DISPLAY_NAME_SOURCE}.
+ *
+ * @see ContactsContract.ContactNameColumns#DISPLAY_NAME_ALTERNATIVE
*/
public static final String DISPLAY_NAME_PRIMARY = "display_name";
/**
- * Alternative representation of the display name. If display name is
+ * An alternative representation of the display name. If display name is
* based on the structured name and the structured name follows
* the Western full name style, then this field contains the "family name first"
* version of the full name. Otherwise, it is the same as DISPLAY_NAME_PRIMARY.
@@ -479,27 +490,42 @@ public final class ContactsContract {
public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt";
/**
- * The type of alphabet used to capture the phonetic name. See
+ * The phonetic alphabet used to represent the {@link #PHONETIC_NAME}. See
* PhoneticNameStyle.
+ *
+ * TODO: convert PhoneticNameStyle to a link after it is un-hidden
*/
public static final String PHONETIC_NAME_STYLE = "phonetic_name_style";
/**
- * Pronunciation of the full name. See PhoneticNameStyle.
+ * <p>
+ * Pronunciation of the full name in the phonetic alphabet specified by
+ * {@link #PHONETIC_NAME_STYLE}.
+ * </p>
+ * <p>
+ * The value may be set manually by the user.
+ * This capability is is of interest only in countries
+ * with commonly used phonetic
+ * alphabets, such as Japan and Korea. See PhoneticNameStyle.
+ * </p>
+ *
+ * TODO: convert PhoneticNameStyle to a link after it is un-hidden
*/
public static final String PHONETIC_NAME = "phonetic_name";
/**
* Sort key that takes into account locale-based traditions for sorting
- * names in address books. More specifically, for Chinese names
- * the sort key is the name's Pinyin spelling; for Japanese names
+ * names in address books. The default
+ * sort key is {@link #DISPLAY_NAME_PRIMARY}. For Chinese names
+ * the sort key is the name's Pinyin spelling, and for Japanese names
* it is the Hiragana version of the phonetic name.
*/
public static final String SORT_KEY_PRIMARY = "sort_key";
/**
* Sort key based on the alternative representation of the full name,
- * specifically the one using the 'family name first' format for
+ * {@link #DISPLAY_NAME_ALTERNATIVE}. Thus for Western names,
+ * it is the one using the "family name first" format for
* Western names.
*/
public static final String SORT_KEY_ALTERNATIVE = "sort_key_alt";
@@ -808,7 +834,10 @@ public final class ContactsContract {
}
/**
- * Mark a contact as having been contacted.
+ * Mark a contact as having been contacted. This updates the
+ * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED} for the
+ * contact, plus the corresponding values of any associated raw
+ * contacts.
*
* @param resolver the ContentResolver to use
* @param contactId the person who was contacted
@@ -1050,15 +1079,37 @@ public final class ContactsContract {
}
/**
- * Constants for the raw contacts table, which contains the base contact
- * information per sync source. Sync adapters and contact management apps
+ * Constants for the raw contacts table, which contains one row of contact
+ * information for each person in each synced account. Sync adapters and
+ * contact management apps
* are the primary consumers of this API.
+ *
+ * <h3>Aggregation</h3>
+ * <p>
+ * As soon as a raw contact is inserted or whenever its constituent data
+ * changes, the provider will check if the raw contact matches other
+ * existing raw contacts and if so will aggregate it with those. The
+ * aggregation is reflected in the {@link RawContacts} table by the change of the
+ * {@link #CONTACT_ID} field, which is the reference to the aggregate contact.
+ * </p>
+ * <p>
+ * Changes to the structured name, organization, phone number, email address,
+ * or nickname trigger a re-aggregation.
+ * </p>
+ * <p>
+ * See also {@link AggregationExceptions} for a mechanism to control
+ * aggregation programmatically.
+ * </p>
+ *
* <h3>Operations</h3>
* <dl>
* <dt><b>Insert</b></dt>
- * <dd>There are two mechanisms that can be used to insert a raw contact: incremental and
- * batch. The incremental method is more traditional but less efficient. It should be used
- * only if the constituent data rows are unavailable at the time the raw contact is created:
+ * <dd>
+ * <p>
+ * Raw contacts can be inserted incrementally or in a batch.
+ * The incremental method is more traditional but less efficient.
+ * It should be used
+ * only if no {@link Data} values are available at the time the raw contact is created:
* <pre>
* ContentValues values = new ContentValues();
* values.put(RawContacts.ACCOUNT_TYPE, accountType);
@@ -1066,9 +1117,10 @@ public final class ContactsContract {
* Uri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values);
* long rawContactId = ContentUris.parseId(rawContactUri);
* </pre>
+ * </p>
* <p>
- * Once data rows are available, insert those. For example, here's how you would insert
- * a name:
+ * Once {@link Data} values become available, insert those.
+ * For example, here's how you would insert a name:
*
* <pre>
* values.clear();
@@ -1084,6 +1136,7 @@ public final class ContactsContract {
* and causes at most one aggregation pass.
* <pre>
* ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+ * ...
* int rawContactInsertIndex = ops.size();
* ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
* .withValue(RawContacts.ACCOUNT_TYPE, accountType)
@@ -1100,21 +1153,27 @@ public final class ContactsContract {
* </pre>
* </p>
* <p>
- * Please note the use of back reference in the construction of the
- * {@link ContentProviderOperation}. It allows an operation to use the result of
- * a previous operation by referring to it by its index in the batch.
+ * Note the use of {@link ContentProviderOperation.Builder#withValueBackReference(String, int)}
+ * to refer to the as-yet-unknown index value of the raw contact inserted in the
+ * first operation.
* </p>
+ *
* <dt><b>Update</b></dt>
- * <dd><p>Just as with insert, the update can be done incrementally or as a batch, the
- * batch mode being the preferred method.</p></dd>
+ * <dd><p>
+ * Raw contacts can be updated incrementally or in a batch.
+ * Batch mode should be used whenever possible.
+ * The procedures and considerations are analogous to those documented above for inserts.
+ * </p></dd>
* <dt><b>Delete</b></dt>
* <dd><p>When a raw contact is deleted, all of its Data rows as well as StatusUpdates,
* AggregationExceptions, PhoneLookup rows are deleted automatically. When all raw
- * contacts in a Contact are deleted, the Contact itself is also deleted automatically.
+ * contacts associated with a {@link Contacts} row are deleted, the {@link Contacts} row
+ * itself is also deleted automatically.
* </p>
* <p>
- * The invocation of {@code resolver.delete(...)}, does not physically delete
- * a raw contacts row. It sets the {@link #DELETED} flag on the raw contact and
+ * The invocation of {@code resolver.delete(...)}, does not immediately delete
+ * a raw contacts row.
+ * Instead, it sets the {@link #DELETED} flag on the raw contact and
* removes the raw contact from its aggregate contact.
* The sync adapter then deletes the raw contact from the server and
* finalizes phone-side deletion by calling {@code resolver.delete(...)}
@@ -1124,10 +1183,11 @@ public final class ContactsContract {
* is marked for deletion, it will remain on the phone. However it will be
* effectively invisible, because it will not be part of any aggregate contact.
* </dd>
+ *
* <dt><b>Query</b></dt>
* <dd>
* <p>
- * Finding all raw contacts in a Contact is easy:
+ * It is easy to find all raw contacts in a Contact:
* <pre>
* Cursor c = getContentResolver().query(RawContacts.CONTENT_URI,
* new String[]{RawContacts._ID},
@@ -1136,7 +1196,7 @@ public final class ContactsContract {
* </pre>
* </p>
* <p>
- * There are two ways to find raw contacts within a specific account,
+ * To find raw contacts within a specific account,
* you can either put the account name and type in the selection or pass them as query
* parameters. The latter approach is preferable, especially when you can reuse the
* URI:
@@ -1178,19 +1238,11 @@ public final class ContactsContract {
* </p>
* </dd>
* </dl>
- * <h3>Aggregation</h3>
- * <p>
- * As soon as a raw contact is inserted or whenever its constituent data
- * changes, the provider will check if the raw contact matches other
- * existing raw contacts and if so will aggregate it with those. From the
- * data standpoint, aggregation is reflected in the change of the
- * {@link #CONTACT_ID} field, which is the reference to the aggregate contact.
- * </p>
- * <p>
- * See also {@link AggregationExceptions} for a mechanism to control
- * aggregation programmatically.
- * </p>
* <h2>Columns</h2>
+ * TODO: include {@link #DISPLAY_NAME_PRIMARY}, {@link #DISPLAY_NAME_ALTERNATIVE},
+ * {@link #DISPLAY_NAME_SOURCE}, {@link #PHONETIC_NAME}, {@link #PHONETIC_NAME_STYLE},
+ * {@link #SORT_KEY_PRIMARY}, {@link #SORT_KEY_ALTERNATIVE}?
+ *
* <table class="jd-sumtable">
* <tr>
* <th colspan='4'>RawContacts</th>
@@ -1199,15 +1251,16 @@ public final class ContactsContract {
* <td>long</td>
* <td>{@link #_ID}</td>
* <td>read-only</td>
- * <td>Row ID. Sync adapter should try to preserve row IDs during updates. In other words,
- * it would be a really bad idea to delete and reinsert a raw contact. A sync adapter should
- * always do an update instead.</td>
+ * <td>Row ID. Sync adapters should try to preserve row IDs during updates. In other words,
+ * it is much better for a sync adapter to update a raw contact rather than to delete and
+ * re-insert it.</td>
* </tr>
* <tr>
* <td>long</td>
* <td>{@link #CONTACT_ID}</td>
* <td>read-only</td>
- * <td>A reference to the {@link ContactsContract.Contacts#_ID} that this raw contact belongs
+ * <td>The ID of the row in the {@link ContactsContract.Contacts} table
+ * that this raw contact belongs
* to. Raw contacts are linked to contacts by the aggregation process, which can be controlled
* by the {@link #AGGREGATION_MODE} field and {@link AggregationExceptions}.</td>
* </tr>
@@ -1238,7 +1291,8 @@ public final class ContactsContract {
* <td>The number of times the contact has been contacted. To have an effect
* on the corresponding value of the aggregate contact, this field
* should be set at the time the raw contact is inserted.
- * See {@link ContactsContract.Contacts#markAsContacted}.</td>
+ * After that, this value is typically updated via
+ * {@link ContactsContract.Contacts#markAsContacted}.</td>
* </tr>
* <tr>
* <td>long</td>
@@ -1247,14 +1301,16 @@ public final class ContactsContract {
* <td>The timestamp of the last time the contact was contacted. To have an effect
* on the corresponding value of the aggregate contact, this field
* should be set at the time the raw contact is inserted.
- * See {@link ContactsContract.Contacts#markAsContacted}.</td>
+ * After that, this value is typically updated via
+ * {@link ContactsContract.Contacts#markAsContacted}.
+ * </td>
* </tr>
* <tr>
* <td>int</td>
* <td>{@link #STARRED}</td>
* <td>read/write</td>
* <td>An indicator for favorite contacts: '1' if favorite, '0' otherwise.
- * Changing this field immediately effects the corresponding aggregate contact:
+ * Changing this field immediately affects the corresponding aggregate contact:
* if any raw contacts in that aggregate contact are starred, then the contact
* itself is marked as starred.</td>
* </tr>
@@ -1267,7 +1323,8 @@ public final class ContactsContract {
* {@link android.media.RingtoneManager#ACTION_RINGTONE_PICKER} intent.
* To have an effect on the corresponding value of the aggregate contact, this field
* should be set at the time the raw contact is inserted. To set a custom
- * ringtone on a contact, use the field {@link ContactsContract.Contacts#CUSTOM_RINGTONE}
+ * ringtone on a contact, use the field {@link ContactsContract.Contacts#CUSTOM_RINGTONE
+ * Contacts.CUSTOM_RINGTONE}
* instead.</td>
* </tr>
* <tr>
@@ -1284,16 +1341,27 @@ public final class ContactsContract {
* <td>{@link #ACCOUNT_NAME}</td>
* <td>read/write-once</td>
* <td>The name of the account instance to which this row belongs, which when paired with
- * {@link #ACCOUNT_TYPE} identifies a specific account. It should be set at the time
+ * {@link #ACCOUNT_TYPE} identifies a specific account.
+ * For example, this will be the Gmail address if it is a Google account.
+ * It should be set at the time
* the raw contact is inserted and never changed afterwards.</td>
* </tr>
* <tr>
* <td>String</td>
* <td>{@link #ACCOUNT_TYPE}</td>
* <td>read/write-once</td>
- * <td>The type of account to which this row belongs, which when paired with
- * {@link #ACCOUNT_NAME} identifies a specific account. It should be set at the time
- * the raw contact is inserted and never changed afterwards.</td>
+ * <td>
+ * <p>
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * It should be set at the time
+ * the raw contact is inserted and never changed afterwards.
+ * </p>
+ * <p>
+ * To ensure uniqueness, new account types should be chosen according to the
+ * Java package naming convention. Thus a Google account is of type "com.google".
+ * </p>
+ * </td>
* </tr>
* <tr>
* <td>String</td>
@@ -1302,8 +1370,8 @@ public final class ContactsContract {
* <td>String that uniquely identifies this row to its source account.
* Typically it is set at the time the raw contact is inserted and never
* changed afterwards. The one notable exception is a new raw contact: it
- * will have an account name and type, but no source id. This should
- * indicated to the sync adapter that a new contact needs to be created
+ * will have an account name and type, but no source id. This
+ * indicates to the sync adapter that a new contact needs to be created
* server-side and its ID stored in the corresponding SOURCE_ID field on
* the phone.
* </td>
@@ -1335,7 +1403,8 @@ public final class ContactsContract {
* <td>String</td>
* <td>{@link #SYNC1}</td>
* <td>read/write</td>
- * <td>Generic column for use by sync adapters. Content provider
+ * <td>Generic column provided for arbitrary use by sync adapters.
+ * The content provider
* stores this information on behalf of the sync adapter but does not
* interpret it in any way.
* </td>
@@ -1372,46 +1441,69 @@ public final class ContactsContract {
}
/**
- * The content:// style URI for this table
+ * The content:// style URI for this table, which requests a directory of
+ * raw contact rows matching the selection criteria.
*/
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
/**
- * The MIME type of {@link #CONTENT_URI} providing a directory of
- * people.
+ * The MIME type of the results from {@link #CONTENT_URI} when a specific
+ * ID value is not provided, and multiple raw contacts may be returned.
*/
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact";
/**
- * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
- * person.
+ * The MIME type of the results when a raw contact ID is appended to {@link #CONTENT_URI},
+ * yielding a subdirectory of a single person.
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/raw_contact";
/**
- * Aggregation mode: aggregate asynchronously.
+ * Aggregation mode: aggregate immediately after insert or update operation(s) are complete.
*/
public static final int AGGREGATION_MODE_DEFAULT = 0;
/**
- * Aggregation mode: aggregate at the time the raw contact is inserted/updated.
- * TODO: deprecate. Aggregation is now synchronous, this value is a no-op
+ * Do not use.
+ *
+ * TODO: deprecate in favor of {@link #AGGREGATION_MODE_DEFAULT}
*/
public static final int AGGREGATION_MODE_IMMEDIATE = 1;
/**
- * If {@link #AGGREGATION_MODE} is {@link #AGGREGATION_MODE_SUSPENDED}, changes
- * to the raw contact do not cause its aggregation to be revisited. Note that changing
+ * <p>
+ * Aggregation mode: aggregation suspended temporarily, and is likely to be resumed later.
+ * Changes to the raw contact will update the associated aggregate contact but will not
+ * result in any change in how the contact is aggregated. Similar to
+ * {@link #AGGREGATION_MODE_DISABLED}, but maintains a link to the corresponding
+ * {@link Contacts} aggregate.
+ * </p>
+ * <p>
+ * This can be used to postpone aggregation until after a series of updates, for better
+ * performance and/or user experience.
+ * </p>
+ * <p>
+ * Note that changing
* {@link #AGGREGATION_MODE} from {@link #AGGREGATION_MODE_SUSPENDED} to
- * {@link #AGGREGATION_MODE_DEFAULT} does not trigger an aggregation pass. Any subsequent
+ * {@link #AGGREGATION_MODE_DEFAULT} does not trigger an aggregation pass, but any
+ * subsequent
* change to the raw contact's data will.
+ * </p>
*/
public static final int AGGREGATION_MODE_SUSPENDED = 2;
/**
- * Aggregation mode: never aggregate this raw contact (note that the raw contact will not
- * have a corresponding Aggregate and therefore will not be included in Aggregates
- * query results.)
+ * <p>
+ * Aggregation mode: never aggregate this raw contact. The raw contact will not
+ * have a corresponding {@link Contacts} aggregate and therefore will not be included in
+ * {@link Contacts} query results.
+ * </p>
+ * <p>
+ * For example, this mode can be used for a raw contact that is marked for deletion while
+ * waiting for the deletion to occur on the server side.
+ * </p>
+ *
+ * @see #AGGREGATION_MODE_SUSPENDED
*/
public static final int AGGREGATION_MODE_DISABLED = 3;
@@ -1441,9 +1533,11 @@ public final class ContactsContract {
}
/**
- * A sub-directory of a single raw contact that contains all of their
+ * A sub-directory of a single raw contact that contains all of its
* {@link ContactsContract.Data} rows. To access this directory
* append {@link Data#CONTENT_DIRECTORY} to the contact URI.
+ *
+ * TODO: deprecate in favor of {@link RawContacts.Entity}.
*/
public static final class Data implements BaseColumns, DataColumns {
/**
@@ -1460,26 +1554,24 @@ public final class ContactsContract {
/**
* <p>
- * A sub-directory of a single raw contact that contains all of their
+ * A sub-directory of a single raw contact that contains all of its
* {@link ContactsContract.Data} rows. To access this directory append
* {@link #CONTENT_DIRECTORY} to the contact URI. See
* {@link RawContactsEntity} for a stand-alone table containing the same
* data.
* </p>
* <p>
- * The Entity directory is similar to the {@link RawContacts.Data}
- * directory but with two important differences:
- * <ul>
- * <li>Entity has different ID fields: {@link #_ID} for the raw contact
- * and {@link #DATA_ID} for the data rows.</li>
- * <li>Entity always contains at least one row, even if there are no
+ * Entity has two ID fields: {@link #_ID} for the raw contact
+ * and {@link #DATA_ID} for the data rows.
+ * Entity always contains at least one row, even if there are no
* actual data rows. In this case the {@link #DATA_ID} field will be
- * null.</li>
- * </ul>
- * Using Entity should preferred to using two separate queries:
- * RawContacts followed by Data. The reason is that Entity reads all
- * data for a raw contact in one transaction, so there is no possibility
- * of the data changing between the two queries.
+ * null.
+ * </p>
+ * <p>
+ * Entity reads all
+ * data for a raw contact in one transaction, to guarantee
+ * consistency.
+ * </p>
*/
public static final class Entity implements BaseColumns, DataColumns {
/**
@@ -1501,6 +1593,11 @@ public final class ContactsContract {
public static final String DATA_ID = "data_id";
}
+ /**
+ * TODO: javadoc
+ * @param cursor
+ * @return
+ */
public static EntityIterator newEntityIterator(Cursor cursor) {
return new EntityIteratorImpl(cursor);
}
@@ -1839,7 +1936,12 @@ public final class ContactsContract {
* By convention, {@link #DATA15} is used for storing BLOBs (binary data).
* </p>
* <p>
- * Typically you should refrain from introducing new kinds of data for an other
+ * The sync adapter for a given account type must correctly handle every data type
+ * used in the corresponding raw contacts. Otherwise it could result in lost or
+ * corrupted data.
+ * </p>
+ * <p>
+ * Similarly, you should refrain from introducing new kinds of data for an other
* party's account types. For example, if you add a data row for
* "favorite song" to a raw contact owned by a Google account, it will not
* get synced to the server, because the Google sync adapter does not know
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 790fe5c..eb863ef 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -19,41 +19,45 @@ package android.provider;
import android.net.Uri;
/**
- * Exposes constants used to interact with the download manager's
- * content provider.
- * The constants URI ... STATUS are the names of columns in the downloads table.
+ * The Download Manager
*
- * @hide
+ * @pending
*/
-// For 1.0 the download manager can't deal with abuse from untrusted apps, so
-// this API is hidden.
-public final class Downloads implements BaseColumns {
+public final class Downloads {
+ /**
+ * @hide
+ */
private Downloads() {}
/**
* The permission to access the download manager
+ * @hide
*/
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
/**
* The permission to access the download manager's advanced functions
+ * @hide
*/
public static final String PERMISSION_ACCESS_ADVANCED =
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
* The permission to directly access the download manager's cache directory
+ * @hide
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
/**
* The permission to send broadcasts on download completion
+ * @hide
*/
public static final String PERMISSION_SEND_INTENTS =
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
* The content:// URI for the data table in the provider
+ * @hide
*/
public static final Uri CONTENT_URI =
Uri.parse("content://downloads/download");
@@ -62,6 +66,7 @@ public final class Downloads implements BaseColumns {
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when that download completes. The
* download's content: uri is specified in the intent's data.
+ * @hide
*/
public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
@@ -75,6 +80,7 @@ public final class Downloads implements BaseColumns {
* multiple downloads.
* Note: this is not currently sent for downloads that have completed
* successfully.
+ * @hide
*/
public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
@@ -83,6 +89,7 @@ public final class Downloads implements BaseColumns {
* The name of the column containing the URI of the data being downloaded.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_URI = "uri";
@@ -90,6 +97,7 @@ public final class Downloads implements BaseColumns {
* The name of the column containing application-specific data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_APP_DATA = "entity";
@@ -103,6 +111,7 @@ public final class Downloads implements BaseColumns {
* whether a download fully completed).
* <P>Type: BOOLEAN</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_NO_INTEGRITY = "no_integrity";
@@ -112,6 +121,7 @@ public final class Downloads implements BaseColumns {
* to use this filename, or a variation, as the actual name for the file.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_FILE_NAME_HINT = "hint";
@@ -120,6 +130,7 @@ public final class Downloads implements BaseColumns {
* was actually stored.
* <P>Type: TEXT</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String _DATA = "_data";
@@ -127,6 +138,7 @@ public final class Downloads implements BaseColumns {
* The name of the column containing the MIME type of the downloaded data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_MIME_TYPE = "mimetype";
@@ -135,6 +147,7 @@ public final class Downloads implements BaseColumns {
* of the download. See the DESTINATION_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_DESTINATION = "destination";
@@ -144,6 +157,7 @@ public final class Downloads implements BaseColumns {
* a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_VISIBILITY = "visibility";
@@ -153,6 +167,7 @@ public final class Downloads implements BaseColumns {
* the CONTROL_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_CONTROL = "control";
@@ -162,6 +177,7 @@ public final class Downloads implements BaseColumns {
* the STATUS_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_STATUS = "status";
@@ -171,6 +187,7 @@ public final class Downloads implements BaseColumns {
* value.
* <P>Type: BIGINT</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_LAST_MODIFICATION = "lastmod";
@@ -180,6 +197,7 @@ public final class Downloads implements BaseColumns {
* notifications to a component in this package when the download completes.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
@@ -190,6 +208,7 @@ public final class Downloads implements BaseColumns {
* Intent.setClassName(String,String).
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
@@ -198,6 +217,7 @@ public final class Downloads implements BaseColumns {
* is sent to the specified class and package when a download has finished.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
@@ -207,6 +227,7 @@ public final class Downloads implements BaseColumns {
* header that gets sent with the request.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_COOKIE_DATA = "cookiedata";
@@ -215,6 +236,7 @@ public final class Downloads implements BaseColumns {
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_USER_AGENT = "useragent";
@@ -223,6 +245,7 @@ public final class Downloads implements BaseColumns {
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_REFERER = "referer";
@@ -231,6 +254,7 @@ public final class Downloads implements BaseColumns {
* downloaded.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_TOTAL_BYTES = "total_bytes";
@@ -239,6 +263,7 @@ public final class Downloads implements BaseColumns {
* has been downloaded so far.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
+ * @hide
*/
public static final String COLUMN_CURRENT_BYTES = "current_bytes";
@@ -251,6 +276,7 @@ public final class Downloads implements BaseColumns {
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
+ * @hide
*/
public static final String COLUMN_OTHER_UID = "otheruid";
@@ -260,6 +286,7 @@ public final class Downloads implements BaseColumns {
* list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_TITLE = "title";
@@ -269,6 +296,7 @@ public final class Downloads implements BaseColumns {
* user in the list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
+ * @hide
*/
public static final String COLUMN_DESCRIPTION = "description";
@@ -284,6 +312,7 @@ public final class Downloads implements BaseColumns {
* Downloads to the external destination only write files for which
* there is a registered handler. The resulting files are accessible
* by filename to all applications.
+ * @hide
*/
public static final int DESTINATION_EXTERNAL = 0;
@@ -295,6 +324,7 @@ public final class Downloads implements BaseColumns {
* application can access the file (indirectly through a content
* provider). This requires the
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION = 1;
@@ -304,6 +334,7 @@ public final class Downloads implements BaseColumns {
* for private files (similar to CACHE_PARTITION) that aren't deleted
* immediately after they are used, and are kept around by the download
* manager as long as space is available.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
@@ -311,16 +342,19 @@ public final class Downloads implements BaseColumns {
* This download will be saved to the download manager's private
* partition, as with DESTINATION_CACHE_PARTITION, but the download
* will not proceed if the user is on a roaming data connection.
+ * @hide
*/
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
/**
* This download is allowed to run.
+ * @hide
*/
public static final int CONTROL_RUN = 0;
/**
* This download must pause at the first opportunity.
+ * @hide
*/
public static final int CONTROL_PAUSED = 1;
@@ -337,6 +371,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the status is informational (i.e. 1xx).
+ * @hide
*/
public static boolean isStatusInformational(int status) {
return (status >= 100 && status < 200);
@@ -346,6 +381,7 @@ public final class Downloads implements BaseColumns {
* Returns whether the download is suspended. (i.e. whether the download
* won't complete without some action from outside the download
* manager).
+ * @hide
*/
public static boolean isStatusSuspended(int status) {
return (status == STATUS_PENDING_PAUSED || status == STATUS_RUNNING_PAUSED);
@@ -353,6 +389,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the status is a success (i.e. 2xx).
+ * @hide
*/
public static boolean isStatusSuccess(int status) {
return (status >= 200 && status < 300);
@@ -360,6 +397,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the status is an error (i.e. 4xx or 5xx).
+ * @hide
*/
public static boolean isStatusError(int status) {
return (status >= 400 && status < 600);
@@ -367,6 +405,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the status is a client error (i.e. 4xx).
+ * @hide
*/
public static boolean isStatusClientError(int status) {
return (status >= 400 && status < 500);
@@ -374,6 +413,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the status is a server error (i.e. 5xx).
+ * @hide
*/
public static boolean isStatusServerError(int status) {
return (status >= 500 && status < 600);
@@ -382,6 +422,7 @@ public final class Downloads implements BaseColumns {
/**
* Returns whether the download has completed (either with success or
* error).
+ * @hide
*/
public static boolean isStatusCompleted(int status) {
return (status >= 200 && status < 300) || (status >= 400 && status < 600);
@@ -389,21 +430,25 @@ public final class Downloads implements BaseColumns {
/**
* This download hasn't stated yet
+ * @hide
*/
public static final int STATUS_PENDING = 190;
/**
* This download hasn't stated yet and is paused
+ * @hide
*/
public static final int STATUS_PENDING_PAUSED = 191;
/**
* This download has started
+ * @hide
*/
public static final int STATUS_RUNNING = 192;
/**
* This download has started and is paused
+ * @hide
*/
public static final int STATUS_RUNNING_PAUSED = 193;
@@ -412,18 +457,21 @@ public final class Downloads implements BaseColumns {
* Warning: there might be other status values that indicate success
* in the future.
* Use isSucccess() to capture the entire category.
+ * @hide
*/
public static final int STATUS_SUCCESS = 200;
/**
* This request couldn't be parsed. This is also used when processing
* requests with unknown/unsupported URI schemes.
+ * @hide
*/
public static final int STATUS_BAD_REQUEST = 400;
/**
* This download can't be performed because the content type cannot be
* handled.
+ * @hide
*/
public static final int STATUS_NOT_ACCEPTABLE = 406;
@@ -435,6 +483,7 @@ public final class Downloads implements BaseColumns {
* client when a response is received whose length cannot be determined
* accurately (therefore making it impossible to know when a download
* completes).
+ * @hide
*/
public static final int STATUS_LENGTH_REQUIRED = 411;
@@ -442,11 +491,13 @@ public final class Downloads implements BaseColumns {
* This download was interrupted and cannot be resumed.
* This is the code for the HTTP error "Precondition Failed", and it is
* also used in situations where the client doesn't have an ETag at all.
+ * @hide
*/
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
* This download was canceled
+ * @hide
*/
public static final int STATUS_CANCELED = 490;
@@ -454,12 +505,14 @@ public final class Downloads implements BaseColumns {
* This download has completed with an error.
* Warning: there will be other status values that indicate errors in
* the future. Use isStatusError() to capture the entire category.
+ * @hide
*/
public static final int STATUS_UNKNOWN_ERROR = 491;
/**
* This download couldn't be completed because of a storage issue.
* Typically, that's because the filesystem is missing or full.
+ * @hide
*/
public static final int STATUS_FILE_ERROR = 492;
@@ -467,47 +520,550 @@ public final class Downloads implements BaseColumns {
* This download couldn't be completed because of an HTTP
* redirect response that the download manager couldn't
* handle.
+ * @hide
*/
public static final int STATUS_UNHANDLED_REDIRECT = 493;
/**
* This download couldn't be completed because of an
* unspecified unhandled HTTP code.
+ * @hide
*/
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
/**
* This download couldn't be completed because of an
* error receiving or processing data at the HTTP level.
+ * @hide
*/
public static final int STATUS_HTTP_DATA_ERROR = 495;
/**
* This download couldn't be completed because of an
* HttpException while setting up the request.
+ * @hide
*/
public static final int STATUS_HTTP_EXCEPTION = 496;
/**
* This download couldn't be completed because there were
* too many redirects.
+ * @hide
*/
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
* This download is visible but only shows in the notifications
* while it's in progress.
+ * @hide
*/
public static final int VISIBILITY_VISIBLE = 0;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
+ * @hide
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
/**
* This download doesn't show in the UI or in the notifications.
+ * @hide
*/
public static final int VISIBILITY_HIDDEN = 2;
+
+ /**
+ * Implementation details
+ *
+ * Exposes constants used to interact with the download manager's
+ * content provider.
+ * The constants URI ... STATUS are the names of columns in the downloads table.
+ *
+ * @hide
+ */
+ public static final class Impl implements BaseColumns {
+ private Impl() {}
+
+ /**
+ * The permission to access the download manager
+ */
+ public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
+
+ /**
+ * The permission to access the download manager's advanced functions
+ */
+ public static final String PERMISSION_ACCESS_ADVANCED =
+ "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
+
+ /**
+ * The permission to directly access the download manager's cache directory
+ */
+ public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
+
+ /**
+ * The permission to send broadcasts on download completion
+ */
+ public static final String PERMISSION_SEND_INTENTS =
+ "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
+
+ /**
+ * The content:// URI for the data table in the provider
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://downloads/download");
+
+ /**
+ * Broadcast Action: this is sent by the download manager to the app
+ * that had initiated a download when that download completes. The
+ * download's content: uri is specified in the intent's data.
+ */
+ public static final String ACTION_DOWNLOAD_COMPLETED =
+ "android.intent.action.DOWNLOAD_COMPLETED";
+
+ /**
+ * Broadcast Action: this is sent by the download manager to the app
+ * that had initiated a download when the user selects the notification
+ * associated with that download. The download's content: uri is specified
+ * in the intent's data if the click is associated with a single download,
+ * or Downloads.CONTENT_URI if the notification is associated with
+ * multiple downloads.
+ * Note: this is not currently sent for downloads that have completed
+ * successfully.
+ */
+ public static final String ACTION_NOTIFICATION_CLICKED =
+ "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
+
+ /**
+ * The name of the column containing the URI of the data being downloaded.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_URI = "uri";
+
+ /**
+ * The name of the column containing application-specific data.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read/Write</P>
+ */
+ public static final String COLUMN_APP_DATA = "entity";
+
+ /**
+ * The name of the column containing the flags that indicates whether
+ * the initiating application is capable of verifying the integrity of
+ * the downloaded file. When this flag is set, the download manager
+ * performs downloads and reports success even in some situations where
+ * it can't guarantee that the download has completed (e.g. when doing
+ * a byte-range request without an ETag, or when it can't determine
+ * whether a download fully completed).
+ * <P>Type: BOOLEAN</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_NO_INTEGRITY = "no_integrity";
+
+ /**
+ * The name of the column containing the filename that the initiating
+ * application recommends. When possible, the download manager will attempt
+ * to use this filename, or a variation, as the actual name for the file.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_FILE_NAME_HINT = "hint";
+
+ /**
+ * The name of the column containing the filename where the downloaded data
+ * was actually stored.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String _DATA = "_data";
+
+ /**
+ * The name of the column containing the MIME type of the downloaded data.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_MIME_TYPE = "mimetype";
+
+ /**
+ * The name of the column containing the flag that controls the destination
+ * of the download. See the DESTINATION_* constants for a list of legal values.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_DESTINATION = "destination";
+
+ /**
+ * The name of the column containing the flags that controls whether the
+ * download is displayed by the UI. See the VISIBILITY_* constants for
+ * a list of legal values.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Init/Read/Write</P>
+ */
+ public static final String COLUMN_VISIBILITY = "visibility";
+
+ /**
+ * The name of the column containing the current control state of the download.
+ * Applications can write to this to control (pause/resume) the download.
+ * the CONTROL_* constants for a list of legal values.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String COLUMN_CONTROL = "control";
+
+ /**
+ * The name of the column containing the current status of the download.
+ * Applications can read this to follow the progress of each download. See
+ * the STATUS_* constants for a list of legal values.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String COLUMN_STATUS = "status";
+
+ /**
+ * The name of the column containing the date at which some interesting
+ * status changed in the download. Stored as a System.currentTimeMillis()
+ * value.
+ * <P>Type: BIGINT</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String COLUMN_LAST_MODIFICATION = "lastmod";
+
+ /**
+ * The name of the column containing the package name of the application
+ * that initiating the download. The download manager will send
+ * notifications to a component in this package when the download completes.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
+
+ /**
+ * The name of the column containing the component name of the class that
+ * will receive notifications associated with the download. The
+ * package/class combination is passed to
+ * Intent.setClassName(String,String).
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
+
+ /**
+ * If extras are specified when requesting a download they will be provided in the intent that
+ * is sent to the specified class and package when a download has finished.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
+
+ /**
+ * The name of the column contain the values of the cookie to be used for
+ * the download. This is used directly as the value for the Cookie: HTTP
+ * header that gets sent with the request.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_COOKIE_DATA = "cookiedata";
+
+ /**
+ * The name of the column containing the user agent that the initiating
+ * application wants the download manager to use for this download.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_USER_AGENT = "useragent";
+
+ /**
+ * The name of the column containing the referer (sic) that the initiating
+ * application wants the download manager to use for this download.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_REFERER = "referer";
+
+ /**
+ * The name of the column containing the total size of the file being
+ * downloaded.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String COLUMN_TOTAL_BYTES = "total_bytes";
+
+ /**
+ * The name of the column containing the size of the part of the file that
+ * has been downloaded so far.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Read</P>
+ */
+ public static final String COLUMN_CURRENT_BYTES = "current_bytes";
+
+ /**
+ * The name of the column where the initiating application can provide the
+ * UID of another application that is allowed to access this download. If
+ * multiple applications share the same UID, all those applications will be
+ * allowed to access this download. This column can be updated after the
+ * download is initiated. This requires the permission
+ * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Init</P>
+ */
+ public static final String COLUMN_OTHER_UID = "otheruid";
+
+ /**
+ * The name of the column where the initiating application can provided the
+ * title of this download. The title will be displayed ito the user in the
+ * list of downloads.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read/Write</P>
+ */
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * The name of the column where the initiating application can provide the
+ * description of this download. The description will be displayed to the
+ * user in the list of downloads.
+ * <P>Type: TEXT</P>
+ * <P>Owner can Init/Read/Write</P>
+ */
+ public static final String COLUMN_DESCRIPTION = "description";
+
+ /*
+ * Lists the destinations that an application can specify for a download.
+ */
+
+ /**
+ * This download will be saved to the external storage. This is the
+ * default behavior, and should be used for any file that the user
+ * can freely access, copy, delete. Even with that destination,
+ * unencrypted DRM files are saved in secure internal storage.
+ * Downloads to the external destination only write files for which
+ * there is a registered handler. The resulting files are accessible
+ * by filename to all applications.
+ */
+ public static final int DESTINATION_EXTERNAL = 0;
+
+ /**
+ * This download will be saved to the download manager's private
+ * partition. This is the behavior used by applications that want to
+ * download private files that are used and deleted soon after they
+ * get downloaded. All file types are allowed, and only the initiating
+ * application can access the file (indirectly through a content
+ * provider). This requires the
+ * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
+ */
+ public static final int DESTINATION_CACHE_PARTITION = 1;
+
+ /**
+ * This download will be saved to the download manager's private
+ * partition and will be purged as necessary to make space. This is
+ * for private files (similar to CACHE_PARTITION) that aren't deleted
+ * immediately after they are used, and are kept around by the download
+ * manager as long as space is available.
+ */
+ public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
+
+ /**
+ * This download will be saved to the download manager's private
+ * partition, as with DESTINATION_CACHE_PARTITION, but the download
+ * will not proceed if the user is on a roaming data connection.
+ */
+ public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
+
+ /**
+ * This download is allowed to run.
+ */
+ public static final int CONTROL_RUN = 0;
+
+ /**
+ * This download must pause at the first opportunity.
+ */
+ public static final int CONTROL_PAUSED = 1;
+
+ /*
+ * Lists the states that the download manager can set on a download
+ * to notify applications of the download progress.
+ * The codes follow the HTTP families:<br>
+ * 1xx: informational<br>
+ * 2xx: success<br>
+ * 3xx: redirects (not used by the download manager)<br>
+ * 4xx: client errors<br>
+ * 5xx: server errors
+ */
+
+ /**
+ * Returns whether the status is informational (i.e. 1xx).
+ */
+ public static boolean isStatusInformational(int status) {
+ return (status >= 100 && status < 200);
+ }
+
+ /**
+ * Returns whether the download is suspended. (i.e. whether the download
+ * won't complete without some action from outside the download
+ * manager).
+ */
+ public static boolean isStatusSuspended(int status) {
+ return (status == STATUS_PENDING_PAUSED || status == STATUS_RUNNING_PAUSED);
+ }
+
+ /**
+ * Returns whether the status is a success (i.e. 2xx).
+ */
+ public static boolean isStatusSuccess(int status) {
+ return (status >= 200 && status < 300);
+ }
+
+ /**
+ * Returns whether the status is an error (i.e. 4xx or 5xx).
+ */
+ public static boolean isStatusError(int status) {
+ return (status >= 400 && status < 600);
+ }
+
+ /**
+ * Returns whether the status is a client error (i.e. 4xx).
+ */
+ public static boolean isStatusClientError(int status) {
+ return (status >= 400 && status < 500);
+ }
+
+ /**
+ * Returns whether the status is a server error (i.e. 5xx).
+ */
+ public static boolean isStatusServerError(int status) {
+ return (status >= 500 && status < 600);
+ }
+
+ /**
+ * Returns whether the download has completed (either with success or
+ * error).
+ */
+ public static boolean isStatusCompleted(int status) {
+ return (status >= 200 && status < 300) || (status >= 400 && status < 600);
+ }
+
+ /**
+ * This download hasn't stated yet
+ */
+ public static final int STATUS_PENDING = 190;
+
+ /**
+ * This download hasn't stated yet and is paused
+ */
+ public static final int STATUS_PENDING_PAUSED = 191;
+
+ /**
+ * This download has started
+ */
+ public static final int STATUS_RUNNING = 192;
+
+ /**
+ * This download has started and is paused
+ */
+ public static final int STATUS_RUNNING_PAUSED = 193;
+
+ /**
+ * This download has successfully completed.
+ * Warning: there might be other status values that indicate success
+ * in the future.
+ * Use isSucccess() to capture the entire category.
+ */
+ public static final int STATUS_SUCCESS = 200;
+
+ /**
+ * This request couldn't be parsed. This is also used when processing
+ * requests with unknown/unsupported URI schemes.
+ */
+ public static final int STATUS_BAD_REQUEST = 400;
+
+ /**
+ * This download can't be performed because the content type cannot be
+ * handled.
+ */
+ public static final int STATUS_NOT_ACCEPTABLE = 406;
+
+ /**
+ * This download cannot be performed because the length cannot be
+ * determined accurately. This is the code for the HTTP error "Length
+ * Required", which is typically used when making requests that require
+ * a content length but don't have one, and it is also used in the
+ * client when a response is received whose length cannot be determined
+ * accurately (therefore making it impossible to know when a download
+ * completes).
+ */
+ public static final int STATUS_LENGTH_REQUIRED = 411;
+
+ /**
+ * This download was interrupted and cannot be resumed.
+ * This is the code for the HTTP error "Precondition Failed", and it is
+ * also used in situations where the client doesn't have an ETag at all.
+ */
+ public static final int STATUS_PRECONDITION_FAILED = 412;
+
+ /**
+ * This download was canceled
+ */
+ public static final int STATUS_CANCELED = 490;
+
+ /**
+ * This download has completed with an error.
+ * Warning: there will be other status values that indicate errors in
+ * the future. Use isStatusError() to capture the entire category.
+ */
+ public static final int STATUS_UNKNOWN_ERROR = 491;
+
+ /**
+ * This download couldn't be completed because of a storage issue.
+ * Typically, that's because the filesystem is missing or full.
+ */
+ public static final int STATUS_FILE_ERROR = 492;
+
+ /**
+ * This download couldn't be completed because of an HTTP
+ * redirect response that the download manager couldn't
+ * handle.
+ */
+ public static final int STATUS_UNHANDLED_REDIRECT = 493;
+
+ /**
+ * This download couldn't be completed because of an
+ * unspecified unhandled HTTP code.
+ */
+ public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
+
+ /**
+ * This download couldn't be completed because of an
+ * error receiving or processing data at the HTTP level.
+ */
+ public static final int STATUS_HTTP_DATA_ERROR = 495;
+
+ /**
+ * This download couldn't be completed because of an
+ * HttpException while setting up the request.
+ */
+ public static final int STATUS_HTTP_EXCEPTION = 496;
+
+ /**
+ * This download couldn't be completed because there were
+ * too many redirects.
+ */
+ public static final int STATUS_TOO_MANY_REDIRECTS = 497;
+
+ /**
+ * This download is visible but only shows in the notifications
+ * while it's in progress.
+ */
+ public static final int VISIBILITY_VISIBLE = 0;
+
+ /**
+ * This download is visible and shows in the notifications while
+ * in progress and after completion.
+ */
+ public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+
+ /**
+ * This download doesn't show in the UI or in the notifications.
+ */
+ public static final int VISIBILITY_HIDDEN = 2;
+
+ }
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index fe3b149..eb48a0c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,6 +20,8 @@ import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -58,9 +60,13 @@ import java.util.ArrayList;
public abstract class WallpaperService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
+ * that other applications can not abuse it.
*/
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
- "android.service.wallpaper.WallpaperService";
+ "android.service.wallpaper.WallpaperService";
/**
* Name under which a WallpaperService component publishes information
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
new file mode 100644
index 0000000..2da8f14
--- /dev/null
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
+ * callback will notify users when a particular gesture event has occurred.
+ * This class should only be used with {@link MotionEvent}s reported via touch.
+ *
+ * To use this class:
+ * <ul>
+ * <li>Create an instance of the {@code ScaleGestureDetector} for your
+ * {@link View}
+ * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ * callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class ScaleGestureDetector {
+ /**
+ * The listener for receiving notifications when gestures occur.
+ * If you want to listen for all the different gestures then implement
+ * this interface. If you only want to listen for a subset it might
+ * be easier to extend {@link SimpleOnScaleGestureListener}.
+ *
+ * An application will receive events in the following order:
+ * <ul>
+ * <li>One {@link OnScaleGestureListener#onScaleBegin()}
+ * <li>Zero or more {@link OnScaleGestureListener#onScale()}
+ * <li>One {@link OnScaleGestureListener#onTransformEnd()}
+ * </ul>
+ */
+ public interface OnScaleGestureListener {
+ /**
+ * Responds to scaling events for a gesture in progress.
+ * Reported by pointer motion.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should consider this event
+ * as handled. If an event was not handled, the detector
+ * will continue to accumulate movement until an event is
+ * handled. This can be useful if an application, for example,
+ * only wants to update scaling factors if the change is
+ * greater than 0.01.
+ */
+ public boolean onScale(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the beginning of a scaling gesture. Reported by
+ * new pointers going down.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should continue recognizing
+ * this gesture. For example, if a gesture is beginning
+ * with a focal point outside of a region where it makes
+ * sense, onScaleBegin() may return false to ignore the
+ * rest of the gesture.
+ */
+ public boolean onScaleBegin(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the end of a scale gesture. Reported by existing
+ * pointers going up. If the end of a gesture would result in a fling,
+ * {@link onTransformFling()} is called instead.
+ *
+ * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
+ * and {@link ScaleGestureDetector#getFocusY()} will return the location
+ * of the pointer remaining on the screen.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ */
+ public void onScaleEnd(ScaleGestureDetector detector);
+ }
+
+ /**
+ * A convenience class to extend when you only want to listen for a subset
+ * of scaling-related events. This implements all methods in
+ * {@link OnScaleGestureListener} but does nothing.
+ * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
+ * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
+ * {@code true}.
+ */
+ public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+
+ public boolean onScale(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ // Intentionally empty
+ }
+ }
+
+ private static final float PRESSURE_THRESHOLD = 0.67f;
+
+ private Context mContext;
+ private OnScaleGestureListener mListener;
+ private boolean mGestureInProgress;
+
+ private MotionEvent mPrevEvent;
+ private MotionEvent mCurrEvent;
+
+ private float mFocusX;
+ private float mFocusY;
+ private float mPrevFingerDiffX;
+ private float mPrevFingerDiffY;
+ private float mCurrFingerDiffX;
+ private float mCurrFingerDiffY;
+ private float mCurrLen;
+ private float mPrevLen;
+ private float mScaleFactor;
+ private float mCurrPressure;
+ private float mPrevPressure;
+ private long mTimeDelta;
+
+ public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ mContext = context;
+ mListener = listener;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ final int action = event.getAction();
+ boolean handled = true;
+
+ if (!mGestureInProgress) {
+ if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+ action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+ event.getPointerCount() >= 2) {
+ // We have a new multi-finger gesture
+
+ // Be paranoid in case we missed an event
+ reset();
+
+ mPrevEvent = MotionEvent.obtain(event);
+ mTimeDelta = 0;
+
+ setContext(event);
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
+ } else {
+ // Transform gesture in progress - attempt to handle it
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_1_UP:
+ case MotionEvent.ACTION_POINTER_2_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;
+ mFocusX = event.getX(id);
+ mFocusY = event.getY(id);
+
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ setContext(event);
+
+ // Only accept the event if our relative pressure is within
+ // a certain limit - this can help filter shaky data as a
+ // finger is lifted.
+ if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
+ final boolean updatePrevious = mListener.onScale(this);
+
+ if (updatePrevious) {
+ mPrevEvent.recycle();
+ mPrevEvent = MotionEvent.obtain(event);
+ }
+ }
+ break;
+ }
+ }
+ return handled;
+ }
+
+ private void setContext(MotionEvent curr) {
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ }
+ mCurrEvent = MotionEvent.obtain(curr);
+
+ mCurrLen = -1;
+ mPrevLen = -1;
+ mScaleFactor = -1;
+
+ final MotionEvent prev = mPrevEvent;
+
+ final float px0 = prev.getX(0);
+ final float py0 = prev.getY(0);
+ final float px1 = prev.getX(1);
+ final float py1 = prev.getY(1);
+ final float cx0 = curr.getX(0);
+ final float cy0 = curr.getY(0);
+ final float cx1 = curr.getX(1);
+ final float cy1 = curr.getY(1);
+
+ final float pvx = px1 - px0;
+ final float pvy = py1 - py0;
+ final float cvx = cx1 - cx0;
+ final float cvy = cy1 - cy0;
+ mPrevFingerDiffX = pvx;
+ mPrevFingerDiffY = pvy;
+ mCurrFingerDiffX = cvx;
+ mCurrFingerDiffY = cvy;
+
+ mFocusX = cx0 + cvx * 0.5f;
+ mFocusY = cy0 + cvy * 0.5f;
+ mTimeDelta = curr.getEventTime() - prev.getEventTime();
+ mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
+ mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
+ }
+
+ private void reset() {
+ if (mPrevEvent != null) {
+ mPrevEvent.recycle();
+ mPrevEvent = null;
+ }
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ mCurrEvent = null;
+ }
+ }
+
+ /**
+ * Returns {@code true} if a two-finger scale gesture is in progress.
+ * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ */
+ public boolean isInProgress() {
+ return mGestureInProgress;
+ }
+
+ /**
+ * Get the X coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return X coordinate of the focal point in pixels.
+ */
+ public float getFocusX() {
+ return mFocusX;
+ }
+
+ /**
+ * Get the Y coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return Y coordinate of the focal point in pixels.
+ */
+ public float getFocusY() {
+ return mFocusY;
+ }
+
+ /**
+ * Return the current distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Distance between pointers in pixels.
+ */
+ public float getCurrentSpan() {
+ if (mCurrLen == -1) {
+ final float cvx = mCurrFingerDiffX;
+ final float cvy = mCurrFingerDiffY;
+ mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+ }
+ return mCurrLen;
+ }
+
+ /**
+ * Return the previous distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Previous distance between pointers in pixels.
+ */
+ public float getPreviousSpan() {
+ if (mPrevLen == -1) {
+ final float pvx = mPrevFingerDiffX;
+ final float pvy = mPrevFingerDiffY;
+ mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+ }
+ return mPrevLen;
+ }
+
+ /**
+ * Return the scaling factor from the previous scale event to the current
+ * event. This value is defined as
+ * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+ *
+ * @return The current scaling factor.
+ */
+ public float getScaleFactor() {
+ if (mScaleFactor == -1) {
+ mScaleFactor = getCurrentSpan() / getPreviousSpan();
+ }
+ return mScaleFactor;
+ }
+
+ /**
+ * Return the time difference in milliseconds between the previous
+ * accepted scaling event and the current scaling event.
+ *
+ * @return Time difference since the last scaling event in milliseconds.
+ */
+ public long getTimeDelta() {
+ return mTimeDelta;
+ }
+
+ /**
+ * Return the event time of the current event being processed.
+ *
+ * @return Current event time in milliseconds.
+ */
+ public long getEventTime() {
+ return mCurrEvent.getEventTime();
+ }
+}
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
deleted file mode 100644
index 196716a..0000000
--- a/core/java/android/view/TransformGestureDetector.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector.SimpleOnGestureListener;
-
-/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
- * will notify users when a particular gesture event has occurred. This class
- * should only be used with {@link MotionEvent}s reported via touch.
- *
- * To use this class:
- * <ul>
- * <li>Create an instance of the {@code TransformGestureDetector} for your
- * {@link View}
- * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- * callback will be executed when the events occur.
- * </ul>
- * @hide Pending API approval
- */
-public class TransformGestureDetector {
- /**
- * The listener for receiving notifications when gestures occur.
- * If you want to listen for all the different gestures then implement
- * this interface. If you only want to listen for a subset it might
- * be easier to extend {@link SimpleOnGestureListener}.
- *
- * An application will receive events in the following order:
- * One onTransformBegin()
- * Zero or more onTransform()
- * One onTransformEnd() or onTransformFling()
- */
- public interface OnTransformGestureListener {
- /**
- * Responds to transformation events for a gesture in progress.
- * Reported by pointer motion.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransform(TransformGestureDetector detector);
-
- /**
- * Responds to the beginning of a transformation gesture. Reported by
- * new pointers going down.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformBegin(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture. Reported by existing
- * pointers going up. If the end of a gesture would result in a fling,
- * onTransformFling is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformEnd(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture that begins a fling.
- * Reported by existing pointers going up. If the end of a gesture
- * would not result in a fling, onTransformEnd is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformFling(TransformGestureDetector detector);
- }
-
- private static final boolean DEBUG = false;
-
- private static final int INITIAL_EVENT_IGNORES = 2;
-
- private Context mContext;
- private float mTouchSizeScale;
- private OnTransformGestureListener mListener;
- private int mVelocityTimeUnits;
- private MotionEvent mInitialEvent;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
- private VelocityTracker mVelocityTracker;
-
- private float mCenterX;
- private float mCenterY;
- private float mTransX;
- private float mTransY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mRotateDegrees;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
-
- // Units in pixels. Current value is pulled out of thin air for debugging only.
- private float mPointerJumpLimit = 30;
-
- private int mEventIgnoreCount;
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener,
- int velocityTimeUnits) {
- mContext = context;
- mListener = listener;
- mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
- mVelocityTimeUnits = velocityTimeUnits;
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
- this(context, listener, 1000);
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- boolean handled = true;
-
- if (mInitialEvent == null) {
- // No transform gesture in progress
- if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
- action == MotionEvent.ACTION_POINTER_2_DOWN) &&
- event.getPointerCount() >= 2) {
- // We have a new multi-finger gesture
- mInitialEvent = MotionEvent.obtain(event);
- mPrevEvent = MotionEvent.obtain(event);
- mVelocityTracker = VelocityTracker.obtain();
- handled = mListener.onTransformBegin(this);
- }
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_1_UP:
- case MotionEvent.ACTION_POINTER_2_UP:
- // Gesture ended
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE:
- setContext(event);
-
- // Our first few events can be crazy from some touchscreens - drop them.
- if (mEventIgnoreCount == 0) {
- mVelocityTracker.addMovement(event);
- handled = mListener.onTransform(this);
- } else {
- mEventIgnoreCount--;
- }
-
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- break;
- }
- }
- return handled;
- }
-
- private void setContext(MotionEvent curr) {
- mCurrEvent = MotionEvent.obtain(curr);
-
- mRotateDegrees = -1;
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
-
- final MotionEvent prev = mPrevEvent;
-
- float px0 = prev.getX(0);
- float py0 = prev.getY(0);
- float px1 = prev.getX(1);
- float py1 = prev.getY(1);
- float cx0 = curr.getX(0);
- float cy0 = curr.getY(0);
- float cx1 = curr.getX(1);
- float cy1 = curr.getY(1);
-
- // Some touchscreens do weird things with pointer values where points are
- // too close along one axis. Try to detect this here and smooth things out.
- // The main indicator is that we get the X or Y value from the other pointer.
- final float dx0 = cx0 - px0;
- final float dy0 = cy0 - py0;
- final float dx1 = cx1 - px1;
- final float dy1 = cy1 - py1;
-
- if (cx0 == cx1) {
- if (Math.abs(dx0) > mPointerJumpLimit) {
- cx0 = px0;
- } else if (Math.abs(dx1) > mPointerJumpLimit) {
- cx1 = px1;
- }
- } else if (cy0 == cy1) {
- if (Math.abs(dy0) > mPointerJumpLimit) {
- cy0 = py0;
- } else if (Math.abs(dy1) > mPointerJumpLimit) {
- cy1 = py1;
- }
- }
-
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- final float pmidx = px0 + pvx * 0.5f;
- final float pmidy = py0 + pvy * 0.5f;
- final float cmidx = cx0 + cvx * 0.5f;
- final float cmidy = cy0 + cvy * 0.5f;
-
- mCenterX = cmidx;
- mCenterY = cmidy;
- mTransX = cmidx - pmidx;
- mTransY = cmidy - pmidy;
- }
-
- private void reset() {
- if (mInitialEvent != null) {
- mInitialEvent.recycle();
- mInitialEvent = null;
- }
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
- }
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public float getCenterX() {
- return mCenterX;
- }
-
- public float getCenterY() {
- return mCenterY;
- }
-
- public float getTranslateX() {
- return mTransX;
- }
-
- public float getTranslateY() {
- return mTransY;
- }
-
- public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
- }
-
- public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
- }
-
- public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
- }
-
- public float getRotation() {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index a5e0e94..2ddf5f8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -16,6 +16,8 @@
package android.view.inputmethod;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ResultReceiver;
@@ -54,9 +56,12 @@ public interface InputMethod {
/**
* This is the interface name that a service implementing an input
* method should say that it supports -- that is, this is the action it
- * uses for its intent filter. (Note: this name is used because this
- * interface should be moved to the view package.)
+ * uses for its intent filter.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission so
+ * that other applications can not abuse it.
*/
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.view.InputMethod";
/**
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index f350d13..e496d97 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -247,4 +247,5 @@ final class JWebCoreJavaBridge extends Handler {
private native void nativeUpdatePluginDirectories(String[] directories,
boolean reload);
public native void setNetworkOnLine(boolean online);
+ public native void setNetworkType(String type, String subtype);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 09ed931..db5641c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -80,6 +80,7 @@ import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
+import java.util.HashMap;
import java.util.Map;
import junit.framework.Assert;
@@ -1132,6 +1133,16 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Inform WebView about the current network type.
+ * {@hide}
+ */
+ public void setNetworkType(String type, String subtype) {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("type", type);
+ map.put("subtype", subtype);
+ mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
+ }
+ /**
* Save the state of this WebView used in
* {@link android.app.Activity#onSaveInstanceState}. Please note that this
* method no longer stores the display data for this WebView. The previous
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 949b318..d509bb4 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -878,6 +878,8 @@ final class WebViewCore {
static final int HIDE_FULLSCREEN = 182;
+ static final int SET_NETWORK_TYPE = 183;
+
// private message ids
private static final int DESTROY = 200;
@@ -1110,6 +1112,16 @@ final class WebViewCore {
.setNetworkOnLine(msg.arg1 == 1);
break;
+ case SET_NETWORK_TYPE:
+ if (BrowserFrame.sJavaBridge == null) {
+ throw new IllegalStateException("No WebView " +
+ "has been created in this process!");
+ }
+ Map<String, String> map = (Map<String, String>) msg.obj;
+ BrowserFrame.sJavaBridge
+ .setNetworkType(map.get("type"), map.get("subtype"));
+ break;
+
case CLEAR_CACHE:
clearCache(msg.arg1 == 1);
break;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 37372c5..0ce70fa 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -50,6 +50,7 @@ public class LinearLayout extends ViewGroup {
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
+ @ViewDebug.ExportedProperty
private boolean mBaselineAligned = true;
/**
@@ -59,6 +60,7 @@ public class LinearLayout extends ViewGroup {
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
+ @ViewDebug.ExportedProperty
private int mBaselineAlignedChildIndex = -1;
/**
@@ -66,12 +68,30 @@ public class LinearLayout extends ViewGroup {
* We'll calculate the baseline of this layout as we measure vertically; for
* horizontal linear layouts, the offset of 0 is appropriate.
*/
+ @ViewDebug.ExportedProperty
private int mBaselineChildTop = 0;
+ @ViewDebug.ExportedProperty
private int mOrientation;
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = -1, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
+ @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
+ @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
+ @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
+ })
private int mGravity = Gravity.LEFT | Gravity.TOP;
+ @ViewDebug.ExportedProperty
private int mTotalLength;
+ @ViewDebug.ExportedProperty
private float mWeightSum;
private int[] mMaxAscent;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c49a86a..d81476a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -927,21 +927,27 @@
android:description="@string/permdesc_readInputState"
android:protectionLevel="signature" />
- <!-- Must be required by input method services, to ensure that only the
- system can bind to them. -->
+ <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
+ to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_INPUT_METHOD"
android:label="@string/permlab_bindInputMethod"
android:description="@string/permdesc_bindInputMethod"
android:protectionLevel="signature" />
- <!-- Must be required by wallpaper services, to ensure that only the
- system can bind to them.
- @hide Live Wallpaper -->
+ <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
+ to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_WALLPAPER"
android:label="@string/permlab_bindWallpaper"
android:description="@string/permdesc_bindWallpaper"
android:protectionLevel="signatureOrSystem" />
+ <!-- Must be required by device administration receiver, to ensure that only the
+ system can interact with it. -->
+ <permission android:name="android.permission.BIND_DEVICE_ADMIN"
+ android:label="@string/permlab_bindDeviceAdmin"
+ android:description="@string/permdesc_bindDeviceAdmin"
+ android:protectionLevel="signature" />
+
<!-- Allows low-level access to setting the orientation (actually
rotation) of the screen. Not for use by normal applications. -->
<permission android:name="android.permission.SET_ORIENTATION"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 53bb586..3dbfa25 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -131,7 +131,8 @@
the first component from this list which is found to be installed is set as the
preferred activity. -->
<string-array name="default_web_search_providers">
- <item>com.android.quicksearchbox/com.android.googlesearch.GoogleSearch</item>
+ <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item>
+ <item>com.android.quicksearchbox/.google.GoogleSearch</item>
<item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
<item>com.android.googlesearch/.GoogleSearch</item>
<item>com.android.websearch/.Search.1</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 539db83..265dacd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -617,6 +617,12 @@
interface of a wallpaper. Should never be needed for normal applications.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindDeviceAdmin">interact with a device admin</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindDeviceAdmin">Allows the holder to send intents to
+ a device administrator. Should never be needed for normal applications.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_setOrientation">change screen orientation</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_setOrientation">Allows an application to change
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index 452125a..61a4293 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -35,6 +35,7 @@ import javax.crypto.SecretKey;
public class SystemKeyStore {
private static final String SYSTEM_KEYSTORE_DIRECTORY = "misc/systemkeys";
+ private static final String KEY_FILE_EXTENSION = ".sks";
private static SystemKeyStore mInstance = new SystemKeyStore();
private SystemKeyStore() { }
@@ -43,6 +44,28 @@ public class SystemKeyStore {
return mInstance;
}
+ public static String toHexString(byte[] keyData) {
+ if (keyData == null) {
+ return null;
+ }
+ int keyLen = keyData.length;
+ int expectedStringLen = keyData.length * 2;
+ StringBuilder sb = new StringBuilder(expectedStringLen);
+ for (int i = 0; i < keyData.length; i++) {
+ String hexStr = Integer.toString(keyData[i] & 0x00FF, 16);
+ if (hexStr.length() == 1) {
+ hexStr = "0" + hexStr;
+ }
+ sb.append(hexStr);
+ }
+ return sb.toString();
+ }
+
+ public String generateNewKeyHexString(int numBits, String algName, String keyName)
+ throws NoSuchAlgorithmException {
+ return toHexString(generateNewKey(numBits, algName, keyName));
+ }
+
public byte[] generateNewKey(int numBits, String algName, String keyName)
throws NoSuchAlgorithmException {
@@ -78,10 +101,14 @@ public class SystemKeyStore {
private File getKeyFile(String keyName) {
File sysKeystoreDir = new File(Environment.getDataDirectory(),
SYSTEM_KEYSTORE_DIRECTORY);
- File keyFile = new File(sysKeystoreDir, keyName);
+ File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION);
return keyFile;
}
+ public String retrieveKeyHexString(String keyName) {
+ return toHexString(retrieveKey(keyName));
+ }
+
public byte[] retrieveKey(String keyName) {
File keyFile = getKeyFile(keyName);
diff --git a/keystore/tests/src/android/security/SystemKeyStoreTest.java b/keystore/tests/src/android/security/SystemKeyStoreTest.java
index a85f889..a9e2687 100644
--- a/keystore/tests/src/android/security/SystemKeyStoreTest.java
+++ b/keystore/tests/src/android/security/SystemKeyStoreTest.java
@@ -32,6 +32,7 @@ import android.test.suitebuilder.annotation.MediumTest;
public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> {
private static final String keyName = "TestKey";
+ private static final String keyName2 = "TestKey2";
private SystemKeyStore mSysKeyStore = null;
public SystemKeyStoreTest() {
@@ -43,6 +44,7 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> {
mSysKeyStore = SystemKeyStore.getInstance();
try {
mSysKeyStore.deleteKey(keyName);
+ mSysKeyStore.deleteKey(keyName2);
} catch (Exception e) { }
super.setUp();
}
@@ -51,13 +53,14 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> {
protected void tearDown() throws Exception {
try {
mSysKeyStore.deleteKey(keyName);
+ mSysKeyStore.deleteKey(keyName2);
} catch (Exception e) { }
super.tearDown();
}
public void testBasicAccess() throws Exception {
try {
- byte[] newKey = mSysKeyStore.generateNewKey(128, "AES", keyName);
+ byte[] newKey = mSysKeyStore.generateNewKey(128, "Blowfish", keyName);
assertNotNull(newKey);
byte[] recKey = mSysKeyStore.retrieveKey(keyName);
assertEquals(newKey.length, recKey.length);
@@ -67,6 +70,14 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> {
mSysKeyStore.deleteKey(keyName);
byte[] nullKey = mSysKeyStore.retrieveKey(keyName);
assertNull(nullKey);
+
+ String newKeyStr = mSysKeyStore.generateNewKeyHexString(128, "AES", keyName2);
+ assertNotNull(newKeyStr);
+ String recKeyStr = mSysKeyStore.retrieveKeyHexString(keyName2);
+ assertEquals(newKeyStr, recKeyStr);
+ mSysKeyStore.deleteKey(keyName2);
+ String nullKey2 = mSysKeyStore.retrieveKeyHexString(keyName);
+ assertNull(nullKey2);
} catch (Exception e) {
fail();
}
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 8c3f252..0d617a5 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -135,50 +135,6 @@ void OMX::CallbackDispatcher::threadEntry() {
////////////////////////////////////////////////////////////////////////////////
-class BufferMeta {
-public:
- BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
- : mOwner(owner),
- mMem(mem),
- mIsBackup(is_backup) {
- }
-
- BufferMeta(OMX *owner, size_t size)
- : mOwner(owner),
- mSize(size),
- mIsBackup(false) {
- }
-
- void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
- return;
- }
-
- memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
- header->pBuffer + header->nOffset,
- header->nFilledLen);
- }
-
- void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
- return;
- }
-
- memcpy(header->pBuffer + header->nOffset,
- (const OMX_U8 *)mMem->pointer() + header->nOffset,
- header->nFilledLen);
- }
-
-private:
- OMX *mOwner;
- sp<IMemory> mMem;
- size_t mSize;
- bool mIsBackup;
-
- BufferMeta(const BufferMeta &);
- BufferMeta &operator=(const BufferMeta &);
-};
-
OMX::OMX()
: mMaster(new OMXMaster),
mDispatcher(new CallbackDispatcher(this)),
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 2e23899..5b45c1c 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -586,14 +586,14 @@ status_t Harness::testSeek(
double r = uniform_rand();
- if (r < 0.5) {
+ if (i > 0 && r < 0.5) {
// 50% chance of just continuing to decode from last position.
requestedSeekTimeUs = -1;
LOGI("requesting linear read");
} else {
- if (r < 0.55) {
+ if (i > 0 && r < 0.55) {
// 5% chance of seeking beyond end of stream.
requestedSeekTimeUs = durationUs;
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index c77424f..322f743 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -86,7 +86,7 @@ static void millisecondSleep(int milliseconds) {
static int mount(const char* path) {
String16 string(path);
- gMountService->mountMedia(string);
+ gMountService->mountVolume(string);
for (int i = 0; i < 60; i++) {
if (isMounted(path)) {
@@ -129,6 +129,11 @@ static int asec_mount(const char *id, const char *key, int ownerUid) {
return 0;
}
+static void asec_unmount(const char *id) {
+ String16 sId(id);
+ gMountService->unmountSecureContainer(sId);
+}
+
static int asec_path(const char *id) {
String16 sId(id);
gMountService->getSecureContainerPath(sId);
@@ -137,7 +142,7 @@ static int asec_path(const char *id) {
static int unmount(const char* path) {
String16 string(path);
- gMountService->unmountMedia(string);
+ gMountService->unmountVolume(string);
for (int i = 0; i < 20; i++) {
if (!isMounted(path)) {
@@ -155,7 +160,7 @@ static int format(const char* path) {
if (isMounted(path))
return -EBUSY;
- gMountService->formatMedia(string);
+ gMountService->formatVolume(string);
return 0;
}
@@ -208,6 +213,9 @@ int main(int argc, char **argv)
return android::asec_destroy(id);
} else if (!strcmp(argument, "mount")) {
return android::asec_mount(id, argv[4], atoi(argv[5]));
+ } else if (!strcmp(argument, "unmount")) {
+ android::asec_unmount(id);
+ return 0;
} else if (!strcmp(argument, "path")) {
return android::asec_path(id);
}
@@ -224,6 +232,7 @@ usage:
" sdutil asec finalize <id>\n"
" sdutil asec destroy <id>\n"
" sdutil asec mount <id> <key> <ownerUid>\n"
+ " sdutil asec unmount <id>\n"
" sdutil asec path <id>\n"
);
return -1;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 471625e..a186e58 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -157,6 +157,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*
* @see #getRenderMode()
* @see #setRenderMode(int)
+ * @see #requestRender()
*/
public final static int RENDERMODE_WHEN_DIRTY = 0;
/**
@@ -165,7 +166,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*
* @see #getRenderMode()
* @see #setRenderMode(int)
- * @see #requestRender()
*/
public final static int RENDERMODE_CONTINUOUSLY = 1;
@@ -210,6 +210,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
+ // 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);
}
/**
@@ -579,7 +582,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Called when the surface is created or recreated.
* <p>
* Called when the rendering thread
- * starts and whenever the EGL context is lost. The context will typically
+ * starts and whenever the EGL context is lost. The EGL context will typically
* be lost when the Android device awakes after going to sleep.
* <p>
* Since this method is called at the beginning of rendering, as well as
@@ -871,7 +874,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void start(){
+ public void start() {
/*
* Get an EGL instance
*/
@@ -896,8 +899,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
/*
- * Create an OpenGL ES context. This must be done only once, an
- * OpenGL context is a somewhat heavy object.
+ * Create an EGL context. We want to do this as rarely as we can, because an
+ * EGL context is a somewhat heavy object.
*/
mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
@@ -1010,6 +1013,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
+
}
/**
@@ -1031,6 +1035,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mRenderer = renderer;
}
+
@Override
public void run() {
setName("GLThread " + getId());
@@ -1051,20 +1056,33 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* This private method should only be called inside a
* synchronized(sGLThreadManager) block.
*/
- private void stopEglLocked() {
- if (mHaveEgl) {
- mHaveEgl = false;
+ private void stopEglSurfaceLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
mEglHelper.destroySurface();
- mEglHelper.finish();
- sGLThreadManager.releaseEglSurfaceLocked(this);
}
}
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
+ if (mHaveEglContext) {
+ mEglHelper.finish();
+ mHaveEglContext = false;
+ sGLThreadManager.releaseEglContextLocked(this);
+ }
+ }
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper();
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
try {
GL10 gl = null;
+ boolean createEglContext = false;
boolean createEglSurface = false;
+ boolean lostEglContext = false;
boolean sizeChanged = false;
boolean wantRenderNotification = false;
boolean doRenderNotification = false;
@@ -1084,12 +1102,25 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
break;
}
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
+
// Do we need to release the EGL surface?
- if (mHaveEgl && mPaused) {
+ if (mHaveEglSurface && mPaused) {
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
}
- stopEglLocked();
+ stopEglSurfaceLocked();
+ if (sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+ stopEglContextLocked();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+ }
+ }
}
// Have we lost the surface view surface?
@@ -1097,8 +1128,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (LOG_SURFACE) {
Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
}
- if (mHaveEgl) {
- stopEglLocked();
+ if (mHaveEglSurface) {
+ stopEglSurfaceLocked();
}
mWaitingForSurface = true;
sGLThreadManager.notifyAll();
@@ -1125,16 +1156,22 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
&& (mWidth > 0) && (mHeight > 0)
&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
- // If we don't have an egl surface, try to acquire one.
- if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
- mHaveEgl = true;
+ // If we don't have an EGL context, try to acquire one.
+ if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ mHaveEglContext = true;
+ createEglContext = true;
mEglHelper.start();
+
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
createEglSurface = true;
sizeChanged = true;
- sGLThreadManager.notifyAll();
}
- if (mHaveEgl) {
+ if (mHaveEglSurface) {
if (mSizeChanged) {
sizeChanged = true;
w = mWidth;
@@ -1177,10 +1214,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceCreated");
}
- mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
createEglSurface = false;
}
+ if (createEglContext) {
+ mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ createEglContext = false;
+ }
+
if (sizeChanged) {
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
@@ -1193,22 +1234,25 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
Log.w("GLThread", "onDrawFrame");
}
mRenderer.onDrawFrame(gl);
- if(!mEglHelper.swap()) {
+ if (!mEglHelper.swap()) {
if (LOG_SURFACE) {
- Log.i("GLThread", "egl surface lost tid=" + getId());
+ Log.i("GLThread", "egl context lost tid=" + getId());
}
+ lostEglContext = true;
}
if (wantRenderNotification) {
doRenderNotification = true;
}
}
+
} finally {
/*
* clean-up everything...
*/
synchronized (sGLThreadManager) {
- stopEglLocked();
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
}
}
}
@@ -1338,13 +1382,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private boolean mPaused;
private boolean mHasSurface;
private boolean mWaitingForSurface;
- private boolean mHaveEgl;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
private int mWidth;
private int mHeight;
private int mRenderMode;
private boolean mRequestRender;
private boolean mRenderComplete;
private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+
// End of member variables protected by the sGLThreadManager monitor.
private Renderer mRenderer;
@@ -1406,12 +1452,12 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
/*
* Tries once to acquire the right to use an EGL
- * surface. Does not block. Requires that we are already
+ * context. Does not block. Requires that we are already
* in the sGLThreadManager monitor when this is called.
*
- * @return true if the right to use an EGL surface was acquired.
+ * @return true if the right to use an EGL context was acquired.
*/
- public boolean tryAcquireEglSurfaceLocked(GLThread thread) {
+ public boolean tryAcquireEglContextLocked(GLThread thread) {
if (mEglOwner == thread || mEglOwner == null) {
mEglOwner = thread;
notifyAll();
@@ -1423,17 +1469,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
return false;
}
+
/*
- * Releases the EGL surface. Requires that we are already in the
+ * Releases the EGL context. Requires that we are already in the
* sGLThreadManager monitor when this is called.
*/
- public void releaseEglSurfaceLocked(GLThread thread) {
+ public void releaseEglContextLocked(GLThread thread) {
if (mEglOwner == thread) {
mEglOwner = null;
}
notifyAll();
}
+ public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+ checkGLESVersion();
+ return mMultipleGLESContextsAllowed;
+ }
+
public synchronized void checkGLDriver(GL10 gl) {
if (! mGLESDriverCheckComplete) {
checkGLESVersion();
@@ -1457,14 +1509,12 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
mGLESVersionCheckComplete = true;
}
-
}
private boolean mGLESVersionCheckComplete;
private int mGLESVersion;
private boolean mGLESDriverCheckComplete;
private boolean mMultipleGLESContextsAllowed;
- private int mGLContextCount;
private static final int kGLES_20 = 0x20000;
private static final String kMSM7K_RENDERER_PREFIX =
"Q3Dimension MSM7500 ";
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index 9837845..c2e9f31 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -39,6 +39,11 @@ endif
ifneq ($(TARGET_SIMULATOR),true)
# we need to access the private Bionic header <bionic_tls.h>
+ # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER
+ # behavior from the bionic Android.mk file
+ ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+ endif
LOCAL_C_INCLUDES += bionic/libc/private
endif
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 7353385..6b7020f 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -20,6 +20,11 @@ LOCAL_MODULE:= libEGL
ifeq ($(TARGET_SIMULATOR),true)
else
LOCAL_SHARED_LIBRARIES += libdl
+ # Bionic's private TLS header relies on the ARCH_ARM_HAVE_TLS_REGISTER to
+ # select the appropriate TLS codepath
+ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+ endif
# we need to access the private Bionic header <bionic_tls.h>
LOCAL_C_INCLUDES += bionic/libc/private
endif
@@ -75,6 +80,9 @@ ifeq ($(TARGET_SIMULATOR),true)
else
LOCAL_SHARED_LIBRARIES += libdl
# we need to access the private Bionic header <bionic_tls.h>
+ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+ endif
LOCAL_C_INCLUDES += bionic/libc/private
endif
@@ -108,6 +116,9 @@ ifeq ($(TARGET_SIMULATOR),true)
else
LOCAL_SHARED_LIBRARIES += libdl
# we need to access the private Bionic header <bionic_tls.h>
+ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+ endif
LOCAL_C_INCLUDES += bionic/libc/private
endif
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 80129d0..5a8d35f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -180,6 +180,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private ConnectivityService(Context context) {
if (DBG) Log.v(TAG, "ConnectivityService starting up");
+
+ // setup our unique device name
+ String id = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.ANDROID_ID);
+ if (id != null && id.length() > 0) {
+ String name = new String("android_").concat(id);
+ SystemProperties.set("net.hostname", name);
+ }
+
mContext = context;
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -1248,11 +1257,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
info = (NetworkInfo) msg.obj;
int type = info.getType();
NetworkInfo.State state = info.getState();
- if(mNetAttributes[type].mLastState == state) {
+ if (mNetAttributes[type].mLastState == state) {
if (DBG) {
// TODO - remove this after we validate the dropping doesn't break anything
Log.d(TAG, "Dropping ConnectivityChange for " +
- info.getTypeName() +": " +
+ info.getTypeName() + ": " +
state + "/" + info.getDetailedState());
}
return;
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
new file mode 100644
index 0000000..e13ddc8
--- /dev/null
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.common.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.app.DeviceAdmin;
+import android.app.DeviceAdminInfo;
+import android.app.DevicePolicyManager;
+import android.app.IDevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Implementation of the device policy APIs.
+ */
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ private static final String TAG = "DevicePolicyManagerService";
+
+ private final Context mContext;
+
+ int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ int mActivePasswordLength = 0;
+ int mFailedPasswordAttempts = 0;
+
+ ActiveAdmin mActiveAdmin;
+
+ static class ActiveAdmin {
+ ActiveAdmin(DeviceAdminInfo _info) {
+ info = _info;
+ }
+
+ final DeviceAdminInfo info;
+ int getUid() { return info.getActivityInfo().applicationInfo.uid; }
+
+ int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ int minimumPasswordLength = 0;
+ long maximumTimeToUnlock = 0;
+ }
+
+ /**
+ * Instantiates the service.
+ */
+ public DevicePolicyManagerService(Context context) {
+ mContext = context;
+ }
+
+ ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
+ if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingPid()) {
+ if (who != null) {
+ if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
+ || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
+ throw new SecurityException("Current admin is not " + who);
+ }
+ }
+ return mActiveAdmin;
+ }
+ throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
+ }
+
+
+ void sendAdminCommandLocked(ActiveAdmin policy, String action) {
+ Intent intent = new Intent(action);
+ intent.setComponent(policy.info.getComponent());
+ mContext.sendBroadcast(intent);
+ }
+
+ ComponentName getActiveAdminLocked() {
+ if (mActiveAdmin != null) {
+ return mActiveAdmin.info.getComponent();
+ }
+ return null;
+ }
+
+ void removeActiveAdminLocked(ComponentName adminReceiver) {
+ ComponentName cur = getActiveAdminLocked();
+ if (cur != null && cur.equals(adminReceiver)) {
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
+ // XXX need to wait for it to complete.
+ mActiveAdmin = null;
+ }
+ }
+
+ public DeviceAdminInfo findAdmin(ComponentName adminName) {
+ Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(adminName);
+ List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
+ resolveIntent, PackageManager.GET_META_DATA);
+ if (infos == null || infos.size() <= 0) {
+ throw new IllegalArgumentException("Unknown admin: " + adminName);
+ }
+
+ try {
+ return new DeviceAdminInfo(mContext, infos.get(0));
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ return null;
+ }
+ }
+
+ private static JournaledFile makeJournaledFile() {
+ final String base = "/data/system/device_policies.xml";
+ return new JournaledFile(new File(base), new File(base + ".tmp"));
+ }
+
+ private void saveSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "policies");
+
+ ActiveAdmin ap = mActiveAdmin;
+ if (ap != null) {
+ out.startTag(null, "admin");
+ out.attribute(null, "name", ap.info.getComponent().flattenToString());
+ if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
+ out.startTag(null, "password-mode");
+ out.attribute(null, "value", Integer.toString(ap.passwordMode));
+ out.endTag(null, "password-mode");
+ if (ap.minimumPasswordLength > 0) {
+ out.startTag(null, "min-password-length");
+ out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
+ out.endTag(null, "mn-password-length");
+ }
+ }
+ if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
+ out.startTag(null, "max-time-to-unlock");
+ out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
+ out.endTag(null, "max-time-to-unlock");
+ }
+ out.endTag(null, "admin");
+ }
+ out.endTag(null, "policies");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
+ private void loadSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ boolean success = false;
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type = parser.next();
+ while (type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+ String tag = parser.getName();
+ if ("policies".equals(tag)) {
+ ActiveAdmin ap = null;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ tag = parser.getName();
+ if (ap == null) {
+ if ("admin".equals(tag)) {
+ DeviceAdminInfo dai = findAdmin(
+ ComponentName.unflattenFromString(
+ parser.getAttributeValue(null, "name")));
+ if (dai != null) {
+ ap = new ActiveAdmin(dai);
+ }
+ }
+ } else if ("password-mode".equals(tag)) {
+ ap.passwordMode = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-length".equals(tag)) {
+ ap.minimumPasswordLength = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("max-time-to-unlock".equals(tag)) {
+ ap.maximumTimeToUnlock = Long.parseLong(
+ parser.getAttributeValue(null, "value"));
+ }
+ } else if (type == XmlPullParser.END_TAG) {
+ tag = parser.getName();
+ if (ap != null && "admin".equals(tag)) {
+ mActiveAdmin = ap;
+ ap = null;
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ }
+ } catch (NullPointerException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ Log.w(TAG, "No valid start tag found in policies file");
+ }
+ }
+
+ public void systemReady() {
+ synchronized (this) {
+ loadSettingsLocked();
+ }
+ }
+
+ public void setActiveAdmin(ComponentName adminReceiver) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ DeviceAdminInfo info = findAdmin(adminReceiver);
+ if (info == null) {
+ throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName cur = getActiveAdminLocked();
+ if (cur != null && cur.equals(adminReceiver)) {
+ throw new IllegalStateException("An admin is already set");
+ }
+ if (cur != null) {
+ removeActiveAdminLocked(adminReceiver);
+ }
+ mActiveAdmin = new ActiveAdmin(info);
+ saveSettingsLocked();
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public ComponentName getActiveAdmin() {
+ synchronized (this) {
+ return getActiveAdminLocked();
+ }
+ }
+
+ public void removeActiveAdmin(ComponentName adminReceiver) {
+ synchronized (this) {
+ if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeActiveAdminLocked(adminReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setPasswordMode(ComponentName who, int mode) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.passwordMode != mode) {
+ ap.passwordMode = mode;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMode() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.passwordMode
+ : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ }
+ }
+
+ public int getActivePasswordMode() {
+ synchronized (this) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(null);
+ return mActivePasswordMode;
+ }
+ }
+
+ public void setMinimumPasswordLength(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.minimumPasswordLength != length) {
+ ap.minimumPasswordLength = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getMinimumPasswordLength() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
+ }
+ }
+
+ public int getActiveMinimumPasswordLength() {
+ synchronized (this) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(null);
+ return mActivePasswordLength;
+ }
+ }
+
+ public int getCurrentFailedPasswordAttempts() {
+ synchronized (this) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(null);
+ return mFailedPasswordAttempts;
+ }
+ }
+
+ public void setMaximumTimeToLock(ComponentName who, long timeMs) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+ if (ap.maximumTimeToUnlock != timeMs) {
+ ap.maximumTimeToUnlock = timeMs;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public long getMaximumTimeToLock() {
+ synchronized (this) {
+ return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
+ }
+ }
+
+ public void wipeData(int flags) {
+ synchronized (this) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Log.w(TAG, "*************** WIPE DATA HERE");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setActivePasswordState(int mode, int length) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ if (mActivePasswordMode != mode || mActivePasswordLength != length
+ || mFailedPasswordAttempts != 0) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mActivePasswordMode = mode;
+ mActivePasswordLength = length;
+ mFailedPasswordAttempts = 0;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_CHANGED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+
+ public void reportFailedPasswordAttempt() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mFailedPasswordAttempts++;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_FAILED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void reportSuccessfulPasswordAttempt() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ if (mFailedPasswordAttempts != 0) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mFailedPasswordAttempts = 0;
+ sendAdminCommandLocked(mActiveAdmin,
+ DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c8a6915..f8f8742 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -127,6 +127,7 @@ class MountService extends IMountService.Stub
private boolean mUmsConnected = false;
private boolean mUmsEnabled = false;
+ private boolean mUmsEnabling = false;
private String mLegacyState = Environment.MEDIA_REMOVED;
@@ -332,13 +333,16 @@ class MountService extends IMountService.Stub
String vp = Environment.getExternalStorageDirectory().getPath();
String vs = getVolumeState(vp);
+ mUmsEnabling = enable;
if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
unmountVolume(vp);
+ mUmsEnabling = false;
updateUsbMassStorageNotification(true, false);
}
setShareMethodEnabled(vp, "ums", enable);
mUmsEnabled = enable;
+ mUmsEnabling = false;
if (!enable) {
mountVolume(vp);
if (mPromptUms) {
@@ -594,10 +598,14 @@ class MountService extends IMountService.Stub
} else if (newState == VolumeState.NoMedia) {
// NoMedia is handled via Disk Remove events
} else if (newState == VolumeState.Idle) {
- // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
+ /*
+ * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
+ * if we're in the process of enabling UMS
+ */
if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
!vs.equals(Environment.MEDIA_NOFS) &&
- !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
+ !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
+ !mUmsEnabling) {
notifyMediaUnmounted(mountPoint);
}
} else if (newState == VolumeState.Pending) {
@@ -1049,6 +1057,11 @@ class MountService extends IMountService.Stub
return getSecureContainerPath(id);
}
+ public void unmountSecureContainer(String id) throws IllegalStateException {
+ String cmd = String.format("unmount_asec %s ", id);
+ mConnector.doCommand(cmd);
+ }
+
public String getSecureContainerPath(String id) throws IllegalStateException {
ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 4b9b366..1b6a56a 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -75,6 +75,7 @@ import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.security.SystemKeyStore;
import android.util.*;
import android.view.Display;
import android.view.WindowManager;
@@ -89,6 +90,7 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -455,7 +457,9 @@ class PackageManagerService extends IPackageManager.Stub {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
- int scanMode = SCAN_MONITOR;
+ // Set flag to monitor and not change apk file paths when
+ // scanning install directories.
+ int scanMode = SCAN_MONITOR | SCAN_NO_PATHS;
if (mNoDexOpt) {
Log.w(TAG, "Running ENG build: no pre-dexopt!");
scanMode |= SCAN_NO_DEX;
@@ -573,12 +577,12 @@ class PackageManagerService extends IPackageManager.Stub {
mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
- scanMode | SCAN_NO_DEX | SCAN_NO_PATHS);
+ scanMode | SCAN_NO_DEX);
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode | SCAN_NO_PATHS);
+ scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
mAppInstallDir = new File(dataDir, "app");
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
@@ -3754,7 +3758,7 @@ class PackageManagerService extends IPackageManager.Stub {
(mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
- SCAN_MONITOR);
+ SCAN_MONITOR | SCAN_NO_PATHS);
if (p != null) {
synchronized (mPackages) {
grantPermissionsLP(p, false);
@@ -3986,7 +3990,8 @@ class PackageManagerService extends IPackageManager.Stub {
Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
- }
+ }
+ } else {
updateSettingsLI(newPackage,
installerPackageName,
res);
@@ -7436,7 +7441,8 @@ class PackageManagerService extends IPackageManager.Stub {
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
- final private String mSdEncryptKey = "none";
+ final private String mSdEncryptKey = "AppsOnSD";
+ final private String mSdEncryptAlg = "AES";
private MountService getMountService() {
return (MountService) ServiceManager.getService("mount");
@@ -7454,10 +7460,25 @@ class PackageManagerService extends IPackageManager.Stub {
String cachePath = null;
// Remove any pending destroy messages
mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+ String sdEncKey;
+ try {
+ sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+ if (sdEncKey == null) {
+ sdEncKey = SystemKeyStore.getInstance().
+ generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey);
+ if (sdEncKey == null) {
+ Log.e(TAG, "Failed to create encryption keys for package: " + pkgName + ".");
+ return null;
+ }
+ }
+ } catch (NoSuchAlgorithmException nsae) {
+ Log.e(TAG, "Failed to create encryption keys with exception: " + nsae);
+ return null;
+ }
try {
cachePath = mountService.createSecureContainer(pkgName,
mbLen,
- "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+ "vfat", sdEncKey, Process.SYSTEM_UID);
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
return cachePath;
} catch(IllegalStateException e) {
@@ -7474,7 +7495,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
cachePath = mountService.createSecureContainer(pkgName,
mbLen,
- "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+ "vfat", sdEncKey, Process.SYSTEM_UID);
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
return cachePath;
} catch(IllegalStateException e) {
@@ -7484,8 +7505,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
private String mountSdDir(String pkgName, int ownerUid) {
+ String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+ if (sdEncKey == null) {
+ Log.e(TAG, "Failed to retrieve encryption keys to mount package code: " + pkgName + ".");
+ return null;
+ }
try {
- return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid);
+ return getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
} catch (IllegalStateException e) {
Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 674ade9..6b3f433 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -201,6 +201,7 @@ class ServerThread extends Thread {
Log.e("System", "Failure starting core service", e);
}
+ DevicePolicyManagerService devicePolicy = null;
StatusBarService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
@@ -209,16 +210,25 @@ class ServerThread extends Thread {
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
+ Log.i(TAG, "Device Policy");
+ devicePolicy = new DevicePolicyManagerService(context);
+ ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting DevicePolicyService", e);
+ }
+
+ try {
Log.i(TAG, "Status Bar");
statusBar = new StatusBarService(context);
- ServiceManager.addService("statusbar", statusBar);
+ ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
Log.e(TAG, "Failure starting StatusBarService", e);
}
try {
Log.i(TAG, "Clipboard Service");
- ServiceManager.addService("clipboard", new ClipboardService(context));
+ ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+ new ClipboardService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Clipboard Service", e);
}
@@ -280,14 +290,16 @@ class ServerThread extends Thread {
try {
Log.i(TAG, "Location Manager");
- ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context));
+ ServiceManager.addService(Context.LOCATION_SERVICE,
+ new LocationManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Location Manager", e);
}
try {
Log.i(TAG, "Search Service");
- ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) );
+ ServiceManager.addService(Context.SEARCH_SERVICE,
+ new SearchManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Search Service", e);
}
@@ -351,7 +363,8 @@ class ServerThread extends Thread {
try {
Log.i(TAG, "Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
+ ServiceManager.addService(Context.BACKUP_SERVICE,
+ new BackupManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Backup Service", e);
}
@@ -391,6 +404,10 @@ class ServerThread extends Thread {
// It is now time to start up the app processes...
+ if (devicePolicy != null) {
+ devicePolicy.systemReady();
+ }
+
if (notification != null) {
notification.systemReady();
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 40d194c..843058c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8381,7 +8381,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityInfo ai = ris.get(i).activityInfo;
intent.setComponent(new ComponentName(ai.packageName, ai.name));
IIntentReceiver finisher = null;
- if (i == 0) {
+ if (i == ris.size()-1) {
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
@@ -8397,7 +8397,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Log.i(TAG, "Sending system update to: " + intent.getComponent());
broadcastIntentLocked(null, null, intent, null, finisher,
0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
- if (i == 0) {
+ if (finisher != null) {
mWaitingUpdate = true;
}
}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index bee0930..42c0254 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -43,6 +43,9 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
+import android.text.style.RelativeSizeSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -61,6 +64,7 @@ import com.android.internal.telephony.cdma.EriInfo;
import com.android.internal.telephony.cdma.TtyIntent;
import com.android.server.am.BatteryStatsService;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
@@ -532,10 +536,69 @@ public class StatusBarPolicy {
sInstance = new StatusBarPolicy(context, service);
}
+ private final CharSequence getSmallTime() {
+ boolean b24 = DateFormat.is24HourFormat(mContext);
+ int res;
+
+ if (b24) {
+ res = R.string.twenty_four_hour_time_format;
+ } else {
+ res = R.string.twelve_hour_time_format;
+ }
+
+ String format = mContext.getString(res);
+
+ /*
+ * Search for an unquoted "a" in the format string, so we can
+ * add dummy characters around it to let us find it again after
+ * formatting and change its size.
+ */
+ int a = -1;
+ boolean quoted = false;
+ for (int i = 0; i < format.length(); i++) {
+ char c = format.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
+
+ if (!quoted && c == 'a') {
+ a = i;
+ break;
+ }
+ }
+
+ final char MAGIC1 = '\uEF00';
+ final char MAGIC2 = '\uEF01';
+
+ if (a >= 0) {
+ format = format.substring(0, a) + MAGIC1 + "a" + MAGIC2 +
+ format.substring(a + 1);
+ }
+
+ String result = new SimpleDateFormat(format).format(mCalendar.getTime());
+
+ int magic1 = result.indexOf(MAGIC1);
+ int magic2 = result.indexOf(MAGIC2);
+
+ if (magic1 >= 0 && magic2 > magic1) {
+ SpannableStringBuilder formatted = new SpannableStringBuilder(result);
+
+ formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
+ Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+ formatted.delete(magic2, magic2 + 1);
+ formatted.delete(magic1, magic1 + 1);
+
+ return formatted;
+ } else {
+ return result;
+ }
+ }
+
private final void updateClock() {
mCalendar.setTimeInMillis(System.currentTimeMillis());
- mClockData.text = DateFormat.getTimeFormat(mContext)
- .format(mCalendar.getTime());
+ mClockData.text = getSmallTime();
mService.updateIcon(mClockIcon, mClockData, null);
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
index 7a4d934..69d55c1 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
@@ -987,4 +987,18 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase
ih.close();
}
+ @MediumTest
+ public void testDbCloseReleasingAllCachedSql() {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
+ "num1 INTEGER, num2 INTEGER, image BLOB);");
+ final String statement = "DELETE FROM test WHERE _id=?;";
+ SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement);
+ assertTrue(statementDoNotClose.getUniqueId() > 0);
+ int nStatement = statementDoNotClose.getUniqueId();
+ assertTrue(statementDoNotClose.getUniqueId() == nStatement);
+ /* do not close statementDoNotClose object.
+ * That should leave it in SQLiteDatabase.mPrograms.
+ * mDatabase.close() in tearDown() should release it.
+ */
+ }
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
index 3b33a99..724ef6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
@@ -359,56 +359,58 @@ public class TimeTest extends TestCase {
Time t = new Time(Time.TIMEZONE_UTC);
t.parse3339("1980-05-23");
- if (!t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23) {
+ if (!t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23) {
fail("Did not parse all-day date correctly");
}
t.parse3339("1980-05-23T09:50:50");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse timezone-offset-less date correctly");
}
t.parse3339("1980-05-23T09:50:50Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.0Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.12Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
t.parse3339("1980-05-23T09:50:50.123Z");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
t.hour != 9 || t.minute != 50 || t.second != 50 ||
t.gmtoff != 0) {
fail("Did not parse UTC date correctly");
}
- t.parse3339("1980-05-23T09:50:50-06:00");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
- t.hour != 9 || t.minute != 50 || t.second != 50 ||
- t.gmtoff != -6*3600) {
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
fail("Did not parse timezone-offset date correctly");
}
- t.parse3339("1980-05-23T09:50:50.123-06:00");
- if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
- t.hour != 9 || t.minute != 50 || t.second != 50 ||
- t.gmtoff != -6*3600) {
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50.123-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
fail("Did not parse timezone-offset date correctly");
}
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
index 52286d1..31ee120 100644
--- a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.TransformGestureDetector;
+import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.LinearLayout;
@@ -48,9 +47,6 @@ public class TransformTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- final LayoutInflater li = (LayoutInflater)getSystemService(
- LAYOUT_INFLATER_SERVICE);
this.setTitle(R.string.act_title);
LinearLayout root = new LinearLayout(this);
@@ -71,15 +67,19 @@ public class TransformTestActivity extends Activity {
private float mPosY;
private float mScale = 1.f;
private Matrix mMatrix;
- private TransformGestureDetector mDetector;
+ private ScaleGestureDetector mDetector;
+
+ private float mLastX;
+ private float mLastY;
- private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+ private class Listener implements ScaleGestureDetector.OnScaleGestureListener {
- public boolean onTransform(TransformGestureDetector detector) {
- Log.d("ttest", "Translation: (" + detector.getTranslateX() +
- ", " + detector.getTranslateY() + ")");
+ public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
+
Log.d("ttest", "Scale: " + scale);
+
+ // Limit the scale so our object doesn't get too big or disappear
if (mScale * scale > 0.1f) {
if (mScale * scale < 10.f) {
mScale *= scale;
@@ -89,16 +89,13 @@ public class TransformTestActivity extends Activity {
} else {
mScale = 0.1f;
}
-
- mPosX += detector.getTranslateX();
- mPosY += detector.getTranslateY();
Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
float sizeX = mDrawable.getIntrinsicWidth()/2;
float sizeY = mDrawable.getIntrinsicHeight()/2;
- float centerX = detector.getCenterX();
- float centerY = detector.getCenterY();
+ float centerX = detector.getFocusX();
+ float centerY = detector.getFocusY();
float diffX = centerX - mPosX;
float diffY = centerY - mPosY;
diffX = diffX*scale - diffX;
@@ -115,24 +112,20 @@ public class TransformTestActivity extends Activity {
return true;
}
- public boolean onTransformBegin(TransformGestureDetector detector) {
- return true;
- }
-
- public boolean onTransformEnd(TransformGestureDetector detector) {
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
- public boolean onTransformFling(TransformGestureDetector detector) {
- return false;
- }
-
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ mLastX = detector.getFocusX();
+ mLastY = detector.getFocusY();
+ }
}
public TransformView(Context context) {
super(context);
mMatrix = new Matrix();
- mDetector = new TransformGestureDetector(context, new Listener());
+ mDetector = new ScaleGestureDetector(context, new Listener());
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mPosX = metrics.widthPixels/2;
mPosY = metrics.heightPixels/2;
@@ -151,12 +144,37 @@ public class TransformTestActivity extends Activity {
@Override
public boolean onTouchEvent(MotionEvent event) {
- boolean handled = mDetector.onTouchEvent(event);
+ mDetector.onTouchEvent(event);
- int pointerCount = event.getPointerCount();
- Log.d("ttest", "pointerCount: " + pointerCount);
+ // Handling single finger pan
+ if (!mDetector.isInProgress()) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float x = event.getX();
+ final float y = event.getY();
+ mPosX += x - mLastX;
+ mPosY += y - mLastY;
+ mLastX = x;
+ mLastY = y;
+
+ float sizeX = mDrawable.getIntrinsicWidth()/2;
+ float sizeY = mDrawable.getIntrinsicHeight()/2;
+
+ mMatrix.reset();
+ mMatrix.postTranslate(-sizeX, -sizeY);
+ mMatrix.postScale(mScale, mScale);
+ mMatrix.postTranslate(mPosX, mPosY);
+ invalidate();
+ break;
+ }
+ }
- return handled;
+ return true;
}
@Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
index ff1b295..35f022e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -20,6 +20,7 @@ package android.graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import javax.imageio.ImageIO;
@@ -33,6 +34,12 @@ public final class Bitmap extends _Original_Bitmap {
mImage = ImageIO.read(input);
}
+ public Bitmap(InputStream is) throws IOException {
+ super(1, true, null, -1);
+
+ mImage = ImageIO.read(is);
+ }
+
Bitmap(BufferedImage image) {
super(1, true, null, -1);
mImage = image;
@@ -237,4 +244,35 @@ public final class Bitmap extends _Original_Bitmap {
return createBitmap(colors, 0, width, width, height, config);
}
+ public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
+ int dstHeight, boolean filter) {
+ Matrix m;
+ synchronized (Bitmap.class) {
+ // small pool of just 1 matrix
+ m = sScaleMatrix;
+ sScaleMatrix = null;
+ }
+
+ if (m == null) {
+ m = new Matrix();
+ }
+
+ final int width = src.getWidth();
+ final int height = src.getHeight();
+ final float sx = dstWidth / (float)width;
+ final float sy = dstHeight / (float)height;
+ m.setScale(sx, sy);
+ Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
+
+ synchronized (Bitmap.class) {
+ // do we need to check for null? why not just assign everytime?
+ if (sScaleMatrix == null) {
+ sScaleMatrix = m;
+ }
+ }
+
+ return b;
+ }
+
+
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
new file mode 100644
index 0000000..e978fe8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Creates Bitmap objects from various sources, including files, streams,
+ * and byte-arrays.
+ */
+public class BitmapFactory {
+ public static class Options {
+ /**
+ * Create a default Options object, which if left unchanged will give
+ * the same result from the decoder as if null were passed.
+ */
+ public Options() {
+ inDither = true;
+ inScaled = true;
+ }
+
+ /**
+ * If set to true, the decoder will return null (no bitmap), but
+ * the out... fields will still be set, allowing the caller to query
+ * the bitmap without having to allocate the memory for its pixels.
+ */
+ public boolean inJustDecodeBounds;
+
+ /**
+ * If set to a value > 1, requests the decoder to subsample the original
+ * image, returning a smaller image to save memory. The sample size is
+ * the number of pixels in either dimension that correspond to a single
+ * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
+ * an image that is 1/4 the width/height of the original, and 1/16 the
+ * number of pixels. Any value <= 1 is treated the same as 1. Note: the
+ * decoder will try to fulfill this request, but the resulting bitmap
+ * may have different dimensions that precisely what has been requested.
+ * Also, powers of 2 are often faster/easier for the decoder to honor.
+ */
+ public int inSampleSize;
+
+ /**
+ * If this is non-null, the decoder will try to decode into this
+ * internal configuration. If it is null, or the request cannot be met,
+ * the decoder will try to pick the best matching config based on the
+ * system's screen depth, and characteristics of the original image such
+ * as if it has per-pixel alpha (requiring a config that also does).
+ */
+ public Bitmap.Config inPreferredConfig;
+
+ /**
+ * If dither is true, the decoder will attempt to dither the decoded
+ * image.
+ */
+ public boolean inDither;
+
+ /**
+ * The pixel density to use for the bitmap. This will always result
+ * in the returned bitmap having a density set for it (see
+ * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)). In addition,
+ * if {@link #inScaled} is set (which it is by default} and this
+ * density does not match {@link #inTargetDensity}, then the bitmap
+ * will be scaled to the target density before being returned.
+ *
+ * <p>If this is 0,
+ * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+ * and {@link BitmapFactory#decodeResourceStream}
+ * will fill in the density associated with the resource. The other
+ * functions will leave it as-is and no density will be applied.
+ *
+ * @see #inTargetDensity
+ * @see #inScreenDensity
+ * @see #inScaled
+ * @see Bitmap#setDensity(int)
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inDensity;
+
+ /**
+ * The pixel density of the destination this bitmap will be drawn to.
+ * This is used in conjunction with {@link #inDensity} and
+ * {@link #inScaled} to determine if and how to scale the bitmap before
+ * returning it.
+ *
+ * <p>If this is 0,
+ * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+ * and {@link BitmapFactory#decodeResourceStream}
+ * will fill in the density associated the Resources object's
+ * DisplayMetrics. The other
+ * functions will leave it as-is and no scaling for density will be
+ * performed.
+ *
+ * @see #inDensity
+ * @see #inScreenDensity
+ * @see #inScaled
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inTargetDensity;
+
+ /**
+ * The pixel density of the actual screen that is being used. This is
+ * purely for applications running in density compatibility code, where
+ * {@link #inTargetDensity} is actually the density the application
+ * sees rather than the real screen density.
+ *
+ * <p>By setting this, you
+ * allow the loading code to avoid scaling a bitmap that is currently
+ * in the screen density up/down to the compatibility density. Instead,
+ * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
+ * bitmap will be left as-is. Anything using the resulting bitmap
+ * must also used {@link Bitmap#getScaledWidth(int)
+ * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
+ * Bitmap.getScaledHeight} to account for any different between the
+ * bitmap's density and the target's density.
+ *
+ * <p>This is never set automatically for the caller by
+ * {@link BitmapFactory} itself. It must be explicitly set, since the
+ * caller must deal with the resulting bitmap in a density-aware way.
+ *
+ * @see #inDensity
+ * @see #inTargetDensity
+ * @see #inScaled
+ * @see android.util.DisplayMetrics#densityDpi
+ */
+ public int inScreenDensity;
+
+ /**
+ * When this flag is set, if {@link #inDensity} and
+ * {@link #inTargetDensity} are not 0, the
+ * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
+ * rather than relying on the graphics system scaling it each time it
+ * is drawn to a Canvas.
+ *
+ * <p>This flag is turned on by default and should be turned off if you need
+ * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this
+ * flag and are always scaled.
+ */
+ public boolean inScaled;
+
+ /**
+ * If this is set to true, then the resulting bitmap will allocate its
+ * pixels such that they can be purged if the system needs to reclaim
+ * memory. In that instance, when the pixels need to be accessed again
+ * (e.g. the bitmap is drawn, getPixels() is called), they will be
+ * automatically re-decoded.
+ *
+ * For the re-decode to happen, the bitmap must have access to the
+ * encoded data, either by sharing a reference to the input
+ * or by making a copy of it. This distinction is controlled by
+ * inInputShareable. If this is true, then the bitmap may keep a shallow
+ * reference to the input. If this is false, then the bitmap will
+ * explicitly make a copy of the input data, and keep that. Even if
+ * sharing is allowed, the implementation may still decide to make a
+ * deep copy of the input data.
+ */
+ public boolean inPurgeable;
+
+ /**
+ * This field works in conjuction with inPurgeable. If inPurgeable is
+ * false, then this field is ignored. If inPurgeable is true, then this
+ * field determines whether the bitmap can share a reference to the
+ * input data (inputstream, array, etc.) or if it must make a deep copy.
+ */
+ public boolean inInputShareable;
+
+ /**
+ * Normally bitmap allocations count against the dalvik heap, which
+ * means they help trigger GCs when a lot have been allocated. However,
+ * in rare cases, the caller may want to allocate the bitmap outside of
+ * that heap. To request that, set inNativeAlloc to true. In these
+ * rare instances, it is solely up to the caller to ensure that OOM is
+ * managed explicitly by calling bitmap.recycle() as soon as such a
+ * bitmap is no longer needed.
+ *
+ * @hide pending API council approval
+ */
+ public boolean inNativeAlloc;
+
+ /**
+ * The resulting width of the bitmap, set independent of the state of
+ * inJustDecodeBounds. However, if there is an error trying to decode,
+ * outWidth will be set to -1.
+ */
+ public int outWidth;
+
+ /**
+ * The resulting height of the bitmap, set independent of the state of
+ * inJustDecodeBounds. However, if there is an error trying to decode,
+ * outHeight will be set to -1.
+ */
+ public int outHeight;
+
+ /**
+ * If known, this string is set to the mimetype of the decoded image.
+ * If not know, or there is an error, it is set to null.
+ */
+ public String outMimeType;
+
+ /**
+ * Temp storage to use for decoding. Suggest 16K or so.
+ */
+ public byte[] inTempStorage;
+
+ private native void requestCancel();
+
+ /**
+ * Flag to indicate that cancel has been called on this object. This
+ * is useful if there's an intermediary that wants to first decode the
+ * bounds and then decode the image. In that case the intermediary
+ * can check, inbetween the bounds decode and the image decode, to see
+ * if the operation is canceled.
+ */
+ public boolean mCancel;
+
+ /**
+ * This can be called from another thread while this options object is
+ * inside a decode... call. Calling this will notify the decoder that
+ * it should cancel its operation. This is not guaranteed to cancel
+ * the decode, but if it does, the decoder... operation will return
+ * null, or if inJustDecodeBounds is true, will set outWidth/outHeight
+ * to -1
+ */
+ public void requestCancelDecode() {
+ mCancel = true;
+ requestCancel();
+ }
+ }
+
+ /**
+ * Decode a file path into a bitmap. If the specified file name is null,
+ * or cannot be decoded into a bitmap, the function returns null.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeFile(String pathName, Options opts) {
+ Bitmap bm = null;
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(pathName);
+ bm = decodeStream(stream, null, opts);
+ } catch (Exception e) {
+ /* do nothing.
+ If the exception happened on open, bm will be null.
+ */
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
+ /**
+ * Decode a file path into a bitmap. If the specified file name is null,
+ * or cannot be decoded into a bitmap, the function returns null.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @return the resulting decoded bitmap, or null if it could not be decoded.
+ */
+ public static Bitmap decodeFile(String pathName) {
+ return decodeFile(pathName, null);
+ }
+
+ /**
+ * Decode a new Bitmap from an InputStream. This InputStream was obtained from
+ * resources, which we pass to be able to scale the bitmap accordingly.
+ */
+ public static Bitmap decodeResourceStream(Resources res, TypedValue value,
+ InputStream is, Rect pad, Options opts) {
+
+ if (opts == null) {
+ opts = new Options();
+ }
+
+ if (opts.inDensity == 0 && value != null) {
+ final int density = value.density;
+ if (density == TypedValue.DENSITY_DEFAULT) {
+ opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (density != TypedValue.DENSITY_NONE) {
+ opts.inDensity = density;
+ }
+ }
+
+ if (opts.inTargetDensity == 0 && res != null) {
+ opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
+ }
+
+ return decodeStream(is, pad, opts);
+ }
+
+ /**
+ * Synonym for opening the given resource and calling
+ * {@link #decodeResourceStream}.
+ *
+ * @param res The resources object containing the image data
+ * @param id The resource id of the image data
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeResource(Resources res, int id, Options opts) {
+ Bitmap bm = null;
+ InputStream is = null;
+
+ try {
+ final TypedValue value = new TypedValue();
+ is = res.openRawResource(id, value);
+
+ bm = decodeResourceStream(res, value, is, null, opts);
+ } catch (Exception e) {
+ /* do nothing.
+ If the exception happened on open, bm will be null.
+ If it happened on close, bm is still valid.
+ */
+ } finally {
+ try {
+ if (is != null) is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ return bm;
+ }
+
+ /**
+ * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
+ * will null Options.
+ *
+ * @param res The resources object containing the image data
+ * @param id The resource id of the image data
+ * @return The decoded bitmap, or null if the image could not be decode.
+ */
+ public static Bitmap decodeResource(Resources res, int id) {
+ return decodeResource(res, id, null);
+ }
+
+ /**
+ * Decode an immutable bitmap from the specified byte array.
+ *
+ * @param data byte array of compressed image data
+ * @param offset offset into imageData for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
+ return null;
+ //return nativeDecodeByteArray(data, offset, length, opts);
+ }
+
+ /**
+ * Decode an immutable bitmap from the specified byte array.
+ *
+ * @param data byte array of compressed image data
+ * @param offset offset into imageData for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @return The decoded bitmap, or null if the image could not be decode.
+ */
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
+ return decodeByteArray(data, offset, length, null);
+ }
+
+ /**
+ * Decode an input stream into a bitmap. If the input stream is null, or
+ * cannot be used to decode a bitmap, the function returns null.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * bitmap.
+ * @param outPadding If not null, return the padding rect for the bitmap if
+ * it exists, otherwise set padding to [-1,-1,-1,-1]. If
+ * no bitmap is returned (null) then padding is
+ * unchanged.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+ // we don't throw in this case, thus allowing the caller to only check
+ // the cache, and not force the image to be decoded.
+ if (is == null) {
+ return null;
+ }
+
+ // we need mark/reset to work properly
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ // so we can call reset() if a given codec gives up after reading up to
+ // this many bytes. FIXME: need to find out from the codecs what this
+ // value should be.
+ is.mark(1024);
+
+ Bitmap bm;
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ // FIXME: log this.
+ return null;
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...) This number is not related to the value passed
+ // to mark(...) above.
+ try {
+ bm = new Bitmap(is);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ return finishDecode(bm, outPadding, opts);
+ }
+
+ private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
+ if (bm == null || opts == null) {
+ return bm;
+ }
+
+ final int density = opts.inDensity;
+ if (density == 0) {
+ return bm;
+ }
+
+ bm.setDensity(density);
+ final int targetDensity = opts.inTargetDensity;
+ if (targetDensity == 0 || density == targetDensity
+ || density == opts.inScreenDensity) {
+ return bm;
+ }
+
+ byte[] np = bm.getNinePatchChunk();
+ final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
+ if (opts.inScaled || isNinePatch) {
+ float scale = targetDensity / (float)density;
+ // TODO: This is very inefficient and should be done in native by Skia
+ final Bitmap oldBitmap = bm;
+ bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+ (int) (bm.getHeight() * scale + 0.5f), true);
+ oldBitmap.recycle();
+
+ if (isNinePatch) {
+ //np = nativeScaleNinePatch(np, scale, outPadding);
+ bm.setNinePatchChunk(np);
+ }
+ bm.setDensity(targetDensity);
+ }
+
+ return bm;
+ }
+
+ /**
+ * Decode an input stream into a bitmap. If the input stream is null, or
+ * cannot be used to decode a bitmap, the function returns null.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * bitmap.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeStream(InputStream is) {
+ return decodeStream(is, null, null);
+ }
+
+ /**
+ * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+ * return null. The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as-is.
+ *
+ * @param fd The file descriptor containing the bitmap data to decode
+ * @param outPadding If not null, return the padding rect for the bitmap if
+ * it exists, otherwise set padding to [-1,-1,-1,-1]. If
+ * no bitmap is returned (null) then padding is
+ * unchanged.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return the decoded bitmap, or null
+ */
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+ return null;
+
+ /* FIXME: implement as needed
+ try {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getMappedSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ Bitmap bm = decodeStream(is, outPadding, opts);
+ return finishDecode(bm, outPadding, opts);
+ }
+ } catch (IOException ex) {
+ // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+ return null;
+ }
+ //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ //return finishDecode(bm, outPadding, opts);
+ */
+ }
+
+ /**
+ * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+ * return null. The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ *
+ * @param fd The file descriptor containing the bitmap data to decode
+ * @return the decoded bitmap, or null
+ */
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
+ return decodeFileDescriptor(fd, null, null);
+ }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 4986c77..02e3220 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -236,10 +236,15 @@ public class Canvas extends _Original_Canvas {
*/
@Override
public int save() {
+ // get the current save count
+ int count = mGraphicsStack.size();
+
+ // create a new graphics and add it to the stack
Graphics2D g = (Graphics2D)getGraphics2d().create();
mGraphicsStack.push(g);
- return mGraphicsStack.size() - 1;
+ // return the old save count
+ return count;
}
/* (non-Javadoc)
@@ -274,10 +279,9 @@ public class Canvas extends _Original_Canvas {
*/
@Override
public int getSaveCount() {
- return mGraphicsStack.size() - 1;
+ return mGraphicsStack.size();
}
-
/* (non-Javadoc)
* @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
*/
@@ -953,10 +957,6 @@ public class Canvas extends _Original_Canvas {
*/
@Override
public void setMatrix(Matrix matrix) {
- // since SetMatrix *replaces* all the other transformation, we have to restore/save
- restore();
- save();
-
// get the new current graphics
Graphics2D g = getGraphics2d();
@@ -968,6 +968,27 @@ public class Canvas extends _Original_Canvas {
}
}
+ /* (non-Javadoc)
+ * @see android.graphics.Canvas#concat(android.graphics.Matrix)
+ */
+ @Override
+ public void concat(Matrix matrix) {
+ // get the current top graphics2D object.
+ Graphics2D g = getGraphics2d();
+
+ // get its current matrix
+ AffineTransform currentTx = g.getTransform();
+ // get the AffineTransform of the given matrix
+ AffineTransform matrixTx = matrix.getTransform();
+
+ // combine them so that the given matrix is applied after.
+ currentTx.preConcatenate(matrixTx);
+
+ // give it to the graphics2D as a new matrix replacing all previous transform
+ g.setTransform(currentTx);
+ }
+
+
// --------------------
/* (non-Javadoc)
@@ -1008,15 +1029,6 @@ public class Canvas extends _Original_Canvas {
}
/* (non-Javadoc)
- * @see android.graphics.Canvas#concat(android.graphics.Matrix)
- */
- @Override
- public void concat(Matrix matrix) {
- // TODO Auto-generated method stub
- super.concat(matrix);
- }
-
- /* (non-Javadoc)
* @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
*/
@Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
index 3974e08..522415c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -87,8 +87,12 @@ public class Matrix extends _Original_Matrix {
}
public AffineTransform getTransform() {
- return new AffineTransform(mValues[0], mValues[1], mValues[2],
- mValues[3], mValues[4], mValues[5]);
+ // the AffineTransform constructor takes the value in a different order
+ // for a matrix [ 0 1 2 ]
+ // [ 3 4 5 ]
+ // the order is 0, 3, 1, 4, 2, 5...
+ return new AffineTransform(mValues[0], mValues[3], mValues[1],
+ mValues[4], mValues[2], mValues[5]);
}
public boolean hasPerspective() {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5a13b0b..2623570 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -43,6 +43,7 @@ public class CreateInfo {
public final static String[] RENAMED_CLASSES =
new String[] {
"android.graphics.Bitmap", "android.graphics._Original_Bitmap",
+ "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory",
"android.graphics.BitmapShader", "android.graphics._Original_BitmapShader",
"android.graphics.Canvas", "android.graphics._Original_Canvas",
"android.graphics.ComposeShader", "android.graphics._Original_ComposeShader",