Merge "3012761 Please fix problems with your strings"
diff --git a/api/current.xml b/api/current.xml
index e3b6a01..aadffa3 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28771,17 +28771,6 @@
visibility="public"
>
</field>
-<field name="COLUMN_ERROR_CODE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""error_code""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="COLUMN_ID"
type="java.lang.String"
transient="false"
@@ -28826,6 +28815,17 @@
visibility="public"
>
</field>
+<field name="COLUMN_REASON"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""reason""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="COLUMN_STATUS"
type="java.lang.String"
transient="false"
@@ -28980,6 +28980,50 @@
visibility="public"
>
</field>
+<field name="PAUSED_QUEUED_FOR_WIFI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PAUSED_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PAUSED_WAITING_FOR_NETWORK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PAUSED_WAITING_TO_RETRY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="STATUS_FAILED"
type="int"
transient="false"
@@ -61065,6 +61109,16 @@
visibility="public"
>
</field>
+<field name="filename"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="flags"
type="int"
transient="false"
@@ -139818,7 +139872,7 @@
visibility="public"
>
<method name="allowThreadDiskReads"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
abstract="false"
native="false"
synchronized="false"
@@ -139829,7 +139883,7 @@
>
</method>
<method name="allowThreadDiskWrites"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
abstract="false"
native="false"
synchronized="false"
@@ -139840,7 +139894,18 @@
>
</method>
<method name="getThreadPolicy"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVmPolicy"
+ return="android.os.StrictMode.VmPolicy"
abstract="false"
native="false"
synchronized="false"
@@ -139860,86 +139925,313 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="policyMask" type="int">
+<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
</parameter>
</method>
-<field name="DISALLOW_DISK_READ"
- type="int"
+<method name="setVmPolicy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="policy" type="android.os.StrictMode.VmPolicy">
+</parameter>
+</method>
+</class>
+<class name="StrictMode.ThreadPolicy"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="LAX"
+ type="android.os.StrictMode.ThreadPolicy"
transient="false"
volatile="false"
- value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="DISALLOW_DISK_WRITE"
- type="int"
+</class>
+<class name="StrictMode.ThreadPolicy.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StrictMode.ThreadPolicy.Builder"
+ type="android.os.StrictMode.ThreadPolicy.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="StrictMode.ThreadPolicy.Builder"
+ type="android.os.StrictMode.ThreadPolicy.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
+</parameter>
+</constructor>
+<method name="build"
+ return="android.os.StrictMode.ThreadPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectAll"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectDiskReads"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectDiskWrites"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectNetwork"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDeath"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDialog"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDropBox"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyLog"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitAll"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitDiskReads"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitDiskWrites"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitNetwork"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="StrictMode.VmPolicy"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="LAX"
+ type="android.os.StrictMode.VmPolicy"
transient="false"
volatile="false"
- value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="DISALLOW_NETWORK"
- type="int"
- transient="false"
- volatile="false"
- value="4"
+</class>
+<class name="StrictMode.VmPolicy.Builder"
+ extends="java.lang.Object"
+ abstract="false"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
-</field>
-<field name="PENALTY_DEATH"
- type="int"
- transient="false"
- volatile="false"
- value="64"
- static="true"
- final="true"
+<constructor name="StrictMode.VmPolicy.Builder"
+ type="android.os.StrictMode.VmPolicy.Builder"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
-</field>
-<field name="PENALTY_DIALOG"
- type="int"
- transient="false"
- volatile="false"
- value="32"
- static="true"
- final="true"
+</constructor>
+<method name="build"
+ return="android.os.StrictMode.VmPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
-</field>
-<field name="PENALTY_DROPBOX"
- type="int"
- transient="false"
- volatile="false"
- value="128"
- static="true"
- final="true"
+</method>
+<method name="detectAll"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
-</field>
-<field name="PENALTY_LOG"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
+</method>
+<method name="detectLeakedSqlLiteObjects"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
-</field>
+</method>
+<method name="penaltyDeath"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDropBox"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyLog"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
</class>
<class name="SystemClock"
extends="java.lang.Object"
@@ -140501,8 +140793,6 @@
</parameter>
<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
</method>
<method name="unregisterListener"
return="void"
@@ -189568,6 +189858,217 @@
>
</field>
</class>
+<class name="DragEvent"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAction"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipData"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipDescription"
+ return="android.content.ClipDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="action" type="int">
+</parameter>
+<parameter name="x" type="float">
+</parameter>
+<parameter name="y" type="float">
+</parameter>
+<parameter name="description" type="android.content.ClipDescription">
+</parameter>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ACTION_DRAG_ENDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_ENTERED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_EXITED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_LOCATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_STARTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DROP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<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="FocusFinder"
extends="java.lang.Object"
abstract="false"
@@ -195957,6 +196458,19 @@
<parameter name="y" type="float">
</parameter>
</method>
+<method name="transform"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="matrix" type="android.graphics.Matrix">
+</parameter>
+</method>
<method name="writeToParcel"
return="void"
abstract="false"
@@ -198511,6 +199025,19 @@
<parameter name="hint" type="int">
</parameter>
</method>
+<method name="dispatchDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
<method name="dispatchDraw"
return="void"
abstract="false"
@@ -200476,6 +201003,19 @@
<parameter name="hint" type="int">
</parameter>
</method>
+<method name="onDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
<method name="onDraw"
return="void"
abstract="false"
@@ -200489,6 +201029,19 @@
<parameter name="canvas" type="android.graphics.Canvas">
</parameter>
</method>
+<method name="onDrawDragThumbnail"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
<method name="onDrawScrollBars"
return="void"
abstract="false"
@@ -200682,6 +201235,17 @@
<parameter name="heightMeasureSpec" type="int">
</parameter>
</method>
+<method name="onMeasureDragThumbnail"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
<method name="onRestoreInstanceState"
return="void"
abstract="false"
@@ -201347,6 +201911,21 @@
<parameter name="contentDescription" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setDragThumbnailDimension"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</method>
<method name="setDrawingCacheBackgroundColor"
return="void"
abstract="false"
@@ -241659,7 +242238,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
</parameter>
</method>
</interface>
@@ -249418,7 +249997,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="n" type="long">
+<parameter name="byteCount" type="long">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
@@ -283808,9 +284387,9 @@
deprecated="not deprecated"
visibility="protected"
>
-<parameter name="url1" type="java.net.URL">
+<parameter name="a" type="java.net.URL">
</parameter>
-<parameter name="url2" type="java.net.URL">
+<parameter name="b" type="java.net.URL">
</parameter>
</method>
<method name="openConnection"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 39b3a20..37c8ad0 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -34,7 +34,6 @@
private String[] mArgs;
private int mNextArg;
- private String mCurArgData;
public static void main(String[] args) {
try {
@@ -274,6 +273,10 @@
}
private void printRestoreSets(RestoreSet[] sets) {
+ if (sets == null || sets.length == 0) {
+ System.out.println("No restore sets");
+ return;
+ }
for (RestoreSet s : sets) {
System.out.println(" " + Long.toHexString(s.token) + " : " + s.name);
}
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index 60cc521..971a177 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -166,7 +166,7 @@
int length;
int fd;
- if (read(the_entropy, vector, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) {
+ if (read(the_entropy, blob.vector, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) {
return SYSTEM_ERROR;
}
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
index 141f69b..4b4923e 100644
--- a/cmds/keystore/keystore_get.h
+++ b/cmds/keystore/keystore_get.h
@@ -32,7 +32,7 @@
#endif
/* This function is provided for native components to get values from keystore.
- * Users are required to link against libcutils. Keys are values are 8-bit safe.
+ * Users are required to link against libcutils. Keys and values are 8-bit safe.
* The first two arguments are the key and its length. The third argument
* specifies the buffer to store the retrieved value, which must be an array of
* KEYSTORE_MESSAGE_SIZE bytes. This function returns the length of the value or
@@ -65,7 +65,10 @@
}
offset += n;
}
+ } else {
+ length = -1;
}
+
close(sock);
return length;
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6ce5b86..bc5e10d 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -17,32 +17,21 @@
#include <unistd.h>
#include <fcntl.h>
-#include <utils/Log.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-
#include <binder/IMemory.h>
-#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
using namespace android;
int main(int argc, char** argv)
{
- const String16 name("SurfaceFlinger");
- sp<ISurfaceComposer> composer;
- if (getService(name, &composer) != NO_ERROR)
+ ScreenshotClient screenshot;
+ if (screenshot.update() != NO_ERROR)
return 0;
- sp<IMemoryHeap> heap;
- uint32_t w, h;
- PixelFormat f;
- status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
- if (err != NO_ERROR)
- return 0;
-
- uint8_t* base = (uint8_t*)heap->getBase();
+ void const* base = screenshot.getPixels();
+ uint32_t w = screenshot.getWidth();
+ uint32_t h = screenshot.getHeight();
+ uint32_t f = screenshot.getFormat();
int fd = dup(STDOUT_FILENO);
write(fd, &w, 4);
write(fd, &h, 4);
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 9a97284..cbdf119 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -53,6 +53,31 @@
LOCAL_SRC_FILES:= \
SineSource.cpp \
+ recordvideo.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright liblog libutils libbinder
+
+LOCAL_C_INCLUDES:= \
+ $(JNI_H_INCLUDE) \
+ frameworks/base/media/libstagefright \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= recordvideo
+
+include $(BUILD_EXECUTABLE)
+
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SineSource.cpp \
audioloop.cpp
LOCAL_SHARED_LIBRARIES := \
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
new file mode 100644
index 0000000..330fbc2
--- /dev/null
+++ b/cmds/stagefright/recordvideo.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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.
+ */
+
+#include "SineSource.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/MediaPlayerInterface.h>
+
+using namespace android;
+
+// print usage showing how to use this utility to record videos
+static void usage(const char *me) {
+ fprintf(stderr, "usage: %s\n", me);
+ fprintf(stderr, " -h(elp)\n");
+ fprintf(stderr, " -b bit rate in bits per second (default 300000)\n");
+ fprintf(stderr, " -c YUV420 color format: [0] semi planar or [1] planar (default 1)\n");
+ fprintf(stderr, " -f frame rate in frames per second (default 30)\n");
+ fprintf(stderr, " -i I frame interval in seconds (default 1)\n");
+ fprintf(stderr, " -n number of frames to be recorded (default 300)\n");
+ fprintf(stderr, " -w width in pixels (default 176)\n");
+ fprintf(stderr, " -t height in pixels (default 144)\n");
+ fprintf(stderr, " -v video codec: [0] AVC [1] M4V [2] H263 (default 0)\n");
+ exit(1);
+}
+
+class DummySource : public MediaSource {
+
+public:
+ DummySource(int width, int height, int nFrames, int fps, int colorFormat)
+ : mWidth(width),
+ mHeight(height),
+ mMaxNumFrames(nFrames),
+ mFrameRate(fps),
+ mColorFormat(colorFormat),
+ mSize((width * height * 3) / 2) {
+ mGroup.add_buffer(new MediaBuffer(mSize));
+
+ // Check the color format to make sure
+ // that the buffer size mSize it set correctly above.
+ CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+ colorFormat == OMX_COLOR_FormatYUV420Planar);
+ }
+
+ virtual sp<MetaData> getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
+ meta->setInt32(kKeyColorFormat, mColorFormat);
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+
+ return meta;
+ }
+
+ virtual status_t start(MetaData *params) {
+ mNumFramesOutput = 0;
+ return OK;
+ }
+
+ virtual status_t stop() {
+ return OK;
+ }
+
+ virtual status_t read(
+ MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+
+ if (mNumFramesOutput % 10 == 0) {
+ fprintf(stderr, ".");
+ }
+ if (mNumFramesOutput == mMaxNumFrames) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ status_t err = mGroup.acquire_buffer(buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ char x = (char)((double)rand() / RAND_MAX * 255);
+ memset((*buffer)->data(), x, mSize);
+ (*buffer)->set_range(0, mSize);
+ (*buffer)->meta_data()->clear();
+ (*buffer)->meta_data()->setInt64(
+ kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate);
+ ++mNumFramesOutput;
+
+ return OK;
+ }
+
+protected:
+ virtual ~DummySource() {}
+
+private:
+ MediaBufferGroup mGroup;
+ int mWidth, mHeight;
+ int mMaxNumFrames;
+ int mFrameRate;
+ int mColorFormat;
+ size_t mSize;
+ int64_t mNumFramesOutput;;
+
+ DummySource(const DummySource &);
+ DummySource &operator=(const DummySource &);
+};
+
+sp<MediaSource> createSource(const char *filename) {
+ sp<MediaSource> source;
+
+ sp<MediaExtractor> extractor =
+ MediaExtractor::Create(new FileSource(filename));
+ if (extractor == NULL) {
+ return NULL;
+ }
+
+ size_t num_tracks = extractor->countTracks();
+
+ sp<MetaData> meta;
+ for (size_t i = 0; i < num_tracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
+ CHECK(meta.get() != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ if (strncasecmp(mime, "video/", 6)) {
+ continue;
+ }
+
+ source = extractor->getTrack(i);
+ break;
+ }
+
+ return source;
+}
+
+enum {
+ kYUV420SP = 0,
+ kYUV420P = 1,
+};
+
+// returns -1 if mapping of the given color is unsuccessful
+// returns an omx color enum value otherwise
+static int translateColorToOmxEnumValue(int color) {
+ switch (color) {
+ case kYUV420SP:
+ return OMX_COLOR_FormatYUV420SemiPlanar;
+ case kYUV420P:
+ return OMX_COLOR_FormatYUV420Planar;
+ default:
+ fprintf(stderr, "Unsupported color: %d\n", color);
+ return -1;
+ }
+}
+
+int main(int argc, char **argv) {
+
+ // Default values for the program if not overwritten
+ int frameRateFps = 30;
+ int width = 176;
+ int height = 144;
+ int bitRateBps = 300000;
+ int iFramesIntervalSeconds = 1;
+ int colorFormat = OMX_COLOR_FormatYUV420Planar;
+ int nFrames = 300;
+ int codec = 0;
+ const char *fileName = "/sdcard/output.mp4";
+
+ android::ProcessState::self()->startThreadPool();
+ int res;
+ while ((res = getopt(argc, argv, "b:c:f:i:n:w:t:v:o:h")) >= 0) {
+ switch (res) {
+ case 'b':
+ {
+ bitRateBps = atoi(optarg);
+ break;
+ }
+
+ case 'c':
+ {
+ colorFormat = translateColorToOmxEnumValue(atoi(optarg));
+ if (colorFormat == -1) {
+ usage(argv[0]);
+ }
+ break;
+ }
+
+ case 'f':
+ {
+ frameRateFps = atoi(optarg);
+ break;
+ }
+
+ case 'i':
+ {
+ iFramesIntervalSeconds = atoi(optarg);
+ break;
+ }
+
+ case 'n':
+ {
+ nFrames = atoi(optarg);
+ break;
+ }
+
+ case 'w':
+ {
+ width = atoi(optarg);
+ break;
+ }
+
+ case 't':
+ {
+ height = atoi(optarg);
+ break;
+ }
+
+ case 'v':
+ {
+ codec = atoi(optarg);
+ if (codec < 0 || codec > 2) {
+ usage(argv[0]);
+ }
+ break;
+ }
+
+ case 'h':
+ default:
+ {
+ usage(argv[0]);
+ break;
+ }
+ }
+ }
+
+ OMXClient client;
+ CHECK_EQ(client.connect(), OK);
+
+ status_t err = OK;
+ sp<MediaSource> decoder = new DummySource(width, height, nFrames, frameRateFps, colorFormat);
+
+ sp<MetaData> enc_meta = new MetaData;
+ switch (codec) {
+ case 1:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ break;
+ case 2:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ break;
+ default:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
+ }
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+ enc_meta->setInt32(kKeySampleRate, frameRateFps);
+ enc_meta->setInt32(kKeyBitRate, bitRateBps);
+ enc_meta->setInt32(kKeyStride, width);
+ enc_meta->setInt32(kKeySliceHeight, height);
+ enc_meta->setInt32(kKeyIFramesInterval, iFramesIntervalSeconds);
+ enc_meta->setInt32(kKeyColorFormat, colorFormat);
+
+ sp<MediaSource> encoder =
+ OMXCodec::Create(
+ client.interface(), enc_meta, true /* createEncoder */, decoder);
+
+ sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
+ writer->addSource(encoder);
+ int64_t start = systemTime();
+ CHECK_EQ(OK, writer->start());
+ while (!writer->reachedEOS()) {
+ }
+ err = writer->stop();
+ int64_t end = systemTime();
+
+ printf("$\n");
+ client.disconnect();
+
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ fprintf(stderr, "record failed: %d\n", err);
+ return 1;
+ }
+ fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000);
+ fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
+ return 0;
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b8bbc88..df18ce7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3352,7 +3352,7 @@
while (i.hasNext()) {
ProviderInfo cpi = i.next();
StringBuilder buf = new StringBuilder(128);
- buf.append("Publishing provider ");
+ buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 69c99cc..013032c 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -47,6 +47,10 @@
* Instances of this class should be obtained through
* {@link android.content.Context#getSystemService(String)} by passing
* {@link android.content.Context#DOWNLOAD_SERVICE}.
+ *
+ * Apps that request downloads through this API should register a broadcast receiver for
+ * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running
+ * download in a notification or from the downloads UI.
*/
public class DownloadManager {
/**
@@ -100,16 +104,23 @@
public final static String COLUMN_STATUS = "status";
/**
- * Indicates the type of error that occurred, when {@link #COLUMN_STATUS} is
- * {@link #STATUS_FAILED}. If an HTTP error occurred, this will hold the HTTP status code as
- * defined in RFC 2616. Otherwise, it will hold one of the ERROR_* constants.
+ * Provides more detail on the status of the download. Its meaning depends on the value of
+ * {@link #COLUMN_STATUS}.
*
- * If {@link #COLUMN_STATUS} is not {@link #STATUS_FAILED}, this column's value is undefined.
+ * When {@link #COLUMN_STATUS} is {@link #STATUS_FAILED}, this indicates the type of error that
+ * occurred. If an HTTP error occurred, this will hold the HTTP status code as defined in RFC
+ * 2616. Otherwise, it will hold one of the ERROR_* constants.
+ *
+ * When {@link #COLUMN_STATUS} is {@link #STATUS_PAUSED}, this indicates why the download is
+ * paused. It will hold one of the PAUSED_* constants.
+ *
+ * If {@link #COLUMN_STATUS} is neither {@link #STATUS_FAILED} nor {@link #STATUS_PAUSED}, this
+ * column's value is undefined.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">RFC 2616
* status codes</a>
*/
- public final static String COLUMN_ERROR_CODE = "error_code";
+ public final static String COLUMN_REASON = "reason";
/**
* Number of bytes download so far.
@@ -156,61 +167,84 @@
public final static int ERROR_UNKNOWN = 1000;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when a storage issue arises which doesn't fit under any
+ * Value of {@link #COLUMN_REASON} when a storage issue arises which doesn't fit under any
* other error code. Use the more specific {@link #ERROR_INSUFFICIENT_SPACE} and
* {@link #ERROR_DEVICE_NOT_FOUND} when appropriate.
*/
public final static int ERROR_FILE_ERROR = 1001;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when an HTTP code was received that download manager
+ * Value of {@link #COLUMN_REASON} when an HTTP code was received that download manager
* can't handle.
*/
public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when an error receiving or processing data occurred at
+ * Value of {@link #COLUMN_REASON} when an error receiving or processing data occurred at
* the HTTP level.
*/
public final static int ERROR_HTTP_DATA_ERROR = 1004;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when there were too many redirects.
+ * Value of {@link #COLUMN_REASON} when there were too many redirects.
*/
public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when there was insufficient storage space. Typically,
+ * Value of {@link #COLUMN_REASON} when there was insufficient storage space. Typically,
* this is because the SD card is full.
*/
public final static int ERROR_INSUFFICIENT_SPACE = 1006;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when no external storage device was found. Typically,
+ * Value of {@link #COLUMN_REASON} when no external storage device was found. Typically,
* this is because the SD card is not mounted.
*/
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+ * Value of {@link #COLUMN_REASON} when some possibly transient error occurred but we can't
* resume the download.
*/
public final static int ERROR_CANNOT_RESUME = 1008;
/**
- * Value of {@link #COLUMN_ERROR_CODE} when the requested destination file already exists (the
+ * Value of {@link #COLUMN_REASON} when the requested destination file already exists (the
* download manager will not overwrite an existing file).
*/
public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
/**
+ * Value of {@link #COLUMN_REASON} when the download is paused because some network error
+ * occurred and the download manager is waiting before retrying the request.
+ */
+ public final static int PAUSED_WAITING_TO_RETRY = 1;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download is waiting for network connectivity to
+ * proceed.
+ */
+ public final static int PAUSED_WAITING_FOR_NETWORK = 2;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download exceeds a size limit for downloads over
+ * the mobile network and the download manager is waiting for a Wi-Fi connection to proceed.
+ */
+ public final static int PAUSED_QUEUED_FOR_WIFI = 3;
+
+ /**
+ * Value of {@link #COLUMN_REASON} when the download is paused for some other reason.
+ */
+ public final static int PAUSED_UNKNOWN = 4;
+
+ /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
/**
- * Broadcast intent action sent by the download manager when a running download notification is
- * clicked.
+ * Broadcast intent action sent by the download manager when the user clicks on a running
+ * download, either from a system notification or from the downloads UI.
*/
public final static String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
@@ -236,7 +270,7 @@
COLUMN_TOTAL_SIZE_BYTES,
COLUMN_LOCAL_URI,
COLUMN_STATUS,
- COLUMN_ERROR_CODE,
+ COLUMN_REASON,
COLUMN_BYTES_DOWNLOADED_SO_FAR,
COLUMN_LAST_MODIFIED_TIMESTAMP
};
@@ -258,7 +292,7 @@
};
private static final Set<String> LONG_COLUMNS = new HashSet<String>(
- Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_ERROR_CODE,
+ Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_REASON,
COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
/**
@@ -383,7 +417,9 @@
}
/**
- * Set the title of this download, to be displayed in notifications (if enabled)
+ * Set the title of this download, to be displayed in notifications (if enabled). If no
+ * title is given, a default one will be assigned based on the download filename, once the
+ * download starts.
* @return this object
*/
public Request setTitle(CharSequence title) {
@@ -617,8 +653,10 @@
parts.add(statusClause("=", Downloads.STATUS_RUNNING));
}
if ((mStatusFlags & STATUS_PAUSED) != 0) {
- parts.add(statusClause("=", Downloads.STATUS_PENDING_PAUSED));
- parts.add(statusClause("=", Downloads.STATUS_RUNNING_PAUSED));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_APP));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK));
+ parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
}
if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
@@ -891,7 +929,11 @@
if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
// return stored destination for legacy external download
- return Uri.fromFile(new File(getUnderlyingString(Downloads.Impl._DATA))).toString();
+ String localPath = getUnderlyingString(Downloads.Impl._DATA);
+ if (localPath == null) {
+ return null;
+ }
+ return Uri.fromFile(new File(localPath)).toString();
}
// return content URI for cache download
@@ -914,8 +956,8 @@
if (column.equals(COLUMN_STATUS)) {
return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
}
- if (column.equals(COLUMN_ERROR_CODE)) {
- return translateErrorCode((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+ if (column.equals(COLUMN_REASON)) {
+ return getReason((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
}
if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
@@ -924,10 +966,36 @@
return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
}
- private long translateErrorCode(int status) {
- if (translateStatus(status) != STATUS_FAILED) {
- return 0; // arbitrary value when status is not an error
+ private long getReason(int status) {
+ switch (translateStatus(status)) {
+ case STATUS_FAILED:
+ return getErrorCode(status);
+
+ case STATUS_PAUSED:
+ return getPausedReason(status);
+
+ default:
+ return 0; // arbitrary value when status is not an error
}
+ }
+
+ private long getPausedReason(int status) {
+ switch (status) {
+ case Downloads.Impl.STATUS_WAITING_TO_RETRY:
+ return PAUSED_WAITING_TO_RETRY;
+
+ case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
+ return PAUSED_WAITING_FOR_NETWORK;
+
+ case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
+ return PAUSED_QUEUED_FOR_WIFI;
+
+ default:
+ return PAUSED_UNKNOWN;
+ }
+ }
+
+ private long getErrorCode(int status) {
if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
|| (500 <= status && status < 600)) {
// HTTP status code
@@ -973,7 +1041,7 @@
return super.getString(super.getColumnIndex(column));
}
- private long translateStatus(int status) {
+ private int translateStatus(int status) {
switch (status) {
case Downloads.STATUS_PENDING:
return STATUS_PENDING;
@@ -981,8 +1049,10 @@
case Downloads.STATUS_RUNNING:
return STATUS_RUNNING;
- case Downloads.STATUS_PENDING_PAUSED:
- case Downloads.STATUS_RUNNING_PAUSED:
+ case Downloads.Impl.STATUS_PAUSED_BY_APP:
+ case Downloads.Impl.STATUS_WAITING_TO_RETRY:
+ case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
+ case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return STATUS_PAUSED;
case Downloads.STATUS_SUCCESS:
diff --git a/core/java/android/bluetooth/AtCommandResult.java b/core/java/android/bluetooth/AtCommandResult.java
index 638be2d..375a6dd 100644
--- a/core/java/android/bluetooth/AtCommandResult.java
+++ b/core/java/android/bluetooth/AtCommandResult.java
@@ -16,8 +16,6 @@
package android.bluetooth;
-import java.util.*;
-
/**
* The result of execution of an single AT command.<p>
*
diff --git a/core/java/android/bluetooth/AtParser.java b/core/java/android/bluetooth/AtParser.java
index 1ea3150..328fb2b 100644
--- a/core/java/android/bluetooth/AtParser.java
+++ b/core/java/android/bluetooth/AtParser.java
@@ -16,16 +16,13 @@
package android.bluetooth;
-import android.bluetooth.AtCommandHandler;
-import android.bluetooth.AtCommandResult;
-
import java.util.*;
/**
* An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard.
* <p>
*
- * Conforment with the subset of V.250 required for implementation of the
+ * Conformant with the subset of V.250 required for implementation of the
* Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP
* specifications. Also implements some V.250 features not required by
* Bluetooth - such as chained commands.<p>
@@ -48,7 +45,7 @@
* are no arguments for get commands.
* <li>Set Command. For example "AT+VGM=14". The command name is "VGM", and
* there is a single integer argument in this case. In the general case then
- * can be zero or more arguments (comma deliminated) each of integer or string
+ * can be zero or more arguments (comma delimited) each of integer or string
* form.
* <li>Test Command. For example "AT+VGM=?. No arguments.
* </ul>
@@ -60,7 +57,7 @@
* headset/handsfree use this is acceptable, because they only use the basic
* commands ATA and ATD, which are not allowed to be chained. For general V.250
* use we would need to improve this class to allow Basic command chaining -
- * however its tricky to get right becuase there is no deliminator for Basic
+ * however it's tricky to get right because there is no delimiter for Basic
* command chaining.<p>
*
* Extended commands can be chained. For example:<p>
@@ -71,7 +68,7 @@
* AT+CIMI
* Except that only one final result code is return (although several
* intermediate responses may be returned), and as soon as one command in the
- * chain fails the rest are abandonded.<p>
+ * chain fails the rest are abandoned.<p>
*
* Handlers are registered by there command name via register(Char c, ...) or
* register(String s, ...). Handlers for Basic command should be registered by
@@ -80,7 +77,7 @@
*
* Refer to:<ul>
* <li>ITU-T Recommendation V.250
- * <li>ETSI TS 127.007 (AT Comannd set for User Equipment, 3GPP TS 27.007)
+ * <li>ETSI TS 127.007 (AT Command set for User Equipment, 3GPP TS 27.007)
* <li>Bluetooth Headset Profile Spec (K6)
* <li>Bluetooth Handsfree Profile Spec (HFP 1.5)
* </ul>
@@ -188,7 +185,7 @@
}
/**
- * Break an argument string into individual arguments (comma deliminated).
+ * Break an argument string into individual arguments (comma delimited).
* Integer arguments are turned into Integer objects. Otherwise a String
* object is used.
*/
@@ -212,7 +209,7 @@
}
/**
- * Return the index of the end of character after the last characeter in
+ * Return the index of the end of character after the last character in
* the extended command name. Uses the V.250 spec for allowed command
* names.
*/
@@ -244,7 +241,7 @@
* Processes an incoming AT command line.<p>
* This method will invoke zero or one command handler methods for each
* command in the command line.<p>
- * @param raw_input The AT input, without EOL deliminator (e.g. <CR>).
+ * @param raw_input The AT input, without EOL delimiter (e.g. <CR>).
* @return Result object for this command line. This can be
* converted to a String[] response with toStrings().
*/
@@ -297,8 +294,8 @@
if (c == '+') {
// Option 2: Extended Command
- // Search for first non-name character. Shortcircuit if we dont
- // handle this command name.
+ // Search for first non-name character. Short-circuit if
+ // we don't handle this command name.
int i = findEndExtendedName(input, index + 1);
String commandName = input.substring(index, i);
if (!mExtHandlers.containsKey(commandName)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 21a4bd6..c66b2de 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -341,7 +341,7 @@
private static final int ADDRESS_LENGTH = 17;
/**
- * Lazyily initialized singleton. Guaranteed final after first object
+ * Lazily initialized singleton. Guaranteed final after first object
* constructed.
*/
private static BluetoothAdapter sAdapter;
@@ -466,7 +466,7 @@
* user action to turn off Bluetooth.
* <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
* system services, and powers down the underlying Bluetooth hardware.
- * <p class="caution"><strong>Bluetooth should never be disbled without
+ * <p class="caution"><strong>Bluetooth should never be disabled without
* direct user consent</strong>. The {@link #disable()} method is
* provided only for applications that include a user interface for changing
* system settings, such as a "power manager" app.</p>
@@ -932,8 +932,8 @@
public Pair<byte[], byte[]> readOutOfBandData() {
if (getState() != STATE_ON) return null;
try {
- byte[] hash = new byte[16];
- byte[] randomizer = new byte[16];
+ byte[] hash;
+ byte[] randomizer;
byte[] ret = mService.readOutOfBandData();
diff --git a/core/java/android/bluetooth/BluetoothAudioGateway.java b/core/java/android/bluetooth/BluetoothAudioGateway.java
index bc32060..9351393 100644
--- a/core/java/android/bluetooth/BluetoothAudioGateway.java
+++ b/core/java/android/bluetooth/BluetoothAudioGateway.java
@@ -23,7 +23,7 @@
import android.util.Log;
/**
- * Listen's for incoming RFCOMM connection for the headset / handsfree service.
+ * Listens for incoming RFCOMM connection for the headset / handsfree service.
*
* TODO: Use the new generic BluetoothSocket class instead of this legacy code
*
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index c8381c9..e604e6b 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -34,8 +34,8 @@
* Bluetooth profiles or services are actually supported by a device. Accurate
* service discovery is done through SDP requests, which are automatically
* performed when creating an RFCOMM socket with {@link
- * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}</p>
+ * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p>
*
* <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
* a remote device.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e577ec4..ada3c24 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -32,7 +32,7 @@
/**
* Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
- * create a connection with the repective device or query information about
+ * create a connection with the respective device or query information about
* it, such as the name, address, class, and bonding state.
*
* <p>This class is really just a thin wrapper for a Bluetooth hardware
@@ -48,7 +48,7 @@
* {@link BluetoothAdapter}) or get one from the set of bonded devices
* returned by {@link BluetoothAdapter#getBondedDevices()
* BluetoothAdapter.getBondedDevices()}. You can then open a
- * {@link BluetoothSocket} for communciation with the remote device, using
+ * {@link BluetoothSocket} for communication with the remote device, using
* {@link #createRfcommSocketToServiceRecord(UUID)}.
*
* <p class="note"><strong>Note:</strong>
@@ -226,8 +226,8 @@
* <p>A shared link keys exists locally for the remote device, so
* communication can be authenticated and encrypted.
* <p><i>Being bonded (paired) with a remote device does not necessarily
- * mean the device is currently connected. It just means that the ponding
- * procedure was compeleted at some earlier time, and the link key is still
+ * mean the device is currently connected. It just means that the pending
+ * procedure was completed at some earlier time, and the link key is still
* stored locally, ready to use on the next connection.
* </i>
*/
@@ -283,7 +283,7 @@
* not respond to pin request in time
* @hide */
public static final int UNBOND_REASON_AUTH_FAILED = 1;
- /** A bond attempt failed because the other side explicilty rejected
+ /** A bond attempt failed because the other side explicitly rejected
* bonding
* @hide */
public static final int UNBOND_REASON_AUTH_REJECTED = 2;
@@ -515,7 +515,7 @@
* Cancel an in-progress bonding request started with {@link #createBond}.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * @return true on sucess, false on error
+ * @return true on success, false on error
* @hide
*/
public boolean cancelBondProcess() {
@@ -532,7 +532,7 @@
* authentication and encryption.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * @return true on sucess, false on error
+ * @return true on success, false on error
* @hide
*/
public boolean removeBond() {
@@ -617,7 +617,7 @@
* with the UUIDs supported by the remote end. If there is an error
* in getting the SDP records or if the process takes a long time,
* an Intent is sent with the UUIDs that is currently present in the
- * cache. Clients should use the {@link getUuids} to get UUIDs
+ * cache. Clients should use the {@link #getUuids} to get UUIDs
* is SDP is not to be performed.
*
* @return False if the sanity check fails, True if the process
@@ -693,7 +693,7 @@
* outgoing connection to this remote device on given channel.
* <p>The remote device will be authenticated and communication on this
* socket will be encrypted.
- * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid RFCOMM channels are in range 1 to 30.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
@@ -715,7 +715,7 @@
* <p>This is designed to be used with {@link
* BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
* Bluetooth applications.
- * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection. This will also perform an SDP lookup of the given uuid to
* determine which channel to connect to.
* <p>The remote device will be authenticated and communication on this
@@ -772,9 +772,9 @@
/**
* Check that a pin is valid and convert to byte array.
*
- * Bluetooth pin's are 1 to 16 bytes of UTF8 characters.
+ * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
* @param pin pin as java String
- * @return the pin code as a UTF8 byte array, or null if it is an invalid
+ * @return the pin code as a UTF-8 byte array, or null if it is an invalid
* Bluetooth pin.
* @hide
*/
@@ -784,9 +784,9 @@
}
byte[] pinBytes;
try {
- pinBytes = pin.getBytes("UTF8");
+ pinBytes = pin.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
- Log.e(TAG, "UTF8 not supported?!?"); // this should not happen
+ Log.e(TAG, "UTF-8 not supported?!?"); // this should not happen
return null;
}
if (pinBytes.length <= 0 || pinBytes.length > 16) {
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
index 7415721..c794be2 100644
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/core/java/android/bluetooth/BluetoothDevicePicker.java
@@ -36,7 +36,8 @@
/**
* Broadcast when one BT device is selected from BT device picker screen.
- * Selected BT device address is contained in extra string {@link BluetoothIntent}
+ * Selected {@link BluetoothDevice} is returned in extra data named
+ * {@link BluetoothDevice#EXTRA_DEVICE}.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DEVICE_SELECTED =
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index b48f48e..4be077c 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -197,7 +197,7 @@
/**
* Disconnects the current Pbap client (PCE). Currently this call blocks,
- * it may soon be made asynchornous. Returns false if this proxy object is
+ * it may soon be made asynchronous. Returns false if this proxy object is
* not currently connected to the Pbap service.
*/
public boolean disconnect() {
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index c9c6c0a..83e59e2 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -29,14 +29,14 @@
* side, use a {@link BluetoothServerSocket} to create a listening server
* socket. When a connection is accepted by the {@link BluetoothServerSocket},
* it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * On the client side, use a single {@link BluetoothSocket} to both initiate
* an outgoing connection and to manage the connection.
*
* <p>The most common type of Bluetooth socket is RFCOMM, which is the type
* supported by the Android APIs. RFCOMM is a connection-oriented, streaming
* transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
*
- * <p>To create a listenting {@link BluetoothServerSocket} that's ready for
+ * <p>To create a listening {@link BluetoothServerSocket} that's ready for
* incoming connections, use
* {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
* BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
@@ -70,7 +70,7 @@
* @param encrypt require the connection to be encrypted
* @param port remote port
* @throws IOException On error, for example Bluetooth not available, or
- * insufficient priveleges
+ * insufficient privileges
*/
/*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
throws IOException {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index ad03399..719d730 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -35,7 +35,7 @@
* side, use a {@link BluetoothServerSocket} to create a listening server
* socket. When a connection is accepted by the {@link BluetoothServerSocket},
* it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * On the client side, use a single {@link BluetoothSocket} to both initiate
* an outgoing connection and to manage the connection.
*
* <p>The most common type of Bluetooth socket is RFCOMM, which is the type
@@ -113,7 +113,7 @@
* @param port remote port
* @param uuid SDP uuid
* @throws IOException On error, for example Bluetooth not available, or
- * insufficient priveleges
+ * insufficient privileges
*/
/*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
@@ -158,7 +158,7 @@
* @param address remote device that this socket can connect to
* @param port remote port
* @throws IOException On error, for example Bluetooth not available, or
- * insufficient priveleges
+ * insufficient privileges
*/
private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
int port) throws IOException {
@@ -226,7 +226,7 @@
}
// all native calls are guaranteed to immediately return after
- // abortNative(), so this lock should immediatley acquire
+ // abortNative(), so this lock should immediately acquire
mLock.writeLock().lock();
try {
mClosed = true;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8ef639b..eaf1e33 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1730,6 +1730,11 @@
XmlUtils.skipCurrentTag(parser);
+ } else if (tagName.equals("uses-package")) {
+ // Dependencies for app installers; we don't currently try to
+ // enforce this.
+ XmlUtils.skipCurrentTag(parser);
+
} else {
if (!RIGID_PARSER) {
Log.w(TAG, "Unknown element under <application>: " + tagName
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index d0ba590..406b091 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -18,6 +18,7 @@
import android.content.pm.ApplicationInfo;
import android.graphics.Canvas;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
@@ -363,6 +364,17 @@
}
/**
+ * Translate a Point in screen coordinates into the app window's coordinates.
+ */
+ public void translatePointInScreenToAppWindow(PointF point) {
+ final float scale = applicationInvertedScale;
+ if (scale != 1.0f) {
+ point.x *= scale;
+ point.y *= scale;
+ }
+ }
+
+ /**
* Translate the location of the sub window.
* @param params
*/
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index 7b962e5..5d6ed44 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -29,6 +29,11 @@
public static final int OBB_OVERLAY = 1 << 0;
/**
+ * The canonical filename of the OBB.
+ */
+ public String filename;
+
+ /**
* The name of the package to which the OBB file belongs.
*/
public String packageName;
@@ -66,6 +71,7 @@
}
public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(filename);
dest.writeString(packageName);
dest.writeInt(version);
dest.writeInt(flags);
@@ -83,6 +89,7 @@
};
private ObbInfo(Parcel source) {
+ filename = source.readString();
packageName = source.readString();
version = source.readInt();
flags = source.readInt();
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
index 572b75e..1b38eea 100644
--- a/core/java/android/content/res/ObbScanner.java
+++ b/core/java/android/content/res/ObbScanner.java
@@ -45,9 +45,14 @@
throw new IllegalArgumentException("OBB file does not exist: " + filePath);
}
+ /*
+ * XXX This will fail to find the real canonical path if bind mounts are
+ * used, but we don't use any bind mounts right now.
+ */
final String canonicalFilePath = obbFile.getCanonicalPath();
ObbInfo obbInfo = new ObbInfo();
+ obbInfo.filename = canonicalFilePath;
getObbInfo_native(canonicalFilePath, obbInfo);
return obbInfo;
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 6ed1a90..a7ad757 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -16,6 +16,7 @@
package android.database.sqlite;
+import android.os.StrictMode;
import android.util.Log;
/**
@@ -106,11 +107,13 @@
// but if the database itself is not closed and is GC'ed, then
// all sub-objects attached to the database could end up getting GC'ed too.
// in that case, don't print any warning.
- if (mInUse) {
+ if (mInUse && StrictMode.vmSqliteObjectLeaksEnabled()) {
int len = mSqlStmt.length();
- Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
- "that you explicitly call close() on your cursor: " +
- mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
+ StrictMode.onSqliteObjectLeaked(
+ "Releasing statement in a finalizer. Please ensure " +
+ "that you explicitly call close() on your cursor: " +
+ mSqlStmt.substring(0, (len > 100) ? 100 : len),
+ mStackTrace);
}
releaseSqlStatement();
} finally {
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index fa7763d..89e8ab7 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -25,6 +25,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.StrictMode;
import android.util.Config;
import android.util.Log;
@@ -505,11 +506,14 @@
try {
// if the cursor hasn't been closed yet, close it first
if (mWindow != null) {
- int len = mQuery.mSql.length();
- Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
+ if (StrictMode.vmSqliteObjectLeaksEnabled()) {
+ int len = mQuery.mSql.length();
+ StrictMode.onSqliteObjectLeaked(
+ "Finalizing a Cursor that has not been deactivated or closed. " +
"database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
mStackTrace);
+ }
close();
SQLiteDebug.notifyActiveCursorFinalized();
} else {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0d8228c..7b930d5 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -181,6 +181,7 @@
* value should be 90.
*
* @see #setDisplayOrientation(int)
+ * @see #setRotation(int)
*/
public int orientation;
};
@@ -1716,23 +1717,46 @@
}
/**
- * Sets the orientation of the device in degrees. For example, suppose
- * the natural position of the device is landscape. If the user takes a
- * picture in landscape mode in 2048x1536 resolution, the rotation
- * should be set to 0. If the user rotates the phone 90 degrees
- * clockwise, the rotation should be set to 90. Applications can use
- * {@link android.view.OrientationEventListener} to set this parameter.
+ * Sets the rotation angle in degrees relative to the orientation of
+ * the camera. This affects the pictures returned from JPEG {@link
+ * PictureCallback}. The camera driver may set orientation in the
+ * EXIF header without rotating the picture. Or the driver may rotate
+ * the picture and the EXIF thumbnail. If the Jpeg picture is rotated,
+ * the orientation in the EXIF header will be missing or 1 (row #0 is
+ * top and column #0 is left side).
*
- * The camera driver may set orientation in the EXIF header without
- * rotating the picture. Or the driver may rotate the picture and
- * the EXIF thumbnail. If the Jpeg picture is rotated, the orientation
- * in the EXIF header will be missing or 1 (row #0 is top and column #0
- * is left side).
+ * If appplications want to rotate the picture to match the
+ * orientation of what users see, apps should use {@link
+ * android.view.OrientationEventListener} and {@link CameraInfo}.
+ * The value from OrientationEventListener is relative to the natural
+ * orientation of the device. CameraInfo.mOrientation is the angle
+ * between camera orientation and natural device orientation. The sum
+ * of the two is the angle for rotation.
*
- * @param rotation The orientation of the device in degrees. Rotation
- * can only be 0, 90, 180 or 270.
+ * For example, suppose the natural orientation of the device is
+ * portrait. The device is rotated 270 degrees clockwise, so the device
+ * orientation is 270. Suppose the camera sensor is mounted in landscape
+ * and the top side of the camera sensor is aligned with the right edge
+ * of the display in natural orientation. So the camera orientation is
+ * 90. The rotation should be set to 0 (270 + 90).
+ *
+ * The reference code is as follows.
+ *
+ * public void public void onOrientationChanged(int orientation) {
+ * if (orientation == ORIENTATION_UNKNOWN) return;
+ * android.hardware.Camera.CameraInfo info =
+ * new android.hardware.Camera.CameraInfo();
+ * android.hardware.Camera.getCameraInfo(cameraId, info);
+ * orientation = (orientation + 45) / 90 * 90;
+ * mParameters.setRotation((orientation + info.mOrientation) % 360);
+ * }
+ *
+ * @param rotation The rotation angle in degrees relative to the
+ * orientation of the camera. Rotation can only be 0,
+ * 90, 180 or 270.
* @throws IllegalArgumentException if rotation value is invalid.
* @see android.view.OrientationEventListener
+ * @see #getCameraInfo(int, CameraInfo)
*/
public void setRotation(int rotation) {
if (rotation == 0 || rotation == 90 || rotation == 180
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 3df8ec0..ad7289d 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -45,7 +45,7 @@
public class MobileDataStateTracker implements NetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private Phone.DataState mMobileDataState;
private ITelephony mPhoneService;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index e56e257..6d19f41 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -68,6 +68,7 @@
private static File RECOVERY_DIR = new File("/cache/recovery");
private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
private static File LOG_FILE = new File(RECOVERY_DIR, "log");
+ private static String LAST_LOG_FILENAME = "last_log";
// Length limits for reading files.
private static int LOG_FILE_MAX_LENGTH = 64 * 1024;
@@ -399,9 +400,10 @@
Log.e(TAG, "Error reading recovery log", e);
}
- // Delete everything in RECOVERY_DIR
+ // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME
String[] names = RECOVERY_DIR.list();
for (int i = 0; names != null && i < names.length; i++) {
+ if (names[i].equals(LAST_LOG_FILENAME)) continue;
File f = new File(RECOVERY_DIR, names[i]);
if (!f.delete()) {
Log.e(TAG, "Can't delete: " + f);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3ddaad9..9494a06 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -30,8 +30,9 @@
import java.util.HashMap;
/**
- * <p>StrictMode is a developer tool which lets you impose stricter
- * rules under which your application runs.
+ * <p>StrictMode is a developer tool which detects things you might be
+ * doing by accident and brings them to your attention so you can fix
+ * them.
*
* <p>StrictMode is most commonly used to catch accidental disk or
* network access on the application's main thread, where UI
@@ -55,24 +56,33 @@
* <pre>
* public void onCreate() {
* if (DEVELOPER_MODE) {
- * StrictMode.setThreadPolicy(StrictMode.DISALLOW_DISK_WRITE |
- * StrictMode.DISALLOW_DISK_READ |
- * StrictMode.DISALLOW_NETWORK |
- * StrictMode.PENALTY_LOG);
+ * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ * .detectDiskReads()
+ * .detectDiskWrites()
+ * .detectNetwork() // or .detectAll() for all detectable problems
+ * .penaltyLog()
+ * .build());
+ * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ * .detectLeakedSqlLiteCursors()
+ * .penaltyLog()
+ * .penaltyDeath()
+ * .build());
* }
* super.onCreate();
* }
* </pre>
*
- * <p>Then you can watch the output of <code>adb logcat</code> while you
- * use your application.
+ * <p>You can decide what should happen when a violation is detected.
+ * For example, using {@link ThreadPolicy.Builder#penaltyLog} you can
+ * watch the output of <code>adb logcat</code> while you use your
+ * application to see the violations as they happen.
*
* <p>If you find violations that you feel are problematic, there are
* a variety of tools to help solve them: threads, {@link android.os.Handler},
* {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc.
* But don't feel compelled to fix everything that StrictMode finds. In particular,
- * a lot of disk accesses are often necessary during the normal activity lifecycle. Use
- * StrictMode to find things you did on accident. Network requests on the UI thread
+ * many cases of disk access are often necessary during the normal activity lifecycle. Use
+ * StrictMode to find things you did by accident. Network requests on the UI thread
* are almost always a problem, though.
*
* <p class="note">StrictMode is not a security mechanism and is not
@@ -94,55 +104,50 @@
// Only show an annoying dialog at most every 30 seconds
private static final long MIN_DIALOG_INTERVAL_MS = 30000;
- private StrictMode() {}
+ // Thread-policy:
/**
- * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
- * thread to write to disk.
+ * @hide
*/
- public static final int DISALLOW_DISK_WRITE = 0x01;
+ public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
/**
- * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
- * thread to read from disk.
+ * @hide
*/
- public static final int DISALLOW_DISK_READ = 0x02;
+ public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
/**
- * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
- * thread to access the network.
+ * @hide
*/
- public static final int DISALLOW_NETWORK = 0x04;
+ public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
- /** @hide */
- public static final int DISALLOW_MASK =
- DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
+ // Process-policy:
/**
- * Penalty flag for {@link #setThreadPolicy} to log violations to
- * the system log, visible with <code>adb logcat</code>.
+ * Note, a "VM_" bit, not thread.
+ * @hide
+ */
+ public static final int DETECT_VM_CURSOR_LEAKS = 0x200; // for ProcessPolicy
+
+ /**
+ * @hide
*/
public static final int PENALTY_LOG = 0x10; // normal android.util.Log
+ // Used for both process and thread policy:
+
/**
- * Penalty flag for {@link #setThreadPolicy} to show an annoying
- * dialog to the developer, rate-limited to be only a little
- * annoying.
+ * @hide
*/
public static final int PENALTY_DIALOG = 0x20;
/**
- * Penalty flag for {@link #setThreadPolicy} to crash hard if
- * policy is violated.
+ * @hide
*/
public static final int PENALTY_DEATH = 0x40;
/**
- * Penalty flag for {@link #setThreadPolicy} to log a stacktrace
- * and timing data to the
- * {@link android.os.DropBoxManager DropBox} on policy violation.
- * Intended mostly for platform integrators doing beta user field
- * data collection.
+ * @hide
*/
public static final int PENALTY_DROPBOX = 0x80;
@@ -159,10 +164,321 @@
*/
public static final int PENALTY_GATHER = 0x100;
- /** @hide */
- public static final int PENALTY_MASK =
- PENALTY_LOG | PENALTY_DIALOG |
- PENALTY_DROPBOX | PENALTY_DEATH;
+ /**
+ * The current VmPolicy in effect.
+ */
+ private static volatile int sVmPolicyMask = 0;
+
+ private StrictMode() {}
+
+ /**
+ * {@link StrictMode} policy applied to a certain thread.
+ *
+ * <p>The policy is enabled by {@link #setThreadPolicy}. The current policy
+ * can be retrieved with {@link #getThreadPolicy}.
+ *
+ * <p>Note that multiple penalties may be provided and they're run
+ * in order from least to most severe (logging before process
+ * death, for example). There's currently no mechanism to choose
+ * different penalties for different detected actions.
+ */
+ public static final class ThreadPolicy {
+ /**
+ * The default, lax policy which doesn't catch anything.
+ */
+ public static final ThreadPolicy LAX = new ThreadPolicy(0);
+
+ final int mask;
+
+ private ThreadPolicy(int mask) {
+ this.mask = mask;
+ }
+
+ @Override
+ public String toString() {
+ return "[StrictMode.ThreadPolicy; mask=" + mask + "]";
+ }
+
+ /**
+ * Creates ThreadPolicy instances. Methods whose names start
+ * with {@code detect} specify what problems we should look
+ * for. Methods whose names start with {@code penalty} specify what
+ * we should do when we detect a problem.
+ *
+ * <p>You can call as many {@code detect} and {@code penalty}
+ * methods as you like. Currently order is insignificant: all
+ * penalties apply to all detected problems.
+ *
+ * <p>For example, detect everything and log anything that's found:
+ * <pre>
+ * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
+ * .detectAll()
+ * .penaltyLog()
+ * .build();
+ * StrictMode.setVmPolicy(policy);
+ * </pre>
+ */
+ public static final class Builder {
+ private int mMask = 0;
+
+ /**
+ * Create a Builder that detects nothing and has no
+ * violations. (but note that {@link #build} will default
+ * to enabling {@link #penaltyLog} if no other penalties
+ * are specified)
+ */
+ public Builder() {
+ mMask = 0;
+ }
+
+ /**
+ * Initialize a Builder from an existing ThreadPolicy.
+ */
+ public Builder(ThreadPolicy policy) {
+ mMask = policy.mask;
+ }
+
+ /**
+ * Detect everything that's potentially suspect.
+ *
+ * <p>As of the Gingerbread release this includes network and
+ * disk operations but will likely expand in future releases.
+ */
+ public Builder detectAll() {
+ return enable(DETECT_DISK_WRITE | DETECT_DISK_READ | DETECT_NETWORK);
+ }
+
+ /**
+ * Disable the detection of everything.
+ */
+ public Builder permitAll() {
+ return disable(DETECT_DISK_WRITE | DETECT_DISK_READ | DETECT_NETWORK);
+ }
+
+ /**
+ * Enable detection of network operations.
+ */
+ public Builder detectNetwork() {
+ return enable(DETECT_NETWORK);
+ }
+
+ /**
+ * Disable detection of network operations.
+ */
+ public Builder permitNetwork() {
+ return disable(DETECT_NETWORK);
+ }
+
+ /**
+ * Enable detection of disk reads.
+ */
+ public Builder detectDiskReads() {
+ return enable(DETECT_DISK_READ);
+ }
+
+ /**
+ * Disable detection of disk reads.
+ */
+ public Builder permitDiskReads() {
+ return disable(DETECT_DISK_READ);
+ }
+
+ /**
+ * Enable detection of disk writes.
+ */
+ public Builder detectDiskWrites() {
+ return enable(DETECT_DISK_WRITE);
+ }
+
+ /**
+ * Disable detection of disk writes.
+ */
+ public Builder permitDiskWrites() {
+ return disable(DETECT_DISK_WRITE);
+ }
+
+ /**
+ * Show an annoying dialog to the developer on detected
+ * violations, rate-limited to be only a little annoying.
+ */
+ public Builder penaltyDialog() {
+ return enable(PENALTY_DIALOG);
+ }
+
+ /**
+ * Crash the whole process on violation. This penalty runs at
+ * the end of all enabled penalties so you'll still get
+ * see logging or other violations before the process dies.
+ */
+ public Builder penaltyDeath() {
+ return enable(PENALTY_DEATH);
+ }
+
+ /**
+ * Log detected violations to the system log.
+ */
+ public Builder penaltyLog() {
+ return enable(PENALTY_LOG);
+ }
+
+ /**
+ * Enable detected violations log a stacktrace and timing data
+ * to the {@link android.os.DropBoxManager DropBox} on policy
+ * violation. Intended mostly for platform integrators doing
+ * beta user field data collection.
+ */
+ public Builder penaltyDropBox() {
+ return enable(PENALTY_DROPBOX);
+ }
+
+ private Builder enable(int bit) {
+ mMask |= bit;
+ return this;
+ }
+
+ private Builder disable(int bit) {
+ mMask &= ~bit;
+ return this;
+ }
+
+ /**
+ * Construct the ThreadPolicy instance.
+ *
+ * <p>Note: if no penalties are enabled before calling
+ * <code>build</code>, {@link #penaltyLog} is implicitly
+ * set.
+ */
+ public ThreadPolicy build() {
+ // If there are detection bits set but no violation bits
+ // set, enable simple logging.
+ if (mMask != 0 &&
+ (mMask & (PENALTY_DEATH | PENALTY_LOG |
+ PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+ penaltyLog();
+ }
+ return new ThreadPolicy(mMask);
+ }
+ }
+ }
+
+ /**
+ * {@link StrictMode} policy applied to all threads in the virtual machine's process.
+ *
+ * <p>The policy is enabled by {@link #setVmPolicy}.
+ */
+ public static final class VmPolicy {
+ /**
+ * The default, lax policy which doesn't catch anything.
+ */
+ public static final VmPolicy LAX = new VmPolicy(0);
+
+ final int mask;
+
+ private VmPolicy(int mask) {
+ this.mask = mask;
+ }
+
+ @Override
+ public String toString() {
+ return "[StrictMode.VmPolicy; mask=" + mask + "]";
+ }
+
+ /**
+ * Creates {@link VmPolicy} instances. Methods whose names start
+ * with {@code detect} specify what problems we should look
+ * for. Methods whose names start with {@code penalty} specify what
+ * we should do when we detect a problem.
+ *
+ * <p>You can call as many {@code detect} and {@code penalty}
+ * methods as you like. Currently order is insignificant: all
+ * penalties apply to all detected problems.
+ *
+ * <p>For example, detect everything and log anything that's found:
+ * <pre>
+ * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
+ * .detectAll()
+ * .penaltyLog()
+ * .build();
+ * StrictMode.setVmPolicy(policy);
+ * </pre>
+ */
+ public static final class Builder {
+ private int mMask;
+
+ /**
+ * Detect everything that's potentially suspect.
+ *
+ * <p>As of the Gingerbread release this only includes
+ * SQLite cursor leaks but will likely expand in future
+ * releases.
+ */
+ public Builder detectAll() {
+ return enable(DETECT_VM_CURSOR_LEAKS);
+ }
+
+ /**
+ * Detect when an
+ * {@link android.database.sqlite.SQLiteCursor} or other
+ * SQLite object is finalized without having been closed.
+ *
+ * <p>You always want to explicitly close your SQLite
+ * cursors to avoid unnecessary database contention and
+ * temporary memory leaks.
+ */
+ public Builder detectLeakedSqlLiteObjects() {
+ return enable(DETECT_VM_CURSOR_LEAKS);
+ }
+
+ /**
+ * Crashes the whole process on violation. This penalty runs at
+ * the end of all enabled penalties so yo you'll still get
+ * your logging or other violations before the process dies.
+ */
+ public Builder penaltyDeath() {
+ return enable(PENALTY_DEATH);
+ }
+
+ /**
+ * Log detected violations to the system log.
+ */
+ public Builder penaltyLog() {
+ return enable(PENALTY_LOG);
+ }
+
+ /**
+ * Enable detected violations log a stacktrace and timing data
+ * to the {@link android.os.DropBoxManager DropBox} on policy
+ * violation. Intended mostly for platform integrators doing
+ * beta user field data collection.
+ */
+ public Builder penaltyDropBox() {
+ return enable(PENALTY_DROPBOX);
+ }
+
+ private Builder enable(int bit) {
+ mMask |= bit;
+ return this;
+ }
+
+ /**
+ * Construct the VmPolicy instance.
+ *
+ * <p>Note: if no penalties are enabled before calling
+ * <code>build</code>, {@link #penaltyLog} is implicitly
+ * set.
+ */
+ public VmPolicy build() {
+ // If there are detection bits set but no violation bits
+ // set, enable simple logging.
+ if (mMask != 0 &&
+ (mMask & (PENALTY_DEATH | PENALTY_LOG |
+ PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+ penaltyLog();
+ }
+ return new VmPolicy(mMask);
+ }
+ }
+ }
/**
* Log of strict mode violation stack traces that have occurred
@@ -181,19 +497,21 @@
};
/**
- * Sets the policy for what actions the current thread isn't
- * expected to do, as well as the penalty if it does.
+ * Sets the policy for what actions on the current thread should
+ * be detected, as well as the penalty if such actions occur.
*
- * <p>Internally this sets a thread-local integer which is
+ * <p>Internally this sets a thread-local variable which is
* propagated across cross-process IPC calls, meaning you can
* catch violations when a system service or another process
* accesses the disk or network on your behalf.
*
- * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values,
- * e.g. {@link #DISALLOW_DISK_READ}, {@link #DISALLOW_DISK_WRITE},
- * {@link #DISALLOW_NETWORK}, {@link #PENALTY_LOG}.
+ * @param policy the policy to put into place
*/
- public static void setThreadPolicy(final int policyMask) {
+ public static void setThreadPolicy(final ThreadPolicy policy) {
+ setThreadPolicyMask(policy.mask);
+ }
+
+ private static void setThreadPolicyMask(final int policyMask) {
// In addition to the Java-level thread-local in Dalvik's
// BlockGuard, we also need to keep a native thread-local in
// Binder in order to propagate the value across Binder calls,
@@ -222,65 +540,76 @@
private static class StrictModeNetworkViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeNetworkViolation(int policyMask) {
- super(policyMask, DISALLOW_NETWORK);
+ super(policyMask, DETECT_NETWORK);
}
}
private static class StrictModeDiskReadViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeDiskReadViolation(int policyMask) {
- super(policyMask, DISALLOW_DISK_READ);
+ super(policyMask, DETECT_DISK_READ);
}
}
private static class StrictModeDiskWriteViolation extends BlockGuard.BlockGuardPolicyException {
public StrictModeDiskWriteViolation(int policyMask) {
- super(policyMask, DISALLOW_DISK_WRITE);
+ super(policyMask, DETECT_DISK_WRITE);
}
}
/**
* Returns the bitmask of the current thread's policy.
*
- * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
+ * @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
+ *
+ * @hide
*/
- public static int getThreadPolicy() {
+ public static int getThreadPolicyMask() {
return BlockGuard.getThreadPolicy().getPolicyMask();
}
/**
- * A convenience wrapper around {@link #getThreadPolicy} and
- * {@link #setThreadPolicy}. Updates the current thread's policy
- * mask to allow both reading & writing to disk, returning the
- * old policy so you can restore it at the end of a block.
- *
- * @return the old policy mask, to be passed to setThreadPolicy to
- * restore the policy.
+ * Returns the current thread's policy.
*/
- public static int allowThreadDiskWrites() {
- int oldPolicy = getThreadPolicy();
- int newPolicy = oldPolicy & ~(DISALLOW_DISK_WRITE | DISALLOW_DISK_READ);
- if (newPolicy != oldPolicy) {
- setThreadPolicy(newPolicy);
- }
- return oldPolicy;
+ public static ThreadPolicy getThreadPolicy() {
+ return new ThreadPolicy(getThreadPolicyMask());
}
/**
- * A convenience wrapper around {@link #getThreadPolicy} and
- * {@link #setThreadPolicy}. Updates the current thread's policy
- * mask to allow reading from disk, returning the old
- * policy so you can restore it at the end of a block.
+ * A convenience wrapper that takes the current
+ * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
+ * to permit both disk reads & writes, and sets the new policy
+ * with {@link #setThreadPolicy}, returning the old policy so you
+ * can restore it at the end of a block.
*
- * @return the old policy mask, to be passed to setThreadPolicy to
+ * @return the old policy, to be passed to {@link #setThreadPolicy} to
+ * restore the policy at the end of a block
+ */
+ public static ThreadPolicy allowThreadDiskWrites() {
+ int oldPolicyMask = getThreadPolicyMask();
+ int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_WRITE | DETECT_DISK_READ);
+ if (newPolicyMask != oldPolicyMask) {
+ setThreadPolicyMask(newPolicyMask);
+ }
+ return new ThreadPolicy(oldPolicyMask);
+ }
+
+ /**
+ * A convenience wrapper that takes the current
+ * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
+ * to permit disk reads, and sets the new policy
+ * with {@link #setThreadPolicy}, returning the old policy so you
+ * can restore it at the end of a block.
+ *
+ * @return the old policy, to be passed to setThreadPolicy to
* restore the policy.
*/
- public static int allowThreadDiskReads() {
- int oldPolicy = getThreadPolicy();
- int newPolicy = oldPolicy & ~(DISALLOW_DISK_READ);
- if (newPolicy != oldPolicy) {
- setThreadPolicy(newPolicy);
+ public static ThreadPolicy allowThreadDiskReads() {
+ int oldPolicyMask = getThreadPolicyMask();
+ int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_READ);
+ if (newPolicyMask != oldPolicyMask) {
+ setThreadPolicyMask(newPolicyMask);
}
- return oldPolicy;
+ return new ThreadPolicy(oldPolicyMask);
}
/**
@@ -294,11 +623,14 @@
if ("user".equals(Build.TYPE)) {
return false;
}
- StrictMode.setThreadPolicy(
- StrictMode.DISALLOW_DISK_WRITE |
- StrictMode.DISALLOW_DISK_READ |
- StrictMode.DISALLOW_NETWORK |
+ StrictMode.setThreadPolicyMask(
+ StrictMode.DETECT_DISK_WRITE |
+ StrictMode.DETECT_DISK_READ |
+ StrictMode.DETECT_NETWORK |
StrictMode.PENALTY_DROPBOX);
+ sVmPolicyMask = StrictMode.DETECT_VM_CURSOR_LEAKS |
+ StrictMode.PENALTY_DROPBOX |
+ StrictMode.PENALTY_LOG;
return true;
}
@@ -372,7 +704,7 @@
// Part of BlockGuard.Policy interface:
public void onWriteToDisk() {
- if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
+ if ((mPolicyMask & DETECT_DISK_WRITE) == 0) {
return;
}
BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask);
@@ -382,7 +714,7 @@
// Part of BlockGuard.Policy interface:
public void onReadFromDisk() {
- if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
+ if ((mPolicyMask & DETECT_DISK_READ) == 0) {
return;
}
BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
@@ -392,7 +724,7 @@
// Part of BlockGuard.Policy interface:
public void onNetwork() {
- if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
+ if ((mPolicyMask & DETECT_NETWORK) == 0) {
return;
}
BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask);
@@ -547,13 +879,13 @@
if (violationMaskSubset != 0) {
int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
violationMaskSubset |= violationBit;
- final int savedPolicy = getThreadPolicy();
+ final int savedPolicyMask = getThreadPolicyMask();
try {
// First, remove any policy before we call into the Activity Manager,
// otherwise we'll infinite recurse as we try to log policy violations
// to disk, thus violating policy, thus requiring logging, etc...
// We restore the current policy below, in the finally block.
- setThreadPolicy(0);
+ setThreadPolicyMask(0);
ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
RuntimeInit.getApplicationObject(),
@@ -563,7 +895,7 @@
Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
} finally {
// Restore the policy.
- setThreadPolicy(savedPolicy);
+ setThreadPolicyMask(savedPolicyMask);
}
}
@@ -592,6 +924,74 @@
}
/**
+ * Sets the policy for what actions in the VM process (on any
+ * thread) should be detected, as well as the penalty if such
+ * actions occur.
+ *
+ * @param policy the policy to put into place
+ */
+ public static void setVmPolicy(final VmPolicy policy) {
+ sVmPolicyMask = policy.mask;
+ }
+
+ /**
+ * Gets the current VM policy.
+ */
+ public static VmPolicy getVmPolicy() {
+ return new VmPolicy(sVmPolicyMask);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean vmSqliteObjectLeaksEnabled() {
+ return (sVmPolicyMask & DETECT_VM_CURSOR_LEAKS) != 0;
+ }
+
+ /**
+ * @hide
+ */
+ public static void onSqliteObjectLeaked(String message, Throwable originStack) {
+ if ((sVmPolicyMask & PENALTY_LOG) != 0) {
+ Log.e(TAG, message, originStack);
+ }
+
+ if ((sVmPolicyMask & PENALTY_DROPBOX) != 0) {
+ final ViolationInfo info = new ViolationInfo(originStack, sVmPolicyMask);
+
+ // The violationMask, passed to ActivityManager, is a
+ // subset of the original StrictMode policy bitmask, with
+ // only the bit violated and penalty bits to be executed
+ // by the ActivityManagerService remaining set.
+ int violationMaskSubset = PENALTY_DROPBOX | DETECT_VM_CURSOR_LEAKS;
+ final int savedPolicyMask = getThreadPolicyMask();
+ try {
+ // First, remove any policy before we call into the Activity Manager,
+ // otherwise we'll infinite recurse as we try to log policy violations
+ // to disk, thus violating policy, thus requiring logging, etc...
+ // We restore the current policy below, in the finally block.
+ setThreadPolicyMask(0);
+
+ ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(),
+ violationMaskSubset,
+ info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
+ } finally {
+ // Restore the policy.
+ setThreadPolicyMask(savedPolicyMask);
+ }
+ }
+
+ if ((sVmPolicyMask & PENALTY_DEATH) != 0) {
+ System.err.println("StrictMode VmPolicy violation with POLICY_DEATH; shutting down.");
+ Process.killProcess(Process.myPid());
+ System.exit(10);
+ }
+ }
+
+ /**
* Called from Parcel.writeNoException()
*/
/* package */ static void writeGatheredViolationsToParcel(Parcel p) {
@@ -621,7 +1021,7 @@
new LogStackTrace().printStackTrace(new PrintWriter(sw));
String ourStack = sw.toString();
- int policyMask = getThreadPolicy();
+ int policyMask = getThreadPolicyMask();
boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
int numViolations = p.readInt();
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4268618..8554ece 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -424,7 +424,6 @@
* @param key secret used to encrypt the OBB; may be <code>null</code> if no
* encryption was used on the OBB.
* @return whether the mount call was successfully queued or not
- * @throws IllegalArgumentException when the OBB is already mounted
*/
public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
try {
@@ -458,10 +457,8 @@
* @param force whether to kill any programs using this in order to unmount
* it
* @return whether the unmount call was successfully queued or not
- * @throws IllegalArgumentException when OBB is not already mounted
*/
- public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
- throws IllegalArgumentException {
+ public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
try {
mObbActionListener.addListener(listener);
mMountService.unmountObb(filename, force, mObbActionListener);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 1e358c9..8fd0e0a 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -378,16 +378,6 @@
}
/**
- * 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);
- }
-
- /**
* Returns whether the status is a success (i.e. 2xx).
* @hide
*/
@@ -435,24 +425,12 @@
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;
-
- /**
* This download has successfully completed.
* Warning: there might be other status values that indicate success
* in the future.
@@ -980,15 +958,6 @@
}
/**
- * 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) {
@@ -1030,19 +999,30 @@
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
+ * This download has been paused by the owning app.
*/
- public static final int STATUS_RUNNING_PAUSED = 193;
+ public static final int STATUS_PAUSED_BY_APP = 193;
+
+ /**
+ * This download encountered some network error and is waiting before retrying the request.
+ */
+ public static final int STATUS_WAITING_TO_RETRY = 194;
+
+ /**
+ * This download is waiting for network connectivity to proceed.
+ */
+ public static final int STATUS_WAITING_FOR_NETWORK = 195;
+
+ /**
+ * This download exceeded a size limit for mobile networks and is waiting for a Wi-Fi
+ * connection to proceed.
+ */
+ public static final int STATUS_QUEUED_FOR_WIFI = 196;
/**
* This download has successfully completed.
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 05cbeff..4edc01f 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -39,7 +39,7 @@
/**
* TODO: Move this to
* java/services/com/android/server/BluetoothEventLoop.java
- * and make the contructor package private again.
+ * and make the constructor package private again.
*
* @hide
*/
@@ -252,10 +252,10 @@
}
String name = propValues[0];
if (name.equals("Name")) {
+ mBluetoothService.setProperty(name, propValues[1]);
Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Pairable") || name.equals("Discoverable")) {
String pairable = name.equals("Pairable") ? propValues[1] :
mBluetoothService.getPropertyInternal("Pairable");
@@ -266,6 +266,7 @@
if (pairable == null || discoverable == null)
return;
+ mBluetoothService.setProperty(name, propValues[1]);
int mode = BluetoothService.bluezStringToScanMode(
pairable.equals("true"),
discoverable.equals("true"));
@@ -275,9 +276,9 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
- mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Discovering")) {
Intent intent;
+ mBluetoothService.setProperty(name, propValues[1]);
if (propValues[1].equals("true")) {
mBluetoothService.setIsDiscovering(true);
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
@@ -288,7 +289,6 @@
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
}
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mBluetoothService.setProperty(name, propValues[1]);
} else if (name.equals("Devices")) {
String value = null;
int len = Integer.valueOf(propValues[1]);
@@ -322,19 +322,20 @@
}
BluetoothDevice device = mAdapter.getRemoteDevice(address);
if (name.equals("Name")) {
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("Class")) {
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothDevice.EXTRA_CLASS,
new BluetoothClass(Integer.valueOf(propValues[1])));
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("Connected")) {
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
Intent intent = null;
if (propValues[1].equals("true")) {
intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
@@ -348,7 +349,6 @@
}
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
} else if (name.equals("UUIDs")) {
String uuid = null;
int len = Integer.valueOf(propValues[1]);
@@ -754,7 +754,7 @@
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
- Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
+ Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " +
"restarting Bluetooth ***");
mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index bd105a7..f8caa2c 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -500,7 +500,7 @@
// forked multiple times in a row, probably because there is
// some race in sdptool or bluez when operated in parallel.
// As a workaround, delay 500ms between each fork of sdptool.
- // TODO: Don't fork sdptool in order to regsiter service
+ // TODO: Don't fork sdptool in order to register service
// records, use a DBUS call instead.
switch (msg.arg1) {
case 1:
@@ -713,7 +713,7 @@
/** local cache of bonding state.
/* we keep our own state to track the intermediate state BONDING, which
/* bluez does not track.
- * All addreses must be passed in upper case.
+ * All addresses must be passed in upper case.
*/
public class BondState {
private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
@@ -976,7 +976,7 @@
}
}
- // This function adds a bluetooth address to the auto pairing blacklis
+ // This function adds a bluetooth address to the auto pairing blacklist
// file. These addresses are added to DynamicAddressBlacklistSection
private void updateAutoPairingData(String address) {
BufferedWriter out = null;
@@ -1106,7 +1106,7 @@
* a device discoverable; you need to call setMode() to make the device
* explicitly discoverable.
*
- * @param timeout_s The discoverable timeout in seconds.
+ * @param timeout The discoverable timeout in seconds.
*/
public synchronized boolean setDiscoverableTimeout(int timeout) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -2565,12 +2565,12 @@
/*package*/ String getAddressFromObjectPath(String objectPath) {
String adapterObjectPath = getPropertyInternal("ObjectPath");
if (adapterObjectPath == null || objectPath == null) {
- Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+ Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
" or deviceObjectPath:" + objectPath + " is null");
return null;
}
if (!objectPath.startsWith(adapterObjectPath)) {
- Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+ Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
" is not a prefix of deviceObjectPath:" + objectPath +
"bluetoothd crashed ?");
return null;
diff --git a/core/java/android/util/CalendarUtils.java b/core/java/android/util/CalendarUtils.java
index 3d340d9..1b2a894 100644
--- a/core/java/android/util/CalendarUtils.java
+++ b/core/java/android/util/CalendarUtils.java
@@ -146,6 +146,9 @@
* This formats a date/time range using Calendar's time zone and the
* local conventions for the region of the device.
*
+ * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in
+ * the UTC time zone instead.
+ *
* @param context the context is required only if the time is shown
* @param startMillis the start time in UTC milliseconds
* @param endMillis the end time in UTC milliseconds
@@ -156,10 +159,16 @@
public String formatDateRange(Context context, long startMillis,
long endMillis, int flags) {
String date;
+ String tz;
+ if ((flags & DateUtils.FORMAT_UTC) != 0) {
+ tz = Time.TIMEZONE_UTC;
+ } else {
+ tz = getTimeZone(context, null);
+ }
synchronized (mSB) {
mSB.setLength(0);
date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags,
- getTimeZone(context, null)).toString();
+ tz).toString();
}
return date;
}
diff --git a/core/java/android/view/DragEvent.aidl b/core/java/android/view/DragEvent.aidl
new file mode 100644
index 0000000..f08943f
--- /dev/null
+++ b/core/java/android/view/DragEvent.aidl
@@ -0,0 +1,19 @@
+/*
+** 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.view;
+
+parcelable DragEvent;
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
new file mode 100644
index 0000000..47c6d3c
--- /dev/null
+++ b/core/java/android/view/DragEvent.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.view;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** !!! TODO: real docs */
+public class DragEvent implements Parcelable {
+ private static final boolean TRACK_RECYCLED_LOCATION = false;
+
+ int mAction;
+ float mX, mY;
+ ClipDescription mClipDescription;
+ ClipData mClipData;
+
+ private DragEvent mNext;
+ private RuntimeException mRecycledLocation;
+ private boolean mRecycled;
+
+ private static final int MAX_RECYCLED = 10;
+ private static final Object gRecyclerLock = new Object();
+ private static int gRecyclerUsed = 0;
+ private static DragEvent gRecyclerTop = null;
+
+ /**
+ * action constants for DragEvent dispatch
+ */
+ public static final int ACTION_DRAG_STARTED = 1;
+ public static final int ACTION_DRAG_LOCATION = 2;
+ public static final int ACTION_DROP = 3;
+ public static final int ACTION_DRAG_ENDED = 4;
+ public static final int ACTION_DRAG_ENTERED = 5;
+ public static final int ACTION_DRAG_EXITED = 6;
+
+ /* hide the constructor behind package scope */
+ DragEvent() {
+ }
+
+ public static DragEvent obtain() {
+ return DragEvent.obtain(0, 0f, 0f, null, null);
+ }
+
+ public static DragEvent obtain(int action, float x, float y,
+ ClipDescription description, ClipData data) {
+ final DragEvent ev;
+ synchronized (gRecyclerLock) {
+ if (gRecyclerTop == null) {
+ return new DragEvent();
+ }
+ ev = gRecyclerTop;
+ gRecyclerTop = ev.mNext;
+ gRecyclerUsed -= 1;
+ }
+ ev.mRecycledLocation = null;
+ ev.mRecycled = false;
+ ev.mNext = null;
+
+ ev.mAction = action;
+ ev.mX = x;
+ ev.mY = y;
+ ev.mClipDescription = description;
+ ev.mClipData = data;
+
+ return ev;
+ }
+
+ public int getAction() {
+ return mAction;
+ }
+
+ public float getX() {
+ return mX;
+ }
+
+ public float getY() {
+ return mY;
+ }
+
+ public ClipData getClipData() {
+ return mClipData;
+ }
+
+ public ClipDescription getClipDescription() {
+ return mClipDescription;
+ }
+
+ /**
+ * Recycle the DragEvent, to be re-used by a later caller. After calling
+ * this function you must never touch the event again.
+ */
+ public final void recycle() {
+ // Ensure recycle is only called once!
+ if (TRACK_RECYCLED_LOCATION) {
+ if (mRecycledLocation != null) {
+ throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+ }
+ mRecycledLocation = new RuntimeException("Last recycled here");
+ } else {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
+ }
+
+ mClipData = null;
+ mClipDescription = null;
+
+ synchronized (gRecyclerLock) {
+ if (gRecyclerUsed < MAX_RECYCLED) {
+ gRecyclerUsed++;
+ mNext = gRecyclerTop;
+ gRecyclerTop = this;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
+ + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
+ + " data=" + mClipData
+ + "}";
+ }
+
+ /* Parcelable interface */
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAction);
+ dest.writeFloat(mX);
+ dest.writeFloat(mY);
+ if (mClipData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mClipData.writeToParcel(dest, flags);
+ }
+ if (mClipDescription == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mClipDescription.writeToParcel(dest, flags);
+ }
+ }
+
+ public static final Parcelable.Creator<DragEvent> CREATOR =
+ new Parcelable.Creator<DragEvent>() {
+ public DragEvent createFromParcel(Parcel in) {
+ DragEvent event = DragEvent.obtain();
+ event.mAction = in.readInt();
+ event.mX = in.readFloat();
+ event.mY = in.readFloat();
+ if (in.readInt() != 0) {
+ event.mClipData = ClipData.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() != 0) {
+ event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
+ }
+ return event;
+ }
+
+ public DragEvent[] newArray(int size) {
+ return new DragEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 921018a..6b9cda0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -17,10 +17,13 @@
package android.view;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -64,4 +67,9 @@
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras, boolean sync);
+
+ /**
+ * Drag/drop events
+ */
+ void dispatchDragEvent(in DragEvent event);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7f10b76..79ea5b6 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -17,6 +17,7 @@
package android.view;
+import android.content.ClipData;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
@@ -116,6 +117,30 @@
boolean performHapticFeedback(IWindow window, int effectId, boolean always);
/**
+ * Allocate the drag's thumbnail surface. Also assigns a token that identifies
+ * the drag to the OS and passes that as the return value. A return value of
+ * null indicates failure.
+ */
+ IBinder prepareDrag(IWindow window, boolean localOnly,
+ int thumbnailWidth, int thumbnailHeight, out Surface outSurface);
+
+ /**
+ * Initiate the drag operation itself
+ */
+ boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY,
+ float thumbCenterX, float thumbCenterY, in ClipData data);
+
+ /**
+ * Tell the OS that we've just dragged into a View that is willing to accept the drop
+ */
+ void dragRecipientEntered(IWindow window);
+
+ /**
+ * Tell the OS that we've just dragged *off* of a View that was willing to accept the drop
+ */
+ void dragRecipientExited(IWindow window);
+
+ /**
* For windows with the wallpaper behind them, and the wallpaper is
* larger than the screen, set the offset within the screen.
* For multi screen launcher type applications, xstep and ystep indicate
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6705596..dfbe65c 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Matrix;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -347,6 +348,8 @@
private RuntimeException mRecycledLocation;
private boolean mRecycled;
+ private native void nativeTransform(Matrix matrix);
+
private MotionEvent(int pointerCount, int sampleCount) {
mPointerIdentifiers = new int[pointerCount];
mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
@@ -1413,6 +1416,19 @@
mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
}
+ /**
+ * Applies a transformation matrix to all of the points in the event.
+ *
+ * @param matrix The transformation matrix to apply.
+ */
+ public final void transform(Matrix matrix) {
+ if (matrix == null) {
+ throw new IllegalArgumentException("matrix must not be null");
+ }
+
+ nativeTransform(matrix);
+ }
+
private final void getPointerCoordsAtSampleIndex(int sampleIndex,
PointerCoords outPointerCoords) {
final float[] dataSamples = mDataSamples;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3b10437..011c3df 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import android.content.ClipData;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -614,6 +615,7 @@
*/
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
private static final boolean DBG = false;
+ static final boolean DEBUG_DRAG = true;
/**
* The logging tag used by this class with android.util.Log.
@@ -1681,7 +1683,7 @@
* The transform matrix for the View. This transform is calculated internally
* based on the rotation, scaleX, and scaleY properties. The identity matrix
* is used by default. Do *not* use this variable directly; instead call
- * getMatrix(), which will automatically recalculate the matrix if necessary
+ * getInverseMatrix(), which will automatically recalculate the matrix if necessary
* to get the correct matrix based on the latest rotation and scale properties.
*/
private Matrix mInverseMatrix;
@@ -1704,7 +1706,8 @@
* A variable that tracks whether we need to recalculate the
* transform matrix, based on whether the rotation or scaleX/Y properties
* have changed since the matrix was last calculated. This variable
- * is only valid after a call to getMatrix().
+ * is only valid after a call to updateMatrix() or to a function that
+ * calls it such as getMatrix(), hasIdentityMatrix() and getInverseMatrix().
*/
private boolean mMatrixIsIdentity = true;
@@ -2030,6 +2033,14 @@
private int mTouchSlop;
/**
+ * Cache drag/drop state
+ *
+ */
+ boolean mCanAcceptDrop;
+ private int mThumbnailWidth;
+ private int mThumbnailHeight;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -5108,7 +5119,7 @@
* @return The current transform matrix for the view
*/
public Matrix getMatrix() {
- hasIdentityMatrix();
+ updateMatrix();
return mMatrix;
}
@@ -5123,11 +5134,20 @@
}
/**
- * Recomputes the transform matrix if necessary.
+ * Returns true if the transform matrix is the identity matrix.
+ * Recomputes the matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
*/
- boolean hasIdentityMatrix() {
+ final boolean hasIdentityMatrix() {
+ updateMatrix();
+ return mMatrixIsIdentity;
+ }
+
+ /**
+ * Recomputes the transform matrix if necessary.
+ */
+ private final void updateMatrix() {
if (mMatrixDirty) {
// transform-related properties have changed since the last time someone
// asked for the matrix; recalculate it with the current values
@@ -5166,7 +5186,6 @@
mMatrixIsIdentity = mMatrix.isIdentity();
mInverseMatrixDirty = true;
}
- return mMatrixIsIdentity;
}
/**
@@ -5176,7 +5195,8 @@
*
* @return The inverse of the current matrix of this view.
*/
- Matrix getInverseMatrix() {
+ final Matrix getInverseMatrix() {
+ updateMatrix();
if (mInverseMatrixDirty) {
if (mInverseMatrix == null) {
mInverseMatrix = new Matrix();
@@ -5464,7 +5484,8 @@
*/
public final void setTop(int top) {
if (top != mTop) {
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5513,7 +5534,8 @@
*/
public final void setBottom(int bottom) {
if (bottom != mBottom) {
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5559,8 +5581,8 @@
*/
public final void setLeft(int left) {
if (left != mLeft) {
- System.out.println("view " + this + " left = " + left);
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5609,7 +5631,8 @@
*/
public final void setRight(int right) {
if (right != mRight) {
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5748,26 +5771,34 @@
* @param outRect The hit rectangle of the view.
*/
public void getHitRect(Rect outRect) {
- if (hasIdentityMatrix() || mAttachInfo == null) {
+ updateMatrix();
+ if (mMatrixIsIdentity || mAttachInfo == null) {
outRect.set(mLeft, mTop, mRight, mBottom);
} else {
- Matrix m = getMatrix();
final RectF tmpRect = mAttachInfo.mTmpTransformRect;
tmpRect.set(-mPivotX, -mPivotY, getWidth() - mPivotX, getHeight() - mPivotY);
- m.mapRect(tmpRect);
+ mMatrix.mapRect(tmpRect);
outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
(int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
}
}
/**
+ * Determines whether the given point, in local coordinates is inside the view.
+ */
+ /*package*/ final boolean pointInView(float localX, float localY) {
+ return localX >= 0 && localX < (mRight - mLeft)
+ && localY >= 0 && localY < (mBottom - mTop);
+ }
+
+ /**
* Utility method to determine whether the given point, in local coordinates,
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
* is still within the view.
*/
private boolean pointInView(float localX, float localY, float slop) {
- return localX > -slop && localY > -slop && localX < ((mRight - mLeft) + slop) &&
+ return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
@@ -5832,7 +5863,8 @@
*/
public void offsetTopAndBottom(int offset) {
if (offset != 0) {
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5872,7 +5904,8 @@
*/
public void offsetLeftAndRight(int offset) {
if (offset != 0) {
- if (hasIdentityMatrix()) {
+ updateMatrix();
+ if (mMatrixIsIdentity) {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
@@ -9754,6 +9787,140 @@
}
/**
+ * Drag and drop. App calls startDrag(), then callbacks to onMeasureDragThumbnail()
+ * and onDrawDragThumbnail() happen, then the drag operation is handed over to the
+ * OS.
+ * !!! TODO: real docs
+ * @hide
+ */
+ public final boolean startDrag(ClipData data, float touchX, float touchY,
+ float thumbnailTouchX, float thumbnailTouchY, boolean myWindowOnly) {
+ if (DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "startDrag: touch=(" + touchX + "," + touchY
+ + ") thumb=(" + thumbnailTouchX + "," + thumbnailTouchY
+ + ") data=" + data + " local=" + myWindowOnly);
+ }
+ boolean okay = false;
+
+ measureThumbnail(); // throws if the view fails to specify dimensions
+
+ Surface surface = new Surface();
+ try {
+ IBinder token = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow,
+ myWindowOnly, mThumbnailWidth, mThumbnailHeight, surface);
+ if (DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" + token
+ + " surface=" + surface);
+ if (token != null) {
+ Canvas canvas = surface.lockCanvas(null);
+ try {
+ onDrawDragThumbnail(canvas);
+ } finally {
+ surface.unlockCanvasAndPost(canvas);
+ }
+
+ okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token,
+ touchX, touchY, thumbnailTouchX, thumbnailTouchY, data);
+ if (DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay);
+ }
+ } catch (Exception e) {
+ Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
+ surface.destroy();
+ }
+
+ return okay;
+ }
+
+ private void measureThumbnail() {
+ mPrivateFlags &= ~MEASURED_DIMENSION_SET;
+
+ onMeasureDragThumbnail();
+
+ // flag not set, setDragThumbnailDimension() was not invoked, we raise
+ // an exception to warn the developer
+ if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
+ throw new IllegalStateException("onMeasureDragThumbnail() did not set the"
+ + " measured dimension by calling setDragThumbnailDimension()");
+ }
+
+ if (DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "Drag thumb measured: w=" + mThumbnailWidth
+ + " h=" + mThumbnailHeight);
+ }
+ }
+
+ /**
+ * The View must call this method from onMeasureDragThumbnail() in order to
+ * specify the dimensions of the drag thumbnail image.
+ *
+ * @param width The desired thumbnail width.
+ * @param height The desired thumbnail height.
+ */
+ protected final void setDragThumbnailDimension(int width, int height) {
+ mPrivateFlags |= MEASURED_DIMENSION_SET;
+ mThumbnailWidth = width;
+ mThumbnailHeight = height;
+ }
+
+ /**
+ * The default implementation specifies a drag thumbnail that matches the
+ * View's current size and appearance.
+ */
+ protected void onMeasureDragThumbnail() {
+ setDragThumbnailDimension(getWidth(), getHeight());
+ }
+
+ /**
+ * The default implementation just draws the current View appearance as the thumbnail
+ * @param canvas
+ */
+ protected void onDrawDragThumbnail(Canvas canvas) {
+ draw(canvas);
+ }
+
+ /**
+ * Drag-and-drop event dispatch. The event.getAction() verb is one of the DragEvent
+ * constants DRAG_STARTED_EVENT, DRAG_EVENT, DROP_EVENT, and DRAG_ENDED_EVENT.
+ *
+ * For DRAG_STARTED_EVENT, event.getClipDescription() describes the content
+ * being dragged. onDragEvent() should return 'true' if the view can handle
+ * a drop of that content. A view that returns 'false' here will receive no
+ * further calls to onDragEvent() about the drag/drop operation.
+ *
+ * For DRAG_ENTERED, event.getClipDescription() describes the content being
+ * dragged. This will be the same content description passed in the
+ * DRAG_STARTED_EVENT invocation.
+ *
+ * For DRAG_EXITED, event.getClipDescription() describes the content being
+ * dragged. This will be the same content description passed in the
+ * DRAG_STARTED_EVENT invocation. The view should return to its approriate
+ * drag-acceptance visual state.
+ *
+ * For DRAG_LOCATION_EVENT, event.getX() and event.getY() give the location in View
+ * coordinates of the current drag point. The view must return 'true' if it
+ * can accept a drop of the current drag content, false otherwise.
+ *
+ * For DROP_EVENT, event.getX() and event.getY() give the location of the drop
+ * within the view; also, event.getClipData() returns the full data payload
+ * being dropped. The view should return 'true' if it consumed the dropped
+ * content, 'false' if it did not.
+ *
+ * For DRAG_ENDED_EVENT, the 'event' argument may be null. The view should return
+ * to its normal visual state.
+ */
+ protected boolean onDragEvent(DragEvent event) {
+ return false;
+ }
+
+ /**
+ * Views typically don't need to override dispatchDragEvent(); it just calls
+ * onDragEvent(what, event) and passes the result up appropriately.
+ *
+ */
+ public boolean dispatchDragEvent(DragEvent event) {
+ return onDragEvent(event);
+ }
+
+ /**
* This needs to be a better API (NOT ON VIEW) before it is exposed. If
* it is ever exposed at all.
* @hide
@@ -9821,30 +9988,6 @@
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
- private static int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) {
- final int stateSet1Length = stateSet1.length;
- final int stateSet2Length = stateSet2.length;
- final int[] newSet = new int[stateSet1Length + stateSet2Length];
- int k = 0;
- int i = 0;
- int j = 0;
- // This is a merge of the two input state sets and assumes that the
- // input sets are sorted by the order imposed by ViewDrawableStates.
- for (int viewState : R.styleable.ViewDrawableStates) {
- if (i < stateSet1Length && stateSet1[i] == viewState) {
- newSet[k++] = viewState;
- i++;
- } else if (j < stateSet2Length && stateSet2[j] == viewState) {
- newSet[k++] = viewState;
- j++;
- }
- if (k > 1) {
- assert(newSet[k - 1] > newSet[k - 2]);
- }
- }
- return newSet;
- }
-
/**
* Inflate a view from an XML resource. This convenience method wraps the {@link
* LayoutInflater} class, which provides a full range of options for view inflation.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 570e288..8a3db38 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -19,6 +19,8 @@
import android.animation.LayoutTransition;
import com.android.internal.R;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
@@ -26,6 +28,7 @@
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -33,6 +36,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
@@ -68,6 +72,7 @@
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final boolean DBG = false;
+ private static final String TAG = "ViewGroup";
/**
* Views which have been hidden or removed which need to be animated on
@@ -105,16 +110,27 @@
*/
private Transformation mInvalidationTransformation;
- // Target of Motion events
- private View mMotionTarget;
+ // View currently under an ongoing drag
+ private View mCurrentDragView;
- // Targets of MotionEvents in split mode
- private SplitMotionTargets mSplitMotionTargets;
+ // Does this group have a child that can accept the current drag payload?
+ private boolean mChildAcceptsDrag;
+
+ // Used during drag dispatch
+ private final PointF mLocalPoint = new PointF();
// Layout animation
private LayoutAnimationController mLayoutAnimationController;
private Animation.AnimationListener mAnimationListener;
+ // First touch target in the linked list of touch targets.
+ private TouchTarget mFirstTouchTarget;
+
+ // Temporary arrays for splitting pointers.
+ private int[] mTmpPointerIndexMap;
+ private int[] mTmpPointerIds;
+ private MotionEvent.PointerCoords[] mTmpPointerCoords;
+
/**
* Internal flags.
*
@@ -813,6 +829,136 @@
/**
* {@inheritDoc}
+ *
+ * !!! TODO: write real docs
+ */
+ @Override
+ public boolean dispatchDragEvent(DragEvent event) {
+ boolean retval = false;
+ final float tx = event.mX;
+ final float ty = event.mY;
+
+ // !!! BUGCHECK: If we have a ViewGroup, we must necessarily have a ViewRoot,
+ // so we don't need to check getRootView() for null here?
+ ViewRoot root = (ViewRoot)(getRootView().getParent());
+
+ // Dispatch down the view hierarchy
+ switch (event.mAction) {
+ case DragEvent.ACTION_DRAG_STARTED: {
+ // clear state to recalculate which views we drag over
+ root.setDragFocus(event, null);
+
+ // Now dispatch down to our children, caching the responses
+ mChildAcceptsDrag = false;
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ final boolean handled = children[i].dispatchDragEvent(event);
+ children[i].mCanAcceptDrop = handled;
+ if (handled) {
+ mChildAcceptsDrag = true;
+ }
+ }
+
+ // Return HANDLED if one of our children can accept the drag
+ if (mChildAcceptsDrag) {
+ retval = true;
+ }
+ } break;
+
+ case DragEvent.ACTION_DRAG_ENDED: {
+ // Notify all of our children that the drag is over
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ children[i].dispatchDragEvent(event);
+ }
+ // We consider drag-ended to have been handled if one of our children
+ // had offered to handle the drag.
+ if (mChildAcceptsDrag) {
+ retval = true;
+ }
+ } break;
+
+ case DragEvent.ACTION_DRAG_LOCATION: {
+ // Find the [possibly new] drag target
+ final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+
+ // If we've changed apparent drag target, tell the view root which view
+ // we're over now. This will in turn send out DRAG_ENTERED / DRAG_EXITED
+ // notifications as appropriate.
+ if (mCurrentDragView != target) {
+ root.setDragFocus(event, target);
+ mCurrentDragView = target;
+ }
+
+ // Dispatch the actual drag location notice, localized into its coordinates
+ if (target != null) {
+ event.mX = mLocalPoint.x;
+ event.mY = mLocalPoint.y;
+
+ retval = target.dispatchDragEvent(event);
+
+ event.mX = tx;
+ event.mY = ty;
+ }
+ } break;
+
+ case DragEvent.ACTION_DROP: {
+ if (View.DEBUG_DRAG) Slog.d(TAG, "Drop event: " + event);
+ View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+ if (target != null) {
+ event.mX = mLocalPoint.x;
+ event.mY = mLocalPoint.y;
+ retval = target.dispatchDragEvent(event);
+ event.mX = tx;
+ event.mY = ty;
+ }
+ } break;
+ }
+
+ // If none of our children could handle the event, try here
+ if (!retval) {
+ retval = onDragEvent(event);
+ }
+ return retval;
+ }
+
+ // Find the frontmost child view that lies under the given point, and calculate
+ // the position within its own local coordinate system.
+ View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
+ final float scrolledX = x + mScrollX;
+ final float scrolledY = y + mScrollY;
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = count - 1; i >= 0; i--) {
+ final View child = children[i];
+ if (child.mCanAcceptDrop == false) {
+ continue;
+ }
+
+ float localX = scrolledX - child.mLeft;
+ float localY = scrolledY - child.mTop;
+ if (!child.hasIdentityMatrix() && mAttachInfo != null) {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = localX;
+ localXY[1] = localY;
+ child.getInverseMatrix().mapPoints(localXY);
+ localX = localXY[0];
+ localY = localXY[1];
+ }
+ if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) &&
+ localY < (child.mBottom - child.mTop)) {
+ outLocalPoint.set(localX, localY);
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
*/
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
@@ -872,150 +1018,254 @@
return false;
}
- if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS) {
- if (mSplitMotionTargets == null) {
- mSplitMotionTargets = new SplitMotionTargets();
- }
- return dispatchSplitTouchEvent(ev);
+ final int action = ev.getAction();
+ final int actionMasked = action & MotionEvent.ACTION_MASK;
+
+ // Handle an initial down.
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
+ // Throw away all previous state when starting a new touch gesture.
+ // The framework may have dropped the up or cancel event for the previous gesture
+ // due to an app switch, ANR, or some other state change.
+ cancelAndClearTouchTargets(ev);
+ resetTouchState();
}
- final int action = ev.getAction();
- final float xf = ev.getX();
- final float yf = ev.getY();
- final float scrolledXFloat = xf + mScrollX;
- final float scrolledYFloat = yf + mScrollY;
-
- boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
-
- if (action == MotionEvent.ACTION_DOWN) {
- if (mMotionTarget != null) {
- // this is weird, we got a pen down, but we thought it was
- // already down!
- // XXX: We should probably send an ACTION_UP to the current
- // target.
- mMotionTarget = null;
+ // Check for interception.
+ final boolean intercepted;
+ if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
+ final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+ if (!disallowIntercept) {
+ intercepted = onInterceptTouchEvent(ev);
+ ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it
+ } else {
+ intercepted = false;
}
- // If we're disallowing intercept or if we're allowing and we didn't
- // intercept
- if (disallowIntercept || !onInterceptTouchEvent(ev)) {
- // reset this event's action (just to protect ourselves)
- ev.setAction(MotionEvent.ACTION_DOWN);
- // We know we want to dispatch the event down, find a child
- // who can handle it, start with the front-most child.
- final View[] children = mChildren;
- final int count = mChildrenCount;
+ } else {
+ intercepted = true;
+ }
- for (int i = count - 1; i >= 0; i--) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
- || child.getAnimation() != null) {
- // Single dispatch always picks its target based on the initial down
- // event's position - index 0
- if (dispatchTouchEventIfInView(child, ev, 0)) {
- mMotionTarget = child;
- return true;
+ // Check for cancelation.
+ final boolean canceled = resetCancelNextUpFlag(this)
+ || actionMasked == MotionEvent.ACTION_CANCEL;
+
+ // Update list of touch targets for pointer down, if needed.
+ final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
+ TouchTarget newTouchTarget = null;
+ boolean alreadyDispatchedToNewTouchTarget = false;
+ if (!canceled && !intercepted) {
+ if (actionMasked == MotionEvent.ACTION_DOWN
+ || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)) {
+ final int actionIndex = ev.getActionIndex(); // always 0 for down
+ final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
+ : TouchTarget.ALL_POINTER_IDS;
+
+ // Clean up earlier touch targets for this pointer id in case they
+ // have become out of sync.
+ removePointersFromTouchTargets(idBitsToAssign);
+
+ final int childrenCount = mChildrenCount;
+ if (childrenCount != 0) {
+ // Find a child that can receive the event. Scan children from front to back.
+ final View[] children = mChildren;
+ final float x = ev.getX(actionIndex);
+ final float y = ev.getY(actionIndex);
+
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final View child = children[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
+ && child.getAnimation() == null) {
+ // Skip invisible child unless it is animating.
+ continue;
+ }
+
+ if (!isTransformedTouchPointInView(x, y, child)) {
+ // New pointer is out of child's bounds.
+ continue;
+ }
+
+ newTouchTarget = getTouchTarget(child);
+ if (newTouchTarget != null) {
+ // Child is already receiving touch within its bounds.
+ // Give it the new pointer in addition to the ones it is handling.
+ newTouchTarget.pointerIdBits |= idBitsToAssign;
+ break;
+ }
+
+ resetCancelNextUpFlag(child);
+ if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
+ // Child wants to receive touch within its bounds.
+ newTouchTarget = addTouchTarget(child, idBitsToAssign);
+ alreadyDispatchedToNewTouchTarget = true;
+ break;
}
}
}
+
+ if (newTouchTarget == null && mFirstTouchTarget != null) {
+ // Did not find a child to receive the event.
+ // Assign the pointer to the least recently added target.
+ newTouchTarget = mFirstTouchTarget;
+ while (newTouchTarget.next != null) {
+ newTouchTarget = newTouchTarget.next;
+ }
+ newTouchTarget.pointerIdBits |= idBitsToAssign;
+ }
}
}
- boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
- (action == MotionEvent.ACTION_CANCEL);
-
- if (isUpOrCancel) {
- // Note, we've already copied the previous state to our local
- // variable, so this takes effect on the next event
- mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
- }
-
- // The event wasn't an ACTION_DOWN, dispatch it to our target if
- // we have one.
- final View target = mMotionTarget;
- if (target == null) {
- // We don't have a target, this means we're handling the
- // event as a regular view.
- ev.setLocation(xf, yf);
- if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- ev.setAction(MotionEvent.ACTION_CANCEL);
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- }
- return super.dispatchTouchEvent(ev);
- }
-
- // Calculate the offset point into the target's local coordinates
- float xc = scrolledXFloat - (float) target.mLeft;
- float yc = scrolledYFloat - (float) target.mTop;
- if (!target.hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = xc;
- localXY[1] = yc;
- target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0];
- yc = localXY[1];
- }
-
- // if have a target, see if we're allowed to and want to intercept its
- // events
- if (!disallowIntercept && onInterceptTouchEvent(ev)) {
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- ev.setAction(MotionEvent.ACTION_CANCEL);
- ev.setLocation(xc, yc);
- if (!target.dispatchTouchEvent(ev)) {
- // target didn't handle ACTION_CANCEL. not much we can do
- // but they should have.
- }
- // clear the target
- mMotionTarget = null;
- // Don't dispatch this event to our own view, because we already
- // saw it when intercepting; we just want to give the following
- // event to the normal onTouchEvent().
- return true;
- }
-
- if (isUpOrCancel) {
- mMotionTarget = null;
- }
-
- // finally offset the event to the target's coordinate system and
- // dispatch the event.
- ev.setLocation(xc, yc);
-
- if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- ev.setAction(MotionEvent.ACTION_CANCEL);
- target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- mMotionTarget = null;
- }
-
- if (target.dispatchTouchEvent(ev)) {
- return true;
+ // Dispatch to touch targets.
+ boolean handled = false;
+ if (mFirstTouchTarget == null) {
+ // No touch targets so treat this as an ordinary view.
+ handled = dispatchTransformedTouchEvent(ev, canceled, null,
+ TouchTarget.ALL_POINTER_IDS);
} else {
- ev.setLocation(xf, yf);
+ // Dispatch to touch targets, excluding the new touch target if we already
+ // dispatched to it. Cancel touch targets if necessary.
+ TouchTarget predecessor = null;
+ TouchTarget target = mFirstTouchTarget;
+ while (target != null) {
+ final TouchTarget next = target.next;
+ if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
+ handled = true;
+ } else {
+ final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
+ if (dispatchTransformedTouchEvent(ev, cancelChild,
+ target.child, target.pointerIdBits)) {
+ handled = true;
+ }
+ if (cancelChild) {
+ if (predecessor == null) {
+ mFirstTouchTarget = next;
+ } else {
+ predecessor.next = next;
+ }
+ target.recycle();
+ target = next;
+ continue;
+ }
+ }
+ predecessor = target;
+ target = next;
+ }
+ }
+
+ // Update list of touch targets for pointer up or cancel, if needed.
+ if (canceled || actionMasked == MotionEvent.ACTION_UP) {
+ resetTouchState();
+ } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
+ final int actionIndex = ev.getActionIndex();
+ final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
+ removePointersFromTouchTargets(idBitsToRemove);
+ }
+
+ return handled;
+ }
+
+ /* Resets all touch state in preparation for a new cycle. */
+ private final void resetTouchState() {
+ clearTouchTargets();
+ resetCancelNextUpFlag(this);
+ mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+ }
+
+ /* Resets the cancel next up flag.
+ * Returns true if the flag was previously set. */
+ private final boolean resetCancelNextUpFlag(View view) {
+ if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
+ view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+ return true;
}
return false;
}
- /**
- * This method detects whether the pointer location at <code>pointerIndex</code> within
- * <code>ev</code> is inside the specified view. If so, the transformed event is dispatched to
- * <code>child</code>.
- *
- * @param child View to hit test against
- * @param ev MotionEvent to test
- * @param pointerIndex Index of the pointer within <code>ev</code> to test
- * @return <code>false</code> if the hit test failed, or the result of
- * <code>child.dispatchTouchEvent</code>
- */
- private boolean dispatchTouchEventIfInView(View child, MotionEvent ev, int pointerIndex) {
- final float x = ev.getX(pointerIndex);
- final float y = ev.getY(pointerIndex);
- final float scrolledX = x + mScrollX;
- final float scrolledY = y + mScrollY;
- float localX = scrolledX - child.mLeft;
- float localY = scrolledY - child.mTop;
- if (!child.hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
+ /* Clears all touch targets. */
+ private final void clearTouchTargets() {
+ TouchTarget target = mFirstTouchTarget;
+ if (target != null) {
+ do {
+ TouchTarget next = target.next;
+ target.recycle();
+ target = next;
+ } while (target != null);
+ mFirstTouchTarget = null;
+ }
+ }
+
+ /* Cancels and clears all touch targets. */
+ private final void cancelAndClearTouchTargets(MotionEvent event) {
+ if (mFirstTouchTarget != null) {
+ boolean syntheticEvent = false;
+ if (event == null) {
+ final long now = SystemClock.uptimeMillis();
+ event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+ syntheticEvent = true;
+ }
+
+ for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
+ resetCancelNextUpFlag(target.child);
+ dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
+ }
+ clearTouchTargets();
+
+ if (syntheticEvent) {
+ event.recycle();
+ }
+ }
+ }
+
+ /* Gets the touch target for specified child view.
+ * Returns null if not found. */
+ private final TouchTarget getTouchTarget(View child) {
+ for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
+ if (target.child == child) {
+ return target;
+ }
+ }
+ return null;
+ }
+
+ /* Adds a touch target for specified child to the beginning of the list.
+ * Assumes the target child is not already present. */
+ private final TouchTarget addTouchTarget(View child, int pointerIdBits) {
+ TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
+ target.next = mFirstTouchTarget;
+ mFirstTouchTarget = target;
+ return target;
+ }
+
+ /* Removes the pointer ids from consideration. */
+ private final void removePointersFromTouchTargets(int pointerIdBits) {
+ TouchTarget predecessor = null;
+ TouchTarget target = mFirstTouchTarget;
+ while (target != null) {
+ final TouchTarget next = target.next;
+ if ((target.pointerIdBits & pointerIdBits) != 0) {
+ target.pointerIdBits &= ~pointerIdBits;
+ if (target.pointerIdBits == 0) {
+ if (predecessor == null) {
+ mFirstTouchTarget = next;
+ } else {
+ predecessor.next = next;
+ }
+ target.recycle();
+ target = next;
+ continue;
+ }
+ }
+ predecessor = target;
+ target = next;
+ }
+ }
+
+ /* Returns true if a child view contains the specified point when transformed
+ * into its coordinate space.
+ * Child must not be null. */
+ private final boolean isTransformedTouchPointInView(float x, float y, View child) {
+ float localX = x + mScrollX - child.mLeft;
+ float localY = y + mScrollY - child.mTop;
+ if (! child.hasIdentityMatrix() && mAttachInfo != null) {
final float[] localXY = mAttachInfo.mTmpTransformLocation;
localXY[0] = localX;
localXY[1] = localY;
@@ -1023,224 +1273,215 @@
localX = localXY[0];
localY = localXY[1];
}
- if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) &&
- localY < (child.mBottom - child.mTop)) {
- // It would be safer to clone the event here but we don't for performance.
- // There are many subtle interactions in touch event dispatch; change at your own risk.
- child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- ev.offsetLocation(localX - x, localY - y);
- if (child.dispatchTouchEvent(ev)) {
- return true;
- } else {
- ev.offsetLocation(x - localX, y - localY);
- return false;
- }
- }
- return false;
+ return child.pointInView(localX, localY);
}
- private boolean dispatchSplitTouchEvent(MotionEvent ev) {
- final SplitMotionTargets targets = mSplitMotionTargets;
- final int action = ev.getAction();
- final int maskedAction = ev.getActionMasked();
- float xf = ev.getX();
- float yf = ev.getY();
- float scrolledXFloat = xf + mScrollX;
- float scrolledYFloat = yf + mScrollY;
+ /* Transforms a motion event into the coordinate space of a particular child view,
+ * filters out irrelevant pointer ids, and overrides its action if necessary.
+ * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. */
+ private final boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
+ View child, int desiredPointerIdBits) {
+ final boolean handled;
- boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+ // Canceling motions is a special case. We don't need to perform any transformations
+ // or filtering. The important part is the action, not the contents.
+ final int oldAction = event.getAction();
+ if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ if (child == null) {
+ handled = super.dispatchTouchEvent(event);
+ } else {
+ handled = child.dispatchTouchEvent(event);
+ }
+ event.setAction(oldAction);
+ return handled;
+ }
- if (maskedAction == MotionEvent.ACTION_DOWN ||
- maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
- final int actionIndex = ev.getActionIndex();
- final int actionId = ev.getPointerId(actionIndex);
+ // Calculate the number of pointers to deliver.
+ final int oldPointerCount = event.getPointerCount();
+ int newPointerCount = 0;
+ if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) {
+ newPointerCount = oldPointerCount;
+ } else {
+ for (int i = 0; i < oldPointerCount; i++) {
+ final int pointerId = event.getPointerId(i);
+ final int pointerIdBit = 1 << pointerId;
+ if ((pointerIdBit & desiredPointerIdBits) != 0) {
+ newPointerCount += 1;
+ }
+ }
+ }
- // Clear out any current target for this ID.
- // XXX: We should probably send an ACTION_UP to the current
- // target if present.
- targets.removeById(actionId);
+ // If for some reason we ended up in an inconsistent state where it looks like we
+ // might produce a motion event with no pointers in it, then drop the event.
+ if (newPointerCount == 0) {
+ return false;
+ }
- // If we're disallowing intercept or if we're allowing and we didn't
- // intercept
- if (disallowIntercept || !onInterceptTouchEvent(ev)) {
- // reset this event's action (just to protect ourselves)
- ev.setAction(action);
- // We know we want to dispatch the event down, try to find a child
- // who can handle it, start with the front-most child.
- final long downTime = ev.getEventTime();
- final View[] children = mChildren;
- final int count = mChildrenCount;
- for (int i = count - 1; i >= 0; i--) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
- || child.getAnimation() != null) {
- final MotionEvent childEvent =
- targets.filterMotionEventForChild(ev, child, downTime);
- if (childEvent != null) {
- try {
- final int childActionIndex = childEvent.findPointerIndex(actionId);
- if (dispatchTouchEventIfInView(child, childEvent,
- childActionIndex)) {
- targets.add(actionId, child, downTime);
+ // If the number of pointers is the same and we don't need to perform any fancy
+ // irreversible transformations, then we can reuse the motion event for this
+ // dispatch as long as we are careful to revert any changes we make.
+ final boolean reuse = newPointerCount == oldPointerCount
+ && (child == null || child.hasIdentityMatrix());
+ if (reuse) {
+ if (child == null) {
+ handled = super.dispatchTouchEvent(event);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ event.offsetLocation(offsetX, offsetY);
- return true;
- }
- } finally {
- childEvent.recycle();
+ handled = child.dispatchTouchEvent(event);
+
+ event.offsetLocation(-offsetX, -offsetY);
+ }
+ return handled;
+ }
+
+ // Make a copy of the event.
+ // If the number of pointers is different, then we need to filter out irrelevant pointers
+ // as we make a copy of the motion event.
+ MotionEvent transformedEvent;
+ if (newPointerCount == oldPointerCount) {
+ transformedEvent = MotionEvent.obtain(event);
+ } else {
+ growTmpPointerArrays(newPointerCount);
+ final int[] newPointerIndexMap = mTmpPointerIndexMap;
+ final int[] newPointerIds = mTmpPointerIds;
+ final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords;
+
+ int newPointerIndex = 0;
+ int oldPointerIndex = 0;
+ while (newPointerIndex < newPointerCount) {
+ final int pointerId = event.getPointerId(oldPointerIndex);
+ final int pointerIdBits = 1 << pointerId;
+ if ((pointerIdBits & desiredPointerIdBits) != 0) {
+ newPointerIndexMap[newPointerIndex] = oldPointerIndex;
+ newPointerIds[newPointerIndex] = pointerId;
+ if (newPointerCoords[newPointerIndex] == null) {
+ newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords();
+ }
+
+ newPointerIndex += 1;
+ }
+ oldPointerIndex += 1;
+ }
+
+ final int newAction;
+ if (cancel) {
+ newAction = MotionEvent.ACTION_CANCEL;
+ } else {
+ final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
+ if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
+ || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) {
+ final int changedPointerId = event.getPointerId(
+ (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+ final int changedPointerIdBits = 1 << changedPointerId;
+ if ((changedPointerIdBits & desiredPointerIdBits) != 0) {
+ if (newPointerCount == 1) {
+ // The first/last pointer went down/up.
+ newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
+ ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
+ } else {
+ // A secondary pointer went down/up.
+ int newChangedPointerIndex = 0;
+ while (newPointerIds[newChangedPointerIndex] != changedPointerId) {
+ newChangedPointerIndex += 1;
}
+ newAction = oldMaskedAction | (newChangedPointerIndex
+ << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
}
+ } else {
+ // An unrelated pointer changed.
+ newAction = MotionEvent.ACTION_MOVE;
+ }
+ } else {
+ // Simple up/down/cancel/move motion action.
+ newAction = oldMaskedAction;
+ }
+ }
+
+ transformedEvent = null;
+ final int historySize = event.getHistorySize();
+ for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) {
+ for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) {
+ final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex];
+ oldPointerIndex = newPointerIndexMap[newPointerIndex];
+ if (historyIndex != historySize) {
+ event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c);
+ } else {
+ event.getPointerCoords(oldPointerIndex, c);
}
}
- // Didn't find a new target. Do we have a "primary" target to send to?
- final SplitMotionTargets.TargetInfo primaryTargetInfo = targets.getPrimaryTarget();
- if (primaryTargetInfo != null) {
- final View primaryTarget = primaryTargetInfo.view;
- final MotionEvent childEvent = targets.filterMotionEventForChild(ev,
- primaryTarget, primaryTargetInfo.downTime);
- if (childEvent != null) {
- try {
- // Calculate the offset point into the target's local coordinates
- float xc = scrolledXFloat - (float) primaryTarget.mLeft;
- float yc = scrolledYFloat - (float) primaryTarget.mTop;
- if (!primaryTarget.hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's
- // coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = xc;
- localXY[1] = yc;
- primaryTarget.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0];
- yc = localXY[1];
- }
- childEvent.setLocation(xc, yc);
- if (primaryTarget.dispatchTouchEvent(childEvent)) {
- targets.add(actionId, primaryTarget, primaryTargetInfo.downTime);
- return true;
- }
- } finally {
- childEvent.recycle();
- }
- }
+ final long eventTime;
+ if (historyIndex != historySize) {
+ eventTime = event.getHistoricalEventTime(historyIndex);
+ } else {
+ eventTime = event.getEventTime();
+ }
+
+ if (transformedEvent == null) {
+ transformedEvent = MotionEvent.obtain(
+ event.getDownTime(), eventTime, newAction,
+ newPointerCount, newPointerIds, newPointerCoords,
+ event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
+ event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
+ event.getFlags());
+ } else {
+ transformedEvent.addBatch(eventTime, newPointerCoords, 0);
}
}
}
- boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
- (action == MotionEvent.ACTION_CANCEL);
-
- if (isUpOrCancel) {
- // Note, we've already copied the previous state to our local
- // variable, so this takes effect on the next event
- mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
- }
-
- if (targets.isEmpty()) {
- // We don't have any targets, this means we're handling the
- // event as a regular view.
- ev.setLocation(xf, yf);
- if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- ev.setAction(MotionEvent.ACTION_CANCEL);
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- }
- return super.dispatchTouchEvent(ev);
- }
-
- // if we have targets, see if we're allowed to and want to intercept their
- // events
- int uniqueTargetCount = targets.getUniqueTargetCount();
- if (!disallowIntercept && onInterceptTouchEvent(ev)) {
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-
- for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
- final View target = targets.getUniqueTargetAt(uniqueIndex).view;
-
- // Calculate the offset point into the target's local coordinates
- float xc = scrolledXFloat - (float) target.mLeft;
- float yc = scrolledYFloat - (float) target.mTop;
- if (!target.hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = xc;
- localXY[1] = yc;
- target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0];
- yc = localXY[1];
- }
-
- ev.setAction(MotionEvent.ACTION_CANCEL);
- ev.setLocation(xc, yc);
- if (!target.dispatchTouchEvent(ev)) {
- // target didn't handle ACTION_CANCEL. not much we can do
- // but they should have.
- }
- }
- targets.clear();
- // Don't dispatch this event to our own view, because we already
- // saw it when intercepting; we just want to give the following
- // event to the normal onTouchEvent().
- return true;
- }
-
- boolean handled = false;
- for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
- final SplitMotionTargets.TargetInfo targetInfo = targets.getUniqueTargetAt(uniqueIndex);
- final View target = targetInfo.view;
-
- final MotionEvent targetEvent =
- targets.filterMotionEventForChild(ev, target, targetInfo.downTime);
- if (targetEvent == null) {
- continue;
+ // Perform any necessary transformations and dispatch.
+ if (child == null) {
+ handled = super.dispatchTouchEvent(transformedEvent);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ transformedEvent.offsetLocation(offsetX, offsetY);
+ if (! child.hasIdentityMatrix()) {
+ transformedEvent.transform(child.getInverseMatrix());
}
- try {
- // Calculate the offset point into the target's local coordinates
- xf = targetEvent.getX();
- yf = targetEvent.getY();
- scrolledXFloat = xf + mScrollX;
- scrolledYFloat = yf + mScrollY;
- float xc = scrolledXFloat - (float) target.mLeft;
- float yc = scrolledYFloat - (float) target.mTop;
- if (!target.hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = xc;
- localXY[1] = yc;
- target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0];
- yc = localXY[1];
- }
-
- // finally offset the event to the target's coordinate system and
- // dispatch the event.
- targetEvent.setLocation(xc, yc);
-
- if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- targetEvent.setAction(MotionEvent.ACTION_CANCEL);
- target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- targets.removeView(target);
- uniqueIndex--;
- uniqueTargetCount--;
- }
-
- handled |= target.dispatchTouchEvent(targetEvent);
- } finally {
- targetEvent.recycle();
- }
+ handled = child.dispatchTouchEvent(transformedEvent);
}
- if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
- final int removeId = ev.getPointerId(ev.getActionIndex());
- targets.removeById(removeId);
- }
-
- if (isUpOrCancel) {
- targets.clear();
- }
-
+ // Done.
+ transformedEvent.recycle();
return handled;
}
+ /* Enlarge the temporary pointer arrays for splitting pointers.
+ * May discard contents (but keeps PointerCoords objects to avoid reallocating them). */
+ private final void growTmpPointerArrays(int desiredCapacity) {
+ final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords;
+ int capacity;
+ if (oldTmpPointerCoords != null) {
+ capacity = oldTmpPointerCoords.length;
+ if (desiredCapacity <= capacity) {
+ return;
+ }
+ } else {
+ capacity = 4;
+ }
+
+ while (capacity < desiredCapacity) {
+ capacity *= 2;
+ }
+
+ mTmpPointerIndexMap = new int[capacity];
+ mTmpPointerIds = new int[capacity];
+ mTmpPointerCoords = new MotionEvent.PointerCoords[capacity];
+
+ if (oldTmpPointerCoords != null) {
+ System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0,
+ oldTmpPointerCoords.length);
+ }
+ }
+
/**
* Enable or disable the splitting of MotionEvents to multiple children during touch event
* dispatch. This behavior is disabled by default.
@@ -1262,7 +1503,6 @@
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
} else {
mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
- mSplitMotionTargets = null;
}
}
@@ -1473,19 +1713,12 @@
*/
@Override
void dispatchDetachedFromWindow() {
- // If we still have a motion target, we are still in the process of
+ // If we still have a touch target, we are still in the process of
// dispatching motion events to a child; we need to get rid of that
// child to avoid dispatching events to it after the window is torn
// down. To make sure we keep the child in a consistent state, we
// first send it an ACTION_CANCEL motion event.
- if (mMotionTarget != null) {
- final long now = SystemClock.uptimeMillis();
- final MotionEvent event = MotionEvent.obtain(now, now,
- MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
- mMotionTarget.dispatchTouchEvent(event);
- event.recycle();
- mMotionTarget = null;
- }
+ cancelAndClearTouchTargets(null);
final int count = mChildrenCount;
final View[] children = mChildren;
@@ -4287,290 +4520,57 @@
}
}
- private static class SplitMotionTargets {
- private SparseArray<View> mTargets;
- private TargetInfo[] mUniqueTargets;
- private int mUniqueTargetCount;
- private MotionEvent.PointerCoords[] mPointerCoords;
- private int[] mPointerIds;
+ /* Describes a touched view and the ids of the pointers that it has captured.
+ *
+ * This code assumes that pointer ids are always in the range 0..31 such that
+ * it can use a bitfield to track which pointer ids are present.
+ * As it happens, the lower layers of the input dispatch pipeline also use the
+ * same trick so the assumption should be safe here...
+ */
+ private static final class TouchTarget {
+ private static final int MAX_RECYCLED = 32;
+ private static final Object sRecycleLock = new Object();
+ private static TouchTarget sRecycleBin;
+ private static int sRecycledCount;
- private static final int INITIAL_UNIQUE_MOTION_TARGETS_SIZE = 5;
- private static final int INITIAL_BUCKET_SIZE = 5;
+ public static final int ALL_POINTER_IDS = -1; // all ones
- public SplitMotionTargets() {
- mTargets = new SparseArray<View>();
- mUniqueTargets = new TargetInfo[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
- mPointerIds = new int[INITIAL_BUCKET_SIZE];
- mPointerCoords = new MotionEvent.PointerCoords[INITIAL_BUCKET_SIZE];
- for (int i = 0; i < INITIAL_BUCKET_SIZE; i++) {
- mPointerCoords[i] = new MotionEvent.PointerCoords();
- }
+ // The touched child view.
+ public View child;
+
+ // The combined bit mask of pointer ids for all pointers captured by the target.
+ public int pointerIdBits;
+
+ // The next target in the target list.
+ public TouchTarget next;
+
+ private TouchTarget() {
}
- public void clear() {
- mTargets.clear();
- final int count = mUniqueTargetCount;
- for (int i = 0; i < count; i++) {
- mUniqueTargets[i].recycle();
- mUniqueTargets[i] = null;
- }
- mUniqueTargetCount = 0;
- }
-
- public void add(int pointerId, View target, long downTime) {
- mTargets.put(pointerId, target);
-
- final int uniqueCount = mUniqueTargetCount;
- boolean addUnique = true;
- for (int i = 0; i < uniqueCount; i++) {
- if (mUniqueTargets[i].view == target) {
- addUnique = false;
- }
- }
- if (addUnique) {
- if (mUniqueTargets.length == uniqueCount) {
- TargetInfo[] newTargets =
- new TargetInfo[uniqueCount + INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
- System.arraycopy(mUniqueTargets, 0, newTargets, 0, uniqueCount);
- mUniqueTargets = newTargets;
- }
- mUniqueTargets[uniqueCount] = TargetInfo.obtain(target, downTime);
- mUniqueTargetCount++;
- }
- }
-
- public int getIdCount() {
- return mTargets.size();
- }
-
- public int getUniqueTargetCount() {
- return mUniqueTargetCount;
- }
-
- public TargetInfo getUniqueTargetAt(int index) {
- return mUniqueTargets[index];
- }
-
- public View get(int id) {
- return mTargets.get(id);
- }
-
- public int indexOfTarget(View target) {
- return mTargets.indexOfValue(target);
- }
-
- public View targetAt(int index) {
- return mTargets.valueAt(index);
- }
-
- public TargetInfo getPrimaryTarget() {
- if (!isEmpty()) {
- // Find the longest-lived target
- long firstTime = Long.MAX_VALUE;
- int firstIndex = 0;
- final int uniqueCount = mUniqueTargetCount;
- for (int i = 0; i < uniqueCount; i++) {
- TargetInfo info = mUniqueTargets[i];
- if (info.downTime < firstTime) {
- firstTime = info.downTime;
- firstIndex = i;
- }
- }
- return mUniqueTargets[firstIndex];
- }
- return null;
- }
-
- public boolean isEmpty() {
- return mUniqueTargetCount == 0;
- }
-
- public void removeById(int id) {
- final int index = mTargets.indexOfKey(id);
- removeAt(index);
- }
-
- public void removeView(View view) {
- int i = 0;
- while (i < mTargets.size()) {
- if (mTargets.valueAt(i) == view) {
- mTargets.removeAt(i);
- } else {
- i++;
- }
- }
- removeUnique(view);
- }
-
- public void removeAt(int index) {
- if (index < 0 || index >= mTargets.size()) {
- return;
- }
-
- final View removeView = mTargets.valueAt(index);
- mTargets.removeAt(index);
- if (mTargets.indexOfValue(removeView) < 0) {
- removeUnique(removeView);
- }
- }
-
- private void removeUnique(View removeView) {
- TargetInfo[] unique = mUniqueTargets;
- int uniqueCount = mUniqueTargetCount;
- for (int i = 0; i < uniqueCount; i++) {
- if (unique[i].view == removeView) {
- unique[i].recycle();
- unique[i] = unique[--uniqueCount];
- unique[uniqueCount] = null;
- break;
- }
- }
-
- mUniqueTargetCount = uniqueCount;
- }
-
- /**
- * Return a new (obtain()ed) MotionEvent containing only data for pointers that should
- * be dispatched to child. Don't forget to recycle it!
- */
- public MotionEvent filterMotionEventForChild(MotionEvent ev, View child, long downTime) {
- int action = ev.getAction();
- final int maskedAction = action & MotionEvent.ACTION_MASK;
-
- // Only send pointer up events if this child was the target. Drop it otherwise.
- if (maskedAction == MotionEvent.ACTION_POINTER_UP &&
- get(ev.getPointerId(ev.getActionIndex())) != child) {
- return null;
- }
-
- int pointerCount = 0;
- final int idCount = getIdCount();
- for (int i = 0; i < idCount; i++) {
- if (targetAt(i) == child) {
- pointerCount++;
- }
- }
-
- int actionId = -1;
- boolean needsNewIndex = false; // True if we should fill in the action's masked index
-
- // If we have a down event, it wasn't counted above.
- if (maskedAction == MotionEvent.ACTION_DOWN) {
- pointerCount++;
- actionId = ev.getPointerId(0);
- } else if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
- pointerCount++;
-
- actionId = ev.getPointerId(ev.getActionIndex());
-
- if (indexOfTarget(child) < 0) {
- // The new action should be ACTION_DOWN if this child isn't currently getting
- // any events.
- action = MotionEvent.ACTION_DOWN;
- } else {
- // Fill in the index portion of the action later.
- needsNewIndex = true;
- }
- } else if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
- actionId = ev.getPointerId(ev.getActionIndex());
- if (pointerCount == 1) {
- // The new action should be ACTION_UP if there's only one pointer left for
- // this target.
- action = MotionEvent.ACTION_UP;
- } else {
- // Fill in the index portion of the action later.
- needsNewIndex = true;
- }
- }
-
- if (pointerCount == 0) {
- return null;
- }
-
- // Fill the buckets with pointer data!
- final int eventPointerCount = ev.getPointerCount();
- int bucketIndex = 0;
- int newActionIndex = -1;
- for (int evp = 0; evp < eventPointerCount; evp++) {
- final int id = ev.getPointerId(evp);
-
- // Add this pointer to the bucket if it is new or targeted at child
- if (id == actionId || get(id) == child) {
- // Expand scratch arrays if needed
- if (mPointerCoords.length <= bucketIndex) {
- int[] pointerIds = new int[pointerCount];
- MotionEvent.PointerCoords[] pointerCoords =
- new MotionEvent.PointerCoords[pointerCount];
- for (int i = mPointerCoords.length; i < pointerCoords.length; i++) {
- pointerCoords[i] = new MotionEvent.PointerCoords();
- }
-
- System.arraycopy(mPointerCoords, 0,
- pointerCoords, 0, mPointerCoords.length);
- System.arraycopy(mPointerIds, 0, pointerIds, 0, mPointerIds.length);
-
- mPointerCoords = pointerCoords;
- mPointerIds = pointerIds;
- }
-
- mPointerIds[bucketIndex] = id;
- ev.getPointerCoords(evp, mPointerCoords[bucketIndex]);
-
- if (needsNewIndex && id == actionId) {
- newActionIndex = bucketIndex;
- }
-
- bucketIndex++;
- }
- }
-
- // Encode the new action index if we have one
- if (newActionIndex >= 0) {
- action = (action & MotionEvent.ACTION_MASK) |
- (newActionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
-
- return MotionEvent.obtain(downTime, ev.getEventTime(),
- action, pointerCount, mPointerIds, mPointerCoords, ev.getMetaState(),
- ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(),
- ev.getSource(), ev.getFlags());
- }
-
- static class TargetInfo {
- public View view;
- public long downTime;
-
- private TargetInfo mNextRecycled;
-
- private static TargetInfo sRecycleBin;
- private static int sRecycledCount;
-
- private static int MAX_RECYCLED = 15;
-
- private TargetInfo() {
- }
-
- public static TargetInfo obtain(View v, long time) {
- TargetInfo info;
+ public static TouchTarget obtain(View child, int pointerIdBits) {
+ final TouchTarget target;
+ synchronized (sRecycleLock) {
if (sRecycleBin == null) {
- info = new TargetInfo();
+ target = new TouchTarget();
} else {
- info = sRecycleBin;
- sRecycleBin = info.mNextRecycled;
- sRecycledCount--;
+ target = sRecycleBin;
+ sRecycleBin = target.next;
+ sRecycledCount--;
+ target.next = null;
}
- info.view = v;
- info.downTime = time;
- return info;
}
+ target.child = child;
+ target.pointerIdBits = pointerIdBits;
+ return target;
+ }
- public void recycle() {
- if (sRecycledCount >= MAX_RECYCLED) {
- return;
+ public void recycle() {
+ synchronized (sRecycleLock) {
+ if (sRecycledCount < MAX_RECYCLED) {
+ next = sRecycleBin;
+ sRecycleBin = this;
+ sRecycledCount += 1;
}
- mNextRecycled = sRecycleBin;
- sRecycleBin = this;
- sRecycledCount++;
}
}
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 77ba6fe..c63f7d6 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -24,6 +24,7 @@
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.*;
@@ -45,6 +46,8 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.app.ActivityManagerNative;
@@ -198,6 +201,11 @@
final ViewConfiguration mViewConfiguration;
+ /* Drag/drop */
+ ClipDescription mDragDescription;
+ View mCurrentDragView;
+ final PointF mDragPoint = new PointF();
+
/**
* see {@link #playSoundEffect(int)}
*/
@@ -1670,6 +1678,7 @@
public final static int FINISH_INPUT_CONNECTION = 1012;
public final static int CHECK_FOCUS = 1013;
public final static int CLOSE_SYSTEM_DIALOGS = 1014;
+ public final static int DISPATCH_DRAG_EVENT = 1015;
@Override
public void handleMessage(Message msg) {
@@ -1845,6 +1854,9 @@
mView.onCloseSystemDialogs((String)msg.obj);
}
} break;
+ case DISPATCH_DRAG_EVENT: {
+ handleDragEvent((DragEvent)msg.obj);
+ } break;
}
}
@@ -2434,6 +2446,87 @@
}
}
+ /* drag/drop */
+ private void handleDragEvent(DragEvent event) {
+ // From the root, only drag start/end/location are dispatched. entered/exited
+ // are determined and dispatched by the viewgroup hierarchy, who then report
+ // that back here for ultimate reporting back to the framework.
+ if (mView != null && mAdded) {
+ final int what = event.mAction;
+
+ if (what == DragEvent.ACTION_DRAG_EXITED) {
+ // A direct EXITED event means that the window manager knows we've just crossed
+ // a window boundary, so the current drag target within this one must have
+ // just been exited. Send it the usual notifications and then we're done
+ // for now.
+ setDragFocus(event, null);
+ } else {
+ // Cache the drag description when the operation starts, then fill it in
+ // on subsequent calls as a convenience
+ if (what == DragEvent.ACTION_DRAG_STARTED) {
+ mDragDescription = event.mClipDescription;
+ } else {
+ event.mClipDescription = mDragDescription;
+ }
+
+ // For events with a [screen] location, translate into window coordinates
+ if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
+ mDragPoint.set(event.mX, event.mY);
+ if (mTranslator != null) {
+ mTranslator.translatePointInScreenToAppWindow(mDragPoint);
+ }
+
+ if (mCurScrollY != 0) {
+ mDragPoint.offset(0, mCurScrollY);
+ }
+
+ event.mX = mDragPoint.x;
+ event.mY = mDragPoint.y;
+ }
+
+ // Remember who the current drag target is pre-dispatch
+ final View prevDragView = mCurrentDragView;
+
+ // Now dispatch the drag/drop event
+ mView.dispatchDragEvent(event);
+
+ // If we changed apparent drag target, tell the OS about it
+ if (prevDragView != mCurrentDragView) {
+ try {
+ if (prevDragView != null) {
+ sWindowSession.dragRecipientExited(mWindow);
+ }
+ if (mCurrentDragView != null) {
+ sWindowSession.dragRecipientEntered(mWindow);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to note drag target change");
+ }
+ mCurrentDragView = prevDragView;
+ }
+ }
+ }
+ event.recycle();
+ }
+
+ public void setDragFocus(DragEvent event, View newDragTarget) {
+ final int action = event.mAction;
+ // If we've dragged off of a view, send it the EXITED message
+ if (mCurrentDragView != newDragTarget) {
+ if (mCurrentDragView != null) {
+ event.mAction = DragEvent.ACTION_DRAG_EXITED;
+ mCurrentDragView.dispatchDragEvent(event);
+ }
+ }
+ // If we've dragged over a new view, send it the ENTERED message
+ if (newDragTarget != null) {
+ event.mAction = DragEvent.ACTION_DRAG_ENTERED;
+ newDragTarget.dispatchDragEvent(event);
+ }
+ mCurrentDragView = newDragTarget;
+ event.mAction = action; // restore the event's original state
+ }
+
private AudioManager getAudioManager() {
if (mView == null) {
throw new IllegalStateException("getAudioManager called when there is no mView");
@@ -2725,7 +2818,12 @@
msg.obj = reason;
sendMessage(msg);
}
-
+
+ public void dispatchDragEvent(DragEvent event) {
+ Message msg = obtainMessage(DISPATCH_DRAG_EVENT, event);
+ sendMessage(msg);
+ }
+
/**
* The window is getting focus so if there is anything focused/selected
* send an {@link AccessibilityEvent} to announce that.
@@ -2936,6 +3034,14 @@
}
}
}
+
+ /* Drag/drop */
+ public void dispatchDragEvent(DragEvent event) {
+ final ViewRoot viewRoot = mViewRoot.get();
+ if (viewRoot != null) {
+ viewRoot.dispatchDragEvent(event);
+ }
+ }
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dc3b44d..eddd04e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -347,6 +347,13 @@
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
/**
+ * Window type: the drag-and-drop pseudowindow. There is only one
+ * drag layer (at most), and it is placed on top of all other windows.
+ * @hide
+ */
+ public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+15;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7944807..fedb873 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1291,6 +1291,7 @@
private void clearHelpers() {
clearTextEntry();
clearActionModes();
+ dismissFullScreenMode();
}
/**
@@ -4911,6 +4912,13 @@
return mFullScreenHolder != null;
}
+ private void dismissFullScreenMode() {
+ if (inFullScreenMode()) {
+ mFullScreenHolder.dismiss();
+ mFullScreenHolder = null;
+ }
+ }
+
void onPinchToZoomAnimationStart() {
// cancel the single touch handling
cancelTouch();
@@ -6878,9 +6886,9 @@
View view = (View) msg.obj;
int npp = msg.arg1;
- if (mFullScreenHolder != null) {
+ if (inFullScreenMode()) {
Log.w(LOGTAG, "Should not have another full screen.");
- mFullScreenHolder.dismiss();
+ dismissFullScreenMode();
}
mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
mFullScreenHolder.setContentView(view);
@@ -6891,10 +6899,7 @@
break;
}
case HIDE_FULLSCREEN:
- if (inFullScreenMode()) {
- mFullScreenHolder.dismiss();
- mFullScreenHolder = null;
- }
+ dismissFullScreenMode();
break;
case DOM_FOCUS_CHANGED:
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 1bc0612..6799ffd 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -88,6 +88,7 @@
private boolean mOutsideTouchable = false;
private boolean mClippingEnabled = true;
private boolean mSplitTouchEnabled;
+ private boolean mLayoutInScreen;
private OnTouchListener mTouchInterceptor;
@@ -607,6 +608,29 @@
}
/**
+ * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
+ * for positioning.</p>
+ *
+ * @return true if the window will always be positioned in screen coordinates.
+ * @hide
+ */
+ public boolean isLayoutInScreenEnabled() {
+ return mLayoutInScreen;
+ }
+
+ /**
+ * <p>Allows the popup window to force the flag
+ * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
+ * This will cause the popup to be positioned in absolute screen coordinates.</p>
+ *
+ * @param enabled true if the popup should always be positioned in screen coordinates
+ * @hide
+ */
+ public void setLayoutInScreenEnabled(boolean enabled) {
+ mLayoutInScreen = enabled;
+ }
+
+ /**
* <p>Change the width and height measure specs that are given to the
* window manager by the popup. By default these are 0, meaning that
* the current width or height is requested as an explicit size from
@@ -910,7 +934,8 @@
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
if(mIgnoreCheekPress) {
curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
}
@@ -934,6 +959,9 @@
if (mSplitTouchEnabled) {
curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
}
+ if (mLayoutInScreen) {
+ curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ }
return curFlags;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f4d193f..5a2cebe 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3798,6 +3798,8 @@
if (mError != null) {
hideError();
}
+
+ hideControllers();
}
@Override
@@ -4165,6 +4167,15 @@
*/
canvas.restore();
+
+ if (mInsertionPointCursorController != null &&
+ mInsertionPointCursorController.isShowing()) {
+ mInsertionPointCursorController.updatePosition();
+ }
+ if (mSelectionModifierCursorController != null &&
+ mSelectionModifierCursorController.isShowing()) {
+ mSelectionModifierCursorController.updatePosition();
+ }
}
@Override
@@ -4788,6 +4799,7 @@
if (mInputMethodState != null) {
mInputMethodState.mExtracting = req;
}
+ hideControllers();
}
/**
@@ -6326,7 +6338,11 @@
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
- hideControllers();
+
+ // Hide the controller if the amount of content changed
+ if (before != after) {
+ hideControllers();
+ }
}
/**
@@ -6668,11 +6684,20 @@
if (mInputContentType != null) {
mInputContentType.enterDown = false;
}
+ hideControllers();
}
startStopMarquee(hasWindowFocus);
}
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (visibility != VISIBLE) {
+ hideControllers();
+ }
+ }
+
/**
* Use {@link BaseInputConnection#removeComposingSpans
* BaseInputConnection.removeComposingSpans()} to remove any IME composing
@@ -6742,8 +6767,6 @@
if (hasSelection()) {
startSelectionActionMode();
- } else if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.show();
}
}
}
@@ -7571,28 +7594,28 @@
if (canSelectAll()) {
menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
- setIcon(com.android.internal.R.drawable.ic_menu_chat_dashboard).
+ setIcon(com.android.internal.R.drawable.ic_menu_select_all).
setAlphabeticShortcut('a');
atLeastOne = true;
}
if (canCut()) {
menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
- setIcon(com.android.internal.R.drawable.ic_menu_compose).
+ setIcon(com.android.internal.R.drawable.ic_menu_cut).
setAlphabeticShortcut('x');
atLeastOne = true;
}
if (canCopy()) {
menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
- setIcon(com.android.internal.R.drawable.ic_menu_attachment).
+ setIcon(com.android.internal.R.drawable.ic_menu_copy).
setAlphabeticShortcut('c');
atLeastOne = true;
}
if (canPaste()) {
menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
- setIcon(com.android.internal.R.drawable.ic_menu_camera).
+ setIcon(com.android.internal.R.drawable.ic_menu_paste).
setAlphabeticShortcut('v');
atLeastOne = true;
}
@@ -7732,6 +7755,8 @@
private int mPositionY;
private CursorController mController;
private boolean mIsDragging;
+ private int mOffsetX;
+ private int mOffsetY;
public HandleView(CursorController controller, Drawable handle) {
super(TextView.this.mContext);
@@ -7740,6 +7765,8 @@
mContainer = new PopupWindow(TextView.this.mContext, null,
com.android.internal.R.attr.textSelectHandleWindowStyle);
mContainer.setSplitTouchEnabled(true);
+ mContainer.setClippingEnabled(false);
+ mContainer.setLayoutInScreenEnabled(true);
}
@Override
@@ -7777,19 +7804,18 @@
final int compoundPaddingRight = getCompoundPaddingRight();
final TextView hostView = TextView.this;
- final int right = hostView.mRight;
- final int left = hostView.mLeft;
- final int bottom = hostView.mBottom;
- final int top = hostView.mTop;
+ final int handleWidth = mDrawable.getIntrinsicWidth();
+ final int left = 0;
+ final int right = hostView.getWidth();
+ final int top = 0;
+ final int bottom = hostView.getHeight();
- final int clipLeft = left + compoundPaddingLeft;
+ final int clipLeft = left + compoundPaddingLeft - (int) (handleWidth * 0.75f);
final int clipTop = top + extendedPaddingTop;
- final int clipRight = right - compoundPaddingRight;
+ final int clipRight = right - compoundPaddingRight + (int) (handleWidth * 0.25f);
final int clipBottom = bottom - extendedPaddingBottom;
- final int handleWidth = mDrawable.getIntrinsicWidth();
- return mPositionX >= clipLeft - handleWidth * 0.75f &&
- mPositionX <= clipRight + handleWidth * 0.25f &&
+ return mPositionX >= clipLeft && mPositionX <= clipRight &&
mPositionY >= clipTop && mPositionY <= clipBottom;
}
@@ -7828,6 +7854,8 @@
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mOffsetX = (int) (ev.getX() - mDrawable.getIntrinsicWidth() / 2.f + 0.5f);
+ mOffsetY = (int) (ev.getY() - mDrawable.getIntrinsicHeight() / 2.f + 0.5f);
mIsDragging = true;
break;
@@ -7836,8 +7864,10 @@
final float rawY = ev.getRawY();
final int[] coords = mTempCoords;
TextView.this.getLocationOnScreen(coords);
- final int x = (int) (rawX - coords[0] + 0.5f);
- final int y = (int) (rawY - coords[1] + 0.5f);
+ final int x = (int) (rawX - coords[0] + 0.5f) - mOffsetX;
+ final int y = (int) (rawY - coords[1] + 0.5f) -
+ (int) (mDrawable.getIntrinsicHeight() * 0.8f) - mOffsetY;
+
mController.updatePosition(this, x, y);
break;
@@ -8146,7 +8176,7 @@
final int previousLine = layout.getLineForOffset(previousOffset);
final int previousLineTop = layout.getLineTop(previousLine);
final int previousLineBottom = layout.getLineBottom(previousLine);
- final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 2;
+ final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 6;
// If new line is just before or after previous line and y position is less than
// hysteresisThreshold away from previous line, keep cursor on previous line.
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 6e11cff..2a8cd94 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -180,7 +180,7 @@
Log.d(TAG, "Found gdbserver: " + entry.getName());
}
- final String installGdbServerPath = APK_LIB + GDBSERVER;
+ final String installGdbServerPath = GDBSERVER;
nativeFiles.add(Pair.create(entry, installGdbServerPath));
return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 4da74e6..d5213db 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,11 +16,14 @@
package com.android.internal.view;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.view.DragEvent;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.KeyEvent;
@@ -66,7 +69,10 @@
}
}
}
-
+
+ public void dispatchDragEvent(DragEvent event) {
+ }
+
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
if (sync) {
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index b782766..cafceab 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -27,6 +27,8 @@
#include "SkMatrix.h"
#include "SkTemplates.h"
+#include "Matrix.h"
+
namespace android {
class SkMatrixGlue {
@@ -403,10 +405,20 @@
{"native_equals", "(II)Z", (void*) SkMatrixGlue::equals}
};
+static jfieldID sNativeInstanceField;
+
int register_android_graphics_Matrix(JNIEnv* env) {
int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Matrix", methods,
sizeof(methods) / sizeof(methods[0]));
+
+ jclass clazz = env->FindClass("android/graphics/Matrix");
+ sNativeInstanceField = env->GetFieldID(clazz, "native_instance", "I");
+
return result;
}
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
+ return reinterpret_cast<SkMatrix*>(env->GetIntField(matrixObj, sNativeInstanceField));
+}
+
}
diff --git a/core/jni/android/graphics/Matrix.h b/core/jni/android/graphics/Matrix.h
new file mode 100644
index 0000000..31edf88
--- /dev/null
+++ b/core/jni/android/graphics/Matrix.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_GRAPHICS_MATRIX_H
+#define _ANDROID_GRAPHICS_MATRIX_H
+
+#include "jni.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+/* Gets the underlying SkMatrix from a Matrix object. */
+extern SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_MATRIX_H
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 93fd54f..537ac72 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,10 +22,26 @@
#include <utils/Log.h>
#include <ui/Input.h>
#include "android_view_MotionEvent.h"
+#include "android/graphics/Matrix.h"
+
+#include <math.h>
+#include "SkMatrix.h"
+#include "SkScalar.h"
// Number of float items per entry in a DVM sample data array
#define NUM_SAMPLE_DATA 9
+#define SAMPLE_X 0
+#define SAMPLE_Y 1
+#define SAMPLE_PRESSURE 2
+#define SAMPLE_SIZE 3
+#define SAMPLE_TOUCH_MAJOR 4
+#define SAMPLE_TOUCH_MINOR 5
+#define SAMPLE_TOOL_MAJOR 6
+#define SAMPLE_TOOL_MINOR 7
+#define SAMPLE_ORIENTATION 8
+
+
namespace android {
// ----------------------------------------------------------------------------
@@ -238,8 +254,87 @@
}
}
+static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
+ // Construct and transform a vector oriented at the specified clockwise angle from vertical.
+ // Coordinate system: down is increasing Y, right is increasing X.
+ SkPoint vector;
+ vector.fX = SkFloatToScalar(sinf(angleRadians));
+ vector.fY = SkFloatToScalar(- cosf(angleRadians));
+ matrix->mapVectors(& vector, 1);
+
+ // Derive the transformed vector's clockwise angle from vertical.
+ float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(- vector.fY));
+ if (result < - M_PI_2) {
+ result += M_PI;
+ } else if (result > M_PI_2) {
+ result -= M_PI;
+ }
+ return result;
+}
+
+static void android_view_MotionEvent_nativeTransform(JNIEnv* env,
+ jobject eventObj, jobject matrixObj) {
+ SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
+
+ jfloat oldXOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
+ jfloat oldYOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
+ jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
+ jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
+ jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mDataSamples));
+ jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
+
+ // The tricky part of this implementation is to preserve the value of
+ // rawX and rawY. So we apply the transformation to the first point
+ // then derive an appropriate new X/Y offset that will preserve rawX and rawY.
+ SkPoint point;
+ jfloat rawX = dataSamples[SAMPLE_X];
+ jfloat rawY = dataSamples[SAMPLE_Y];
+ matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
+ & point);
+ jfloat newX = SkScalarToFloat(point.fX);
+ jfloat newY = SkScalarToFloat(point.fY);
+ jfloat newXOffset = newX - rawX;
+ jfloat newYOffset = newY - rawY;
+
+ dataSamples[SAMPLE_ORIENTATION] = transformAngle(matrix, dataSamples[SAMPLE_ORIENTATION]);
+
+ // Apply the transformation to all samples.
+ jfloat* currentDataSample = dataSamples;
+ jfloat* endDataSample = dataSamples + numPointers * numSamples * NUM_SAMPLE_DATA;
+ for (;;) {
+ currentDataSample += NUM_SAMPLE_DATA;
+ if (currentDataSample == endDataSample) {
+ break;
+ }
+
+ jfloat x = currentDataSample[SAMPLE_X] + oldXOffset;
+ jfloat y = currentDataSample[SAMPLE_Y] + oldYOffset;
+ matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
+ currentDataSample[SAMPLE_X] = SkScalarToFloat(point.fX) - newXOffset;
+ currentDataSample[SAMPLE_Y] = SkScalarToFloat(point.fY) - newYOffset;
+
+ currentDataSample[SAMPLE_ORIENTATION] = transformAngle(matrix,
+ currentDataSample[SAMPLE_ORIENTATION]);
+ }
+
+ env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
+
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, newXOffset);
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, newYOffset);
+
+ env->DeleteLocalRef(dataSampleArray);
+}
+
// ----------------------------------------------------------------------------
+static JNINativeMethod gMotionEventMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeTransform",
+ "(Landroid/graphics/Matrix;)V",
+ (void*)android_view_MotionEvent_nativeTransform },
+};
+
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -258,6 +353,10 @@
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_view_MotionEvent(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/MotionEvent",
+ gMotionEventMethods, NELEM(gMotionEventMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy.png b/core/res/res/drawable-hdpi/ic_menu_copy.png
new file mode 100644
index 0000000..8f11153
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut.png b/core/res/res/drawable-hdpi/ic_menu_cut.png
new file mode 100644
index 0000000..6ad379e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste.png b/core/res/res/drawable-hdpi/ic_menu_paste.png
new file mode 100644
index 0000000..5a3850f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_select_all.png b/core/res/res/drawable-hdpi/ic_menu_select_all.png
new file mode 100644
index 0000000..dde6741
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_select_all.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy.png b/core/res/res/drawable-mdpi/ic_menu_copy.png
new file mode 100644
index 0000000..89d626f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut.png b/core/res/res/drawable-mdpi/ic_menu_cut.png
new file mode 100644
index 0000000..1b4733e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste.png b/core/res/res/drawable-mdpi/ic_menu_paste.png
new file mode 100755
index 0000000..cdf7ca3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_select_all.png b/core/res/res/drawable-mdpi/ic_menu_select_all.png
new file mode 100644
index 0000000..37fd3cbd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_select_all.png
Binary files differ
diff --git a/core/res/res/menu/webview_copy.xml b/core/res/res/menu/webview_copy.xml
index 224f54f..adba563 100644
--- a/core/res/res/menu/webview_copy.xml
+++ b/core/res/res/menu/webview_copy.xml
@@ -16,7 +16,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/copy"
- android:icon="@drawable/ic_menu_attachment"
+ android:icon="@drawable/ic_menu_copy"
android:showAsAction="always"
/>
<item android:id="@+id/share"
@@ -24,7 +24,7 @@
android:showAsAction="always"
/>
<item android:id="@+id/select_all"
- android:icon="@drawable/ic_menu_chat_dashboard"
+ android:icon="@drawable/ic_menu_select_all"
android:showAsAction="always"
/>
<item android:id="@+id/find"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b618756..830fb01 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -71,20 +71,6 @@
<item>@drawable/indicator_code_lock_point_area_default</item>
<item>@drawable/indicator_code_lock_point_area_green</item>
<item>@drawable/indicator_code_lock_point_area_red</item>
- <!-- SlidingTab drawables shared by InCallScreen and LockScreen -->
- <item>@drawable/jog_tab_bar_left_end_confirm_gray</item>
- <item>@drawable/jog_tab_bar_left_end_normal</item>
- <item>@drawable/jog_tab_bar_left_end_pressed</item>
- <item>@drawable/jog_tab_bar_right_end_confirm_gray</item>
- <item>@drawable/jog_tab_bar_right_end_normal</item>
- <item>@drawable/jog_tab_bar_right_end_pressed</item>
- <item>@drawable/jog_tab_left_confirm_gray</item>
- <item>@drawable/jog_tab_left_normal</item>
- <item>@drawable/jog_tab_left_pressed</item>
- <item>@drawable/jog_tab_right_confirm_gray</item>
- <item>@drawable/jog_tab_right_normal</item>
- <item>@drawable/jog_tab_right_pressed</item>
- <item>@drawable/jog_tab_target_gray</item>
</array>
<!-- Do not translate. These are all of the color state list resources that should be
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
old mode 100644
new mode 100755
index 7a9b59a..8b4f91f
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -387,8 +387,10 @@
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_storage">Storage</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <string name="permgroupdesc_storage" product="nosdcard">Access the shared storage.</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgroupdesc_storage">Access the SD card.</string>
+ <string name="permgroupdesc_storage" product="default">Access the SD card.</string>
<!-- Permissions -->
@@ -1230,10 +1232,14 @@
<string name="permdesc_writeDictionary">Allows an application to write new words into the
user dictionary.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <string name="permlab_sdcardWrite" product="nosdcard">modify/delete shared storage contents</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardWrite">modify/delete SD card contents</string>
+ <string name="permlab_sdcardWrite" product="default">modify/delete SD card contents</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <string name="permdesc_sdcardWrite" product="nosdcard">Allows an application to write to the shared storage.</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardWrite">Allows an application to write to the SD card.</string>
+ <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</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_cache_filesystem">access the cache filesystem</string>
@@ -1243,31 +1249,29 @@
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
- <string name="policylab_limitPassword">Limit password</string>
+ <string name="policylab_limitPassword">Set password rules</string>
<!-- Description of policy access to limiting the user's password choices -->
- <string name="policydesc_limitPassword">Restrict the types of passwords you
- are allowed to use.</string>
+ <string name="policydesc_limitPassword">Control the length and the characters
+ allowed in screen-unlock passwords</string>
<!-- Title of policy access to watch user login attempts -->
- <string name="policylab_watchLogin">Watch login attempts</string>
+ <string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
<!-- Description of policy access to watch user login attempts -->
- <string name="policydesc_watchLogin">Monitor failed attempts to login to
- the device, to perform some action.</string>
+ <string name="policydesc_watchLogin">Monitor the number of incorrect passwords
+ entered when unlocking the screen, and lock the phone or erase all the phone\'s
+ data if too many incorrect passwords are entered</string>
<!-- Title of policy access to reset user's password -->
- <string name="policylab_resetPassword">Reset password</string>
+ <string name="policylab_resetPassword">Change the screen-unlock password</string>
<!-- Description of policy access to reset user's password -->
- <string name="policydesc_resetPassword">Force your password
- to a new value, requiring the administrator give it to you
- before you can log in.</string>
+ <string name="policydesc_resetPassword">Change the screen-unlock password</string>
<!-- Title of policy access to force lock the device -->
- <string name="policylab_forceLock">Force lock</string>
+ <string name="policylab_forceLock">Lock the screen</string>
<!-- Description of policy access to limiting the user's password choices -->
- <string name="policydesc_forceLock">Control when device locks,
- requiring you re-enter its password.</string>
+ <string name="policydesc_forceLock">Control how and when the screen locks</string>
<!-- Title of policy access to wipe the user's data -->
<string name="policylab_wipeData">Erase all data</string>
<!-- Description of policy access to wipe the user's data -->
- <string name="policydesc_wipeData">Perform a factory reset, deleting
- all of your data without any confirmation.</string>
+ <string name="policydesc_wipeData">Erase the phone\'s data without warning,
+ by performing a factory data reset</string>
<string name="policylab_setGlobalProxy">Set the device global proxy</string>
<!-- Description of policy access to wipe the user's data -->
<string name="policydesc_setGlobalProxy">Set the device global proxy
@@ -2115,12 +2119,16 @@
<!-- See USB_STORAGE. USB_STORAGE_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to mount. This is the title. -->
<string name="usb_storage_title">USB connected</string>
+ <!-- See USB_STORAGE. This is the message. [CHAR LIMIT=NONE] -->
+ <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s shared storage.</string>
<!-- See USB_STORAGE. This is the message. -->
- <string name="usb_storage_message">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
+ <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
<!-- See USB_STORAGE. This is the button text to mount the phone on the computer. -->
<string name="usb_storage_button_mount">Turn on USB storage</string>
+ <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. [CHAR LIMIT=NONE] -->
+ <string name="usb_storage_error_message" product="nosdcard">There is a problem using your shared storage for USB storage.</string>
<!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. -->
- <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
+ <string name="usb_storage_error_message" product="default">There is a problem using your SD card for USB storage.</string>
<!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title -->
<string name="usb_storage_notification_title">USB connected</string>
<!-- See USB_STORAGE. This is the message. -->
@@ -2135,8 +2143,10 @@
<!-- This is the label for the activity, and should never be visible to the user. -->
<!-- See USB_STORAGE_STOP. USB_STORAGE_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop usb storage. This is the title. -->
<string name="usb_storage_stop_title">USB storage in use</string>
+ <!-- See USB_STORAGE_STOP. This is the message. [CHAR LIMIT=NONE] -->
+ <string name="usb_storage_stop_message" product="nosdcard">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s shared storage from your computer.</string>
<!-- See USB_STORAGE_STOP. This is the message. -->
- <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
+ <string name="usb_storage_stop_message" product="default">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
<!-- See USB_STORAGE_STOP. This is the button text to stop usb storage. -->
<string name="usb_storage_stop_button_mount">Turn off USB storage</string>
<!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. -->
@@ -2153,10 +2163,14 @@
<!-- External media format dialog strings -->
<!-- This is the label for the activity, and should never be visible to the user. -->
+ <!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. [CHAR LIMIT=20] -->
+ <string name="extmedia_format_title" product="nosdcard">Format shared storage</string>
<!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. -->
- <string name="extmedia_format_title">Format SD card</string>
+ <string name="extmedia_format_title" product="default">Format SD card</string>
+ <!-- See EXTMEDIA_FORMAT. This is the message. [CHAR LIMIT=NONE] -->
+ <string name="extmedia_format_message" product="nosdcard">Format shared storage, erasing all files stored there? Action cannot be reversed!</string>
<!-- See EXTMEDIA_FORMAT. This is the message. -->
- <string name="extmedia_format_message">Are you sure you want to format the SD card? All data on your card will be lost.</string>
+ <string name="extmedia_format_message" product="default">Are you sure you want to format the SD card? All data on your card will be lost.</string>
<!-- See EXTMEDIA_FORMAT. This is the button text to format the sd card. -->
<string name="extmedia_format_button_format">Format</string>
@@ -2181,29 +2195,51 @@
<string name="candidates_style"><u>candidates</u></string>
<!-- External media notification strings -->
+ <!-- Shown when external media is being checked [CHAR LIMIT=30] -->
+ <string name="ext_media_checking_notification_title" product="nosdcard">Preparing shared storage</string>
<!-- Shown when external media is being checked -->
- <string name="ext_media_checking_notification_title">Preparing SD card</string>
+ <string name="ext_media_checking_notification_title" product="default">Preparing SD card</string>
<string name="ext_media_checking_notification_message">Checking for errors.</string>
+ <!-- Shown when external media is blank (or unsupported filesystem) [CHAR LIMIT=30] -->
+ <string name="ext_media_nofs_notification_title" product="nosdcard">Blank shared storage</string>
<!-- Shown when external media is blank (or unsupported filesystem) -->
- <string name="ext_media_nofs_notification_title">Blank SD card</string>
- <string name="ext_media_nofs_notification_message">SD card blank or has unsupported filesystem.</string>
+ <string name="ext_media_nofs_notification_title" product="default">Blank SD card</string>
+ <!-- Shown when shared storage cannot be read. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_nofs_notification_message" product="nosdcard">Shared storage blank or has unsupported filesystem.</string>
+ <string name="ext_media_nofs_notification_message" product="default">SD card blank or has unsupported filesystem.</string>
+ <!-- Shown when external media is unmountable (corrupt)) [CHAR LIMIT=30] -->
+ <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged shared storage</string>
<!-- Shown when external media is unmountable (corrupt)) -->
- <string name="ext_media_unmountable_notification_title">Damaged SD card</string>
- <string name="ext_media_unmountable_notification_message">SD card damaged. You may have to reformat it.</string>
+ <string name="ext_media_unmountable_notification_title" product="default">Damaged SD card</string>
+ <!-- Shown when shared storage cannot be read. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_unmountable_notification_message" product="nosdcard">Shared storage damaged. You may have to reformat it.</string>
+ <string name="ext_media_unmountable_notification_message" product="default">SD card damaged. You may have to reformat it.</string>
+ <!-- Shown when external media is unsafely removed [CHAR LIMIT=30] -->
+ <string name="ext_media_badremoval_notification_title" product="nosdcard">Shared storage unexpectedly removed</string>
<!-- Shown when external media is unsafely removed -->
- <string name="ext_media_badremoval_notification_title">SD card unexpectedly removed</string>
- <string name="ext_media_badremoval_notification_message">Unmount SD card before removing to avoid data loss.</string>
+ <string name="ext_media_badremoval_notification_title" product="default">SD card unexpectedly removed</string>
+ <!-- Shown when external media is unsafely removed. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount shared storage before removing to avoid data loss.</string>
+ <string name="ext_media_badremoval_notification_message" product="default">Unmount SD card before removing to avoid data loss.</string>
+ <!-- Shown when external media has been safely removed [CHAR LIMIT=30] -->
+ <string name="ext_media_safe_unmount_notification_title" product="nosdcard">Shared storage safe to remove</string>
<!-- Shown when external media has been safely removed -->
- <string name="ext_media_safe_unmount_notification_title">SD card safe to remove</string>
- <string name="ext_media_safe_unmount_notification_message">You can safely remove SD card.</string>
+ <string name="ext_media_safe_unmount_notification_title" product="default">SD card safe to remove</string>
+ <!-- Shown when external media has been safely removed. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove shared storage.</string>
+ <string name="ext_media_safe_unmount_notification_message" product="default">You can safely remove SD card.</string>
+ <!-- Shown when external media is missing [CHAR LIMIT=30] -->
+ <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed shared storage</string>
<!-- Shown when external media is missing -->
- <string name="ext_media_nomedia_notification_title">Removed SD card</string>
- <string name="ext_media_nomedia_notification_message">SD card removed. Insert a new one.</string>
+ <string name="ext_media_nomedia_notification_title" product="default">Removed SD card</string>
+ <!-- Shown when external media is missing. [CHAR LIMIT=NONE] -->
+ <string name="ext_media_nomedia_notification_message" product="nosdcard">Shared storage removed. Insert new media.</string>
+ <string name="ext_media_nomedia_notification_message" product="default">SD card removed. Insert a new one.</string>
<!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. -->
<string name="activity_list_empty">No matching activities found</string>
diff --git a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
index 38f336e..27eea4d 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
@@ -120,7 +120,7 @@
Cursor cursor = getCursor(dlRequest);
try {
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, error);
+ verifyInt(cursor, DownloadManager.COLUMN_REASON, error);
} finally {
cursor.close();
}
@@ -183,7 +183,7 @@
Cursor cursor = getCursor(dlRequest);
try {
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
DownloadManager.ERROR_CANNOT_RESUME);
} finally {
cursor.close();
@@ -269,7 +269,7 @@
try {
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
DownloadManager.ERROR_FILE_ERROR);
} finally {
cursor.close();
@@ -476,7 +476,7 @@
// For the last download we should have failed b/c there is not enough space left in cache
Cursor cursor = getCursor(dlRequest);
try {
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
DownloadManager.ERROR_INSUFFICIENT_SPACE);
} finally {
cursor.close();
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 4ff0295..ddf138f 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -145,7 +145,7 @@
cursor = getCursor(dlRequest);
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
- verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+ verifyInt(cursor, DownloadManager.COLUMN_REASON,
DownloadManager.ERROR_INSUFFICIENT_SPACE);
} finally {
if (cursor != null) {
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 1beba53..e111662 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -30,7 +30,6 @@
import android.text.util.Rfc822Tokenizer;
import android.test.MoreAsserts;
-import com.android.common.Rfc822Validator;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -238,39 +237,6 @@
}
}
- //==============================================================================================
- // Email validator
- //==============================================================================================
-
- @SmallTest
- public void testEmailValidator() {
- Rfc822Validator validator = new Rfc822Validator("gmail.com");
- String[] validEmails = new String[] {
- "a@b.com", "a@b.fr", "a+b@c.com", "a@b.info",
- };
-
- for (String email : validEmails) {
- assertTrue(email + " should be a valid email address", validator.isValid(email));
- }
-
- String[] invalidEmails = new String[] {
- "a", "a@b", "a b", "a@b.12"
- };
-
- for (String email : invalidEmails) {
- assertFalse(email + " should not be a valid email address", validator.isValid(email));
- }
-
- Map<String, String> fixes = Maps.newHashMap();
- fixes.put("a", "<a@gmail.com>");
- fixes.put("a b", "<ab@gmail.com>");
- fixes.put("a@b", "<a@b>");
-
- for (Map.Entry<String, String> e : fixes.entrySet()) {
- assertEquals(e.getValue(), validator.fixText(e.getKey()).toString());
- }
- }
-
@SmallTest
public void testRfc822TokenizerFullAddress() {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo@google.com>");
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c3416a0..1324431 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -236,6 +236,7 @@
* @param y The y-coordinate of the end of a line
*/
public void lineTo(float x, float y) {
+ isSimplePath = false;
native_lineTo(mNativePath, x, y);
}
@@ -250,6 +251,7 @@
* this contour, to specify a line
*/
public void rLineTo(float dx, float dy) {
+ isSimplePath = false;
native_rLineTo(mNativePath, dx, dy);
}
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 05b2d60..dd9fa15 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -39,7 +39,7 @@
public enum DataType {
//FLOAT_16 (1, 2),
FLOAT_32 (2, 4),
- //FLOAT_64 (3, 8),
+ FLOAT_64 (3, 8),
SIGNED_8 (4, 1),
SIGNED_16 (5, 2),
SIGNED_32 (6, 4),
@@ -149,6 +149,13 @@
return rs.mElement_F32;
}
+ public static Element F64(RenderScript rs) {
+ if(rs.mElement_F64 == null) {
+ rs.mElement_F64 = createUser(rs, DataType.FLOAT_64);
+ }
+ return rs.mElement_F64;
+ }
+
public static Element ELEMENT(RenderScript rs) {
if(rs.mElement_ELEMENT == null) {
rs.mElement_ELEMENT = createUser(rs, DataType.RS_ELEMENT);
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index b6f88be..ff3e22b 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -124,7 +124,7 @@
addI32(Float.floatToRawIntBits(v));
}
- public void addF64(float v) {
+ public void addF64(double v) {
addI64(Double.doubleToRawLongBits(v));
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index c4421c3..c952d79 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -388,6 +388,10 @@
synchronized void nSamplerSet(int param, int value) {
rsnSamplerSet(mContext, param, value);
}
+ native void rsnSamplerSet2(int con, int param, float value);
+ synchronized void nSamplerSet2(int param, float value) {
+ rsnSamplerSet2(mContext, param, value);
+ }
native int rsnSamplerCreate(int con);
synchronized int nSamplerCreate() {
return rsnSamplerCreate(mContext);
@@ -498,6 +502,7 @@
Element mElement_U32;
Element mElement_I32;
Element mElement_F32;
+ Element mElement_F64;
Element mElement_BOOLEAN;
Element mElement_ELEMENT;
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index 343fcdb..b627207 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -130,6 +130,7 @@
Value mWrapS;
Value mWrapT;
Value mWrapR;
+ float mAniso;
public Builder(RenderScript rs) {
mRS = rs;
@@ -138,6 +139,7 @@
mWrapS = Value.WRAP;
mWrapT = Value.WRAP;
mWrapR = Value.WRAP;
+ mAniso = 1.0f;
}
public void setMin(Value v) {
@@ -182,6 +184,14 @@
}
}
+ public void setAnisotropy(float v) {
+ if(v >= 0.0f) {
+ mAniso = v;
+ } else {
+ throw new IllegalArgumentException("Invalid value");
+ }
+ }
+
static synchronized Sampler internalCreate(RenderScript rs, Builder b) {
rs.nSamplerBegin();
rs.nSamplerSet(0, b.mMin.mID);
@@ -189,6 +199,7 @@
rs.nSamplerSet(2, b.mWrapS.mID);
rs.nSamplerSet(3, b.mWrapT.mID);
rs.nSamplerSet(4, b.mWrapR.mID);
+ rs.nSamplerSet2(5, b.mAniso);
int id = rs.nSamplerCreate();
return new Sampler(id, rs);
}
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 6aed11b..67a2b63 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -1096,6 +1096,13 @@
rsSamplerSet(con, (RsSamplerParam)p, (RsSamplerValue)v);
}
+static void
+nSamplerSet2(JNIEnv *_env, jobject _this, RsContext con, jint p, jfloat v)
+{
+ LOG_API("nSamplerSet2, con(%p), param(%i), value(%f)", con, p, v);
+ rsSamplerSet2(con, (RsSamplerParam)p, v);
+}
+
static jint
nSamplerCreate(JNIEnv *_env, jobject _this, RsContext con)
{
@@ -1303,6 +1310,7 @@
{"rsnSamplerBegin", "(I)V", (void*)nSamplerBegin },
{"rsnSamplerSet", "(III)V", (void*)nSamplerSet },
+{"rsnSamplerSet2", "(IIF)V", (void*)nSamplerSet2 },
{"rsnSamplerCreate", "(I)I", (void*)nSamplerCreate },
{"rsnMeshCreate", "(III)I", (void*)nMeshCreate },
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 1510f87..c6990bf 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -42,8 +42,11 @@
#define CBLK_FORCEREADY_ON 0x0004 // track is considered ready immediately by AudioFlinger
#define CBLK_FORCEREADY_OFF 0x0000 // track is ready when buffer full
#define CBLK_INVALID_MSK 0x0008
-#define CBLK_INVALID_ON 0x0008 // track buffer is invalidated by AudioFlinger: must be re-created
-#define CBLK_INVALID_OFF 0x0000
+#define CBLK_INVALID_ON 0x0008 // track buffer is invalidated by AudioFlinger:
+#define CBLK_INVALID_OFF 0x0000 // must be re-created
+#define CBLK_DISABLED_MSK 0x0010
+#define CBLK_DISABLED_ON 0x0010 // track disabled by AudioFlinger due to underrun:
+#define CBLK_DISABLED_OFF 0x0000 // must be re-started
struct audio_track_cblk_t
{
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 76307b2..6533600 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -115,7 +115,8 @@
*/
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth, uint32_t reqHeight) = 0;
/* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call.
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8773d71..a80832d 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -170,6 +170,36 @@
};
// ---------------------------------------------------------------------------
+
+class ScreenshotClient
+{
+ sp<IMemoryHeap> mHeap;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ PixelFormat mFormat;
+public:
+ ScreenshotClient();
+
+ // frees the previous screenshot and capture a new one
+ status_t update();
+ status_t update(uint32_t reqWidth, uint32_t reqHeight);
+
+ // release memory occupied by the screenshot
+ void release();
+
+ // pixels are valid until this object is freed or
+ // release() or update() is called
+ void const* getPixels() const;
+
+ uint32_t getWidth() const;
+ uint32_t getHeight() const;
+ PixelFormat getFormat() const;
+ uint32_t getStride() const;
+ // size of allocated memory in bytes
+ size_t getSize() const;
+};
+
+// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index e85735a..d3fbaf7 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -308,9 +308,6 @@
GetStateFunc getStateFunc);
bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
-
- // dump state
- void dumpDeviceInfo(String8& dump);
};
@@ -340,6 +337,7 @@
inline bool isIgnored() { return mMappers.isEmpty(); }
+ void dump(String8& dump);
void addMapper(InputMapper* mapper);
void configure();
void reset();
@@ -393,6 +391,7 @@
virtual uint32_t getSources() = 0;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(String8& dump);
virtual void configure();
virtual void reset();
virtual void process(const RawEvent* rawEvent) = 0;
@@ -436,6 +435,7 @@
virtual uint32_t getSources();
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(String8& dump);
virtual void reset();
virtual void process(const RawEvent* rawEvent);
@@ -484,6 +484,7 @@
virtual uint32_t getSources();
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(String8& dump);
virtual void reset();
virtual void process(const RawEvent* rawEvent);
@@ -540,6 +541,7 @@
virtual uint32_t getSources();
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(String8& dump);
virtual void configure();
virtual void reset();
@@ -761,15 +763,16 @@
} mLocked;
virtual void configureParameters();
- virtual void logParameters();
+ virtual void dumpParameters(String8& dump);
virtual void configureRawAxes();
- virtual void logRawAxes();
+ virtual void dumpRawAxes(String8& dump);
virtual bool configureSurfaceLocked();
- virtual void logMotionRangesLocked();
+ virtual void dumpSurfaceLocked(String8& dump);
virtual void configureVirtualKeysLocked();
+ virtual void dumpVirtualKeysLocked(String8& dump);
virtual void parseCalibration();
virtual void resolveCalibration();
- virtual void logCalibration();
+ virtual void dumpCalibration(String8& dump);
enum TouchResult {
// Dispatch the touch normally.
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 60523db..a0cc5d6 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -44,7 +44,7 @@
uint32_t id;
bool operator<(const LayerSize& rhs) const {
- if (id != 0 && rhs.id != 0) {
+ if (id != 0 && rhs.id != 0 && id != rhs.id) {
return id < rhs.id;
}
if (width == rhs.width) {
@@ -54,7 +54,7 @@
}
bool operator==(const LayerSize& rhs) const {
- return width == rhs.width && height == rhs.height;
+ return id == rhs.id && width == rhs.width && height == rhs.height;
}
}; // struct LayerSize
@@ -83,7 +83,7 @@
*/
bool blend;
/**
- * Indicates that this layer has never been used before.
+ * Indicates whether this layer has been used already.
*/
bool empty;
}; // struct Layer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 2770868..8c70cf9 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -114,6 +114,8 @@
glGenTextures(1, &layer->texture);
glBindTexture(GL_TEXTURE_2D, layer->texture);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 23de3a5..0810fb8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -366,6 +366,8 @@
return false;
}
+ glActiveTexture(GL_TEXTURE0);
+
LayerSize size(bounds.getWidth(), bounds.getHeight());
Layer* layer = mCaches.layerCache.get(size);
if (!layer) {
@@ -383,17 +385,22 @@
// Copy the framebuffer into the layer
glBindTexture(GL_TEXTURE_2D, layer->texture);
- if (layer->empty) {
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
- bounds.getWidth(), bounds.getHeight(), 0);
- layer->empty = false;
- } else {
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom,
- bounds.getWidth(), bounds.getHeight());
- }
+ // TODO: Workaround for b/3054204
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+ bounds.getWidth(), bounds.getHeight(), 0);
- if (flags & SkCanvas::kClipToLayer_SaveFlag) {
- if (mSnapshot->clipTransformed(bounds)) setScissorFromClip();
+ // TODO: Waiting for b/3054204 to be fixed
+// if (layer->empty) {
+// glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+// bounds.getWidth(), bounds.getHeight(), 0);
+// layer->empty = false;
+// } else {
+// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom,
+// bounds.getWidth(), bounds.getHeight());
+// }
+
+ if (flags & SkCanvas::kClipToLayer_SaveFlag && mSnapshot->clipTransformed(bounds)) {
+ setScissorFromClip();
}
// Enqueue the buffer coordinates to clear the corresponding region later
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 70e06a1..377727b 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -138,8 +138,8 @@
const SkPath *path, const SkPaint* paint) {
const SkRect& bounds = path->getBounds();
- const float pathWidth = bounds.width();
- const float pathHeight = bounds.height();
+ const float pathWidth = fmax(bounds.width(), 1.0f);
+ const float pathHeight = fmax(bounds.height(), 1.0f);
if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
LOGW("Path too large to be rendered into a texture");
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 7902b9a..13ae1fb 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -122,7 +122,8 @@
RS_SAMPLER_MAG_FILTER,
RS_SAMPLER_WRAP_S,
RS_SAMPLER_WRAP_T,
- RS_SAMPLER_WRAP_R
+ RS_SAMPLER_WRAP_R,
+ RS_SAMPLER_ANISO
};
enum RsSamplerValue {
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
index 33945a5..f5fecba 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -65,7 +65,6 @@
static void copyInput() {
- RS_DEBUG_MARKER;
rs_allocation ain = rsGetAllocation(InPixel);
uint32_t dimx = rsAllocationGetDimX(ain);
uint32_t dimy = rsAllocationGetDimY(ain);
@@ -74,7 +73,6 @@
ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
}
}
- RS_DEBUG_MARKER;
}
void filter() {
diff --git a/libs/rs/java/Samples/res/drawable/checker.png b/libs/rs/java/Samples/res/drawable/checker.png
new file mode 100644
index 0000000..b631e1e
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/checker.png
Binary files differ
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index d4e83d3..a15c4a1 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -19,11 +19,12 @@
import java.io.Writer;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.renderscript.*;
import android.renderscript.ProgramStore.DepthFunc;
+import android.renderscript.Sampler.Value;
import android.util.Log;
-import android.graphics.BitmapFactory;
-import android.graphics.Bitmap;
public class RsRenderStatesRS {
@@ -42,7 +43,7 @@
mOptionsARGB.inScaled = false;
mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
mMode = 0;
- mMaxModes = 8;
+ mMaxModes = 9;
initRS();
}
@@ -53,6 +54,8 @@
private Sampler mLinearWrap;
private Sampler mMipLinearWrap;
private Sampler mNearestClamp;
+ private Sampler mMipLinearAniso8;
+ private Sampler mMipLinearAniso15;
private ProgramStore mProgStoreBlendNoneDepth;
private ProgramStore mProgStoreBlendNone;
@@ -74,10 +77,12 @@
private ProgramRaster mCullBack;
private ProgramRaster mCullFront;
+ private ProgramRaster mCullNone;
private Allocation mTexTorus;
private Allocation mTexOpaque;
private Allocation mTexTransparent;
+ private Allocation mTexChecker;
private Allocation mAllocPV;
@@ -243,10 +248,12 @@
mTexTorus = loadTextureRGB(R.drawable.torusmap);
mTexOpaque = loadTextureRGB(R.drawable.data);
mTexTransparent = loadTextureARGB(R.drawable.leaf);
+ mTexChecker = loadTextureRGB(R.drawable.checker);
mScript.set_gTexTorus(mTexTorus);
mScript.set_gTexOpaque(mTexOpaque);
mScript.set_gTexTransparent(mTexTransparent);
+ mScript.set_gTexChecker(mTexChecker);
}
private void initFonts() {
@@ -295,18 +302,32 @@
mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
+ bs = new Sampler.Builder(mRS);
+ bs.setMin(Sampler.Value.LINEAR_MIP_LINEAR);
+ bs.setMag(Sampler.Value.LINEAR);
+ bs.setWrapS(Sampler.Value.WRAP);
+ bs.setWrapT(Sampler.Value.WRAP);
+ bs.setAnisotropy(8.0f);
+ mMipLinearAniso8 = bs.create();
+ bs.setAnisotropy(15.0f);
+ mMipLinearAniso15 = bs.create();
+
mScript.set_gLinearClamp(mLinearClamp);
mScript.set_gLinearWrap(mLinearWrap);
mScript.set_gMipLinearWrap(mMipLinearWrap);
+ mScript.set_gMipLinearAniso8(mMipLinearAniso8);
+ mScript.set_gMipLinearAniso15(mMipLinearAniso15);
mScript.set_gNearestClamp(mNearestClamp);
}
private void initProgramRaster() {
mCullBack = ProgramRaster.CULL_BACK(mRS);
mCullFront = ProgramRaster.CULL_FRONT(mRS);
+ mCullNone = ProgramRaster.CULL_NONE(mRS);
mScript.set_gCullBack(mCullBack);
mScript.set_gCullFront(mCullFront);
+ mScript.set_gCullNone(mCullNone);
}
private void initRS() {
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
index 659e1e4..b471504 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -31,6 +31,7 @@
rs_allocation gTexOpaque;
rs_allocation gTexTorus;
rs_allocation gTexTransparent;
+rs_allocation gTexChecker;
rs_mesh gMbyNMesh;
rs_mesh gTorusMesh;
@@ -47,10 +48,13 @@
rs_sampler gLinearClamp;
rs_sampler gLinearWrap;
rs_sampler gMipLinearWrap;
+rs_sampler gMipLinearAniso8;
+rs_sampler gMipLinearAniso15;
rs_sampler gNearestClamp;
rs_program_raster gCullBack;
rs_program_raster gCullFront;
+rs_program_raster gCullNone;
// Custom vertex shader compunents
VertexShaderConstants *gVSConstants;
@@ -64,11 +68,11 @@
#pragma rs export_var(gProgVertex, gProgFragmentColor, gProgFragmentTexture)
#pragma rs export_var(gProgStoreBlendNoneDepth, gProgStoreBlendNone, gProgStoreBlendAlpha, gProgStoreBlendAdd)
-#pragma rs export_var(gTexOpaque, gTexTorus, gTexTransparent)
+#pragma rs export_var(gTexOpaque, gTexTorus, gTexTransparent, gTexChecker)
#pragma rs export_var(gMbyNMesh, gTorusMesh)
#pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono)
-#pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gNearestClamp)
-#pragma rs export_var(gCullBack, gCullFront)
+#pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gMipLinearAniso8, gMipLinearAniso15, gNearestClamp)
+#pragma rs export_var(gCullBack, gCullFront, gCullNone)
#pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom, gProgFragmentMultitex)
//What we are showing
@@ -110,7 +114,7 @@
rsgBindProgramVertex(gProgVertex);
// Setup the projectioni matrix
rs_matrix4x4 proj;
- rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -1,1);
+ rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
rsgProgramVertexLoadProjectionMatrix(&proj);
}
@@ -276,14 +280,12 @@
startX + width, startY + height, 0, 1.5, 1.5,
startX + width, startY, 0, 1.5, 0);
-
rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
rsgBindFont(gFontMono);
rsgDrawText("Filtering: linear clamp", 10, 290);
rsgDrawText("Filtering: linear wrap", 10, 590);
rsgDrawText("Filtering: nearest clamp", 310, 290);
rsgDrawText("Filtering: miplinear wrap", 310, 590);
-
}
float gTorusRotation = 0;
@@ -430,7 +432,7 @@
rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
- rsgBindTexture(gProgFragmentMultitex, 0, gTexOpaque);
+ rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker);
rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
@@ -446,6 +448,69 @@
rsgDrawText("Custom shader with multitexturing", 10, 280);
}
+float gAnisoTime = 0.0f;
+uint anisoMode = 0;
+void displayAnisoSample() {
+
+ gAnisoTime += gDt;
+
+ rsgBindProgramVertex(gProgVertex);
+ float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+ rs_matrix4x4 proj;
+ rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+ rsgProgramVertexLoadProjectionMatrix(&proj);
+
+ rs_matrix4x4 matrix;
+ // Fragment shader with texture
+ rsgBindProgramStore(gProgStoreBlendNone);
+ rsgBindProgramFragment(gProgFragmentTexture);
+ rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -10.0f);
+ rsMatrixRotate(&matrix, -80, 1.0f, 0.0f, 0.0f);
+ rsgProgramVertexLoadModelMatrix(&matrix);
+
+ rsgBindProgramRaster(gCullNone);
+
+ rsgBindTexture(gProgFragmentTexture, 0, gTexChecker);
+
+ if(gAnisoTime >= 5.0f) {
+ gAnisoTime = 0.0f;
+ anisoMode ++;
+ anisoMode = anisoMode % 3;
+ }
+
+ if(anisoMode == 0) {
+ rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso8);
+ }
+ else if(anisoMode == 1) {
+ rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso15);
+ }
+ else {
+ rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+ }
+
+ float startX = -15;
+ float startY = -15;
+ float width = 30;
+ float height = 30;
+ rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+ startX, startY + height, 0, 0, 10,
+ startX + width, startY + height, 0, 10, 10,
+ startX + width, startY, 0, 10, 0);
+
+ rsgBindProgramRaster(gCullBack);
+
+ rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+ rsgBindFont(gFontMono);
+ if(anisoMode == 0) {
+ rsgDrawText("Anisotropic filtering 8", 10, 40);
+ }
+ else if(anisoMode == 1) {
+ rsgDrawText("Anisotropic filtering 15", 10, 40);
+ }
+ else {
+ rsgDrawText("Miplinear filtering", 10, 40);
+ }
+}
int root(int launchID) {
@@ -479,6 +544,9 @@
case 7:
displayMultitextureSample();
break;
+ case 8:
+ displayAnisoSample();
+ break;
}
return 10;
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index dbc9133..835dea2 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -21,6 +21,8 @@
import android.util.Log;
import java.util.ArrayList;
import java.util.ListIterator;
+import java.util.Timer;
+import java.util.TimerTask;
public class RSTestCore {
@@ -42,12 +44,18 @@
private ArrayList<UnitTest> unitTests;
private ListIterator<UnitTest> test_iter;
private UnitTest activeTest;
+ private boolean stopTesting;
+
+ /* Periodic timer for ensuring future tests get scheduled */
+ private Timer mTimer;
+ public static final int RS_TIMER_PERIOD = 100;
public void init(RenderScriptGL rs, Resources res, int width, int height) {
mRS = rs;
mRes = res;
mWidth = width;
mHeight = height;
+ stopTesting = false;
mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist, true);
@@ -88,9 +96,17 @@
test_iter = unitTests.listIterator();
refreshTestResults(); /* Kick off the first test */
+
+ TimerTask pTask = new TimerTask() {
+ public void run() {
+ refreshTestResults();
+ }
+ };
+
+ mTimer = new Timer();
+ mTimer.schedule(pTask, RS_TIMER_PERIOD, RS_TIMER_PERIOD);
}
- static int count = 0;
public void checkAndRunNextTest() {
if (activeTest != null) {
if (!activeTest.isAlive()) {
@@ -104,7 +120,7 @@
}
}
- if (activeTest == null) {
+ if (!stopTesting && activeTest == null) {
if (test_iter.hasNext()) {
activeTest = test_iter.next();
activeTest.start();
@@ -112,8 +128,14 @@
* should start running. The message handler in UnitTest.java
* ensures this. */
}
+ else {
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer.purge();
+ mTimer = null;
+ }
+ }
}
- count++;
}
public void refreshTestResults() {
@@ -127,6 +149,29 @@
}
}
+ public void cleanup() {
+ stopTesting = true;
+ UnitTest t = activeTest;
+
+ /* Stop periodic refresh of testing */
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer.purge();
+ mTimer = null;
+ }
+
+ /* Wait to exit until we finish the current test */
+ if (t != null) {
+ try {
+ t.join();
+ }
+ catch (InterruptedException e) {
+ }
+ t = null;
+ }
+
+ }
+
public void newTouchPosition(float x, float y, float pressure, int id) {
}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
index ce99c6d..b811d48 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
@@ -62,6 +62,7 @@
@Override
protected void onDetachedFromWindow() {
if(mRS != null) {
+ mRender.cleanup();
mRS = null;
destroyRenderScript();
}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
index 8391fb3..9d57e90 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
@@ -33,6 +33,7 @@
pRS.mMessageCallback = mRsMessage;
s.invoke_fp_mad_test(0, 0);
pRS.finish();
+ waitForMessage();
pRS.destroy();
}
}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
index bef6ec5..fb355dd 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
@@ -33,6 +33,7 @@
pRS.mMessageCallback = mRsMessage;
s.invoke_primitives_test(0, 0);
pRS.finish();
+ waitForMessage();
pRS.destroy();
}
}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
index 5eb0d67..c9d88a6 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
@@ -23,6 +23,7 @@
public int result;
private ScriptField_ListAllocs_s.Item mItem;
private RSTestCore mRSTC;
+ private boolean msgHandled;
/* These constants must match those in shared.rsh */
public static final int RS_MSG_TEST_PASSED = 100;
@@ -35,6 +36,7 @@
super();
mRSTC = rstc;
name = n;
+ msgHandled = false;
result = initResult;
testID = numTests++;
}
@@ -67,6 +69,7 @@
if (mItem != null) {
mItem.result = result;
+ msgHandled = true;
try {
mRSTC.refreshTestResults();
}
@@ -79,6 +82,12 @@
}
};
+ public void waitForMessage() {
+ while (!msgHandled) {
+ yield();
+ }
+ }
+
public void setItem(ScriptField_ListAllocs_s.Item item) {
mItem = item;
}
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 2b7928f..a4752f4 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -288,6 +288,11 @@
param RsSamplerValue value
}
+SamplerSet2 {
+ param RsSamplerParam p
+ param float value
+ }
+
SamplerCreate {
ret RsSampler
}
diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp
index b4ec250..ef69b75 100644
--- a/libs/rs/rsAdapter.cpp
+++ b/libs/rs/rsAdapter.cpp
@@ -183,7 +183,6 @@
uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes();
uint32_t lineSize = eSize * w;
- uint32_t destW = getDimX();
const uint8_t *src = static_cast<const uint8_t *>(data);
for (uint32_t line=yoff; line < (yoff+h); line++) {
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 87c4f2b..0356e4d 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -293,7 +293,6 @@
}
for (uint32_t line=yoff; line < (yoff+h); line++) {
- uint8_t * ptr = static_cast<uint8_t *>(mPtr);
if (mType->getElement()->getHasReferences()) {
incRefs(src, w);
decRefs(dst, w);
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index b4d5014..4752b35 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -282,18 +282,19 @@
rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms");
rsc->props.mLogVisual = getProp("debug.rs.visual");
- ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
- if (!tlsStruct) {
+ rsc->mTlsStruct = new ScriptTLSStruct;
+ if (!rsc->mTlsStruct) {
LOGE("Error allocating tls storage");
return NULL;
}
- tlsStruct->mContext = rsc;
- tlsStruct->mScript = NULL;
- int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct);
+ rsc->mTlsStruct->mContext = rsc;
+ rsc->mTlsStruct->mScript = NULL;
+ int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
if (status) {
LOGE("pthread_setspecific %i", status);
}
+ rsc->mScriptC.init(rsc);
if (rsc->mIsGraphicsContext) {
rsc->mStateRaster.init(rsc);
rsc->setRaster(NULL);
@@ -360,6 +361,7 @@
rsc->deinitEGL();
pthread_mutex_unlock(&gInitMutex);
}
+ delete rsc->mTlsStruct;
LOGV("%p, RS Thread exited", rsc);
return NULL;
@@ -386,6 +388,11 @@
#endif
setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
+ int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
+ if (status) {
+ LOGE("pthread_setspecific %i", status);
+ }
+
while(rsc->mRunning) {
rsc->mWorkers.mLaunchSignals[idx].wait();
if (rsc->mWorkers.mLaunchCallback) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 2e84930..f90e4af 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -49,6 +49,24 @@
namespace renderscript {
+#if 0
+#define CHECK_OBJ(o) { \
+ GET_TLS(); \
+ if(!ObjectBase::isValid(rsc, (const ObjectBase *)o)) { \
+ LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__); \
+ } \
+}
+#define CHECK_OBJ_OR_NULL(o) { \
+ GET_TLS(); \
+ if(o && !ObjectBase::isValid(rsc, (const ObjectBase *)o)) { \
+ LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__); \
+ } \
+}
+#else
+#define CHECK_OBJ(o)
+#define CHECK_OBJ_OR_NULL(o)
+#endif
+
class Context
{
public:
@@ -64,6 +82,7 @@
Context * mContext;
Script * mScript;
};
+ ScriptTLSStruct *mTlsStruct;
typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h
index f30915e..06298e8 100644
--- a/libs/rs/rsContextHostStub.h
+++ b/libs/rs/rsContextHostStub.h
@@ -111,6 +111,9 @@
bool mLogScripts;
bool mLogObjects;
bool mLogShaders;
+ bool mLogShadersAttr;
+ bool mLogShadersUniforms;
+ bool mLogVisual;
} props;
void dumpDebug() const { }
@@ -120,6 +123,7 @@
mutable const ObjectBase * mObjHead;
bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+ float ext_texture_max_aniso() const {return 1.0f;}
uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;}
uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;}
uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 4f8d8df..12dedac 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -743,6 +743,8 @@
{
mInitialized = false;
+ mFontShaderFConstant.clear();
+
mIndexBuffer.clear();
mVertexArray.clear();
diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp
index 48f1fee..f69cb15 100644
--- a/libs/rs/rsObjectBase.cpp
+++ b/libs/rs/rsObjectBase.cpp
@@ -61,6 +61,7 @@
if (mRSC) {
remove();
}
+ rsAssert(rsc);
mRSC = rsc;
if (rsc) {
add();
@@ -194,3 +195,15 @@
}
}
+bool ObjectBase::isValid(const Context *rsc, const ObjectBase *obj)
+{
+ const ObjectBase * o = rsc->mObjHead;
+ while (o) {
+ if (o == obj) {
+ return true;
+ }
+ o = o->mNext;
+ }
+ return false;
+}
+
diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h
index ad95b81..59fb4a6 100644
--- a/libs/rs/rsObjectBase.h
+++ b/libs/rs/rsObjectBase.h
@@ -56,6 +56,8 @@
virtual void serialize(OStream *stream) const = 0;
virtual RsA3DClassID getClassId() const = 0;
+ static bool isValid(const Context *rsc, const ObjectBase *obj);
+
protected:
const char *mAllocFile;
uint32_t mAllocLine;
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index c6a848c..180d78e 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -44,7 +44,8 @@
RsSamplerValue minFilter,
RsSamplerValue wrapS,
RsSamplerValue wrapT,
- RsSamplerValue wrapR) : ObjectBase(rsc)
+ RsSamplerValue wrapR,
+ float aniso) : ObjectBase(rsc)
{
mAllocFile = __FILE__;
mAllocLine = __LINE__;
@@ -53,6 +54,7 @@
mWrapS = wrapS;
mWrapT = wrapT;
mWrapR = wrapR;
+ mAniso = aniso;
}
Sampler::~Sampler()
@@ -93,6 +95,11 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
}
+ float anisoValue = rsMin(rsc->ext_texture_max_aniso(), mAniso);
+ if(rsc->ext_texture_max_aniso() > 1.0f) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue);
+ }
+
rsc->checkError("Sampler::setupGL2 tex env");
}
@@ -147,6 +154,7 @@
ss->mWrapS = RS_SAMPLER_WRAP;
ss->mWrapT = RS_SAMPLER_WRAP;
ss->mWrapR = RS_SAMPLER_WRAP;
+ ss->mAniso = 1.0f;
}
void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value)
@@ -169,21 +177,37 @@
case RS_SAMPLER_WRAP_R:
ss->mWrapR = value;
break;
+ default:
+ LOGE("Attempting to set invalid value on sampler");
+ break;
}
+}
+void rsi_SamplerSet2(Context *rsc, RsSamplerParam param, float value)
+{
+ SamplerState * ss = &rsc->mStateSampler;
+
+ switch(param) {
+ case RS_SAMPLER_ANISO:
+ ss->mAniso = value;
+ break;
+ default:
+ LOGE("Attempting to set invalid value on sampler");
+ break;
+ }
}
RsSampler rsi_SamplerCreate(Context *rsc)
{
SamplerState * ss = &rsc->mStateSampler;
-
Sampler * s = new Sampler(rsc,
ss->mMagFilter,
ss->mMinFilter,
ss->mWrapS,
ss->mWrapT,
- ss->mWrapR);
+ ss->mWrapR,
+ ss->mAniso);
s->incUserRef();
return s;
}
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 32a8efd..4946355 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -36,7 +36,8 @@
RsSamplerValue minFilter,
RsSamplerValue wrapS,
RsSamplerValue wrapT,
- RsSamplerValue wrapR);
+ RsSamplerValue wrapR,
+ float aniso = 1.0f);
virtual ~Sampler();
@@ -56,6 +57,7 @@
RsSamplerValue mWrapS;
RsSamplerValue mWrapT;
RsSamplerValue mWrapR;
+ float mAniso;
int32_t mBoundSlot;
@@ -74,6 +76,7 @@
RsSamplerValue mWrapS;
RsSamplerValue mWrapT;
RsSamplerValue mWrapR;
+ float mAniso;
ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT];
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index e9621b9..c6418be 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -349,25 +349,29 @@
ScriptCState::ScriptCState()
{
- mScript = NULL;
- clear();
+ mScript.clear();
}
ScriptCState::~ScriptCState()
{
- delete mScript;
- mScript = NULL;
+ mScript.clear();
}
-void ScriptCState::clear()
+void ScriptCState::init(Context *rsc)
{
+ clear(rsc);
+}
+
+void ScriptCState::clear(Context *rsc)
+{
+ rsAssert(rsc);
for (uint32_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
mConstantBufferTypes[ct].clear();
mSlotWritable[ct] = false;
}
- delete mScript;
- mScript = new ScriptC(NULL);
+ mScript.clear();
+ mScript.set(new ScriptC(rsc));
}
static BCCvoid* symbolLookup(BCCvoid* pContext, const BCCchar* name)
@@ -503,7 +507,7 @@
void rsi_ScriptCBegin(Context * rsc)
{
ScriptCState *ss = &rsc->mScriptC;
- ss->clear();
+ ss->clear(rsc);
}
void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len)
@@ -522,10 +526,10 @@
{
ScriptCState *ss = &rsc->mScriptC;
- ScriptC *s = ss->mScript;
- ss->mScript = NULL;
+ ObjectBaseRef<ScriptC> s = ss->mScript.get();
+ ss->mScript.clear();
- ss->runCompiler(rsc, s);
+ ss->runCompiler(rsc, s.get());
s->incUserRef();
s->setContext(rsc);
for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
@@ -533,8 +537,8 @@
s->mSlotWritable[ct] = ss->mSlotWritable[ct];
}
- ss->clear();
- return s;
+ ss->clear(rsc);
+ return s.get();
}
}
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index 9d09b0b..7ec80aa 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -79,14 +79,16 @@
ScriptCState();
~ScriptCState();
- ScriptC *mScript;
+ ObjectBaseRef<ScriptC> mScript;
ObjectBaseRef<const Type> mConstantBufferTypes[MAX_SCRIPT_BANKS];
//String8 mSlotNames[MAX_SCRIPT_BANKS];
bool mSlotWritable[MAX_SCRIPT_BANKS];
//String8 mInvokableNames[MAX_SCRIPT_BANKS];
- void clear();
+ void init(Context *rsc);
+
+ void clear(Context *rsc);
void runCompiler(Context *rsc, ScriptC *s);
struct SymbolTable_t {
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 22fd421..e0de867 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -203,45 +203,46 @@
static uint32_t SC_allocGetDimX(RsAllocation va)
{
- GET_TLS();
const Allocation *a = static_cast<const Allocation *>(va);
- //LOGE("SC_allocGetDimX a=%p", a);
- //LOGE(" type=%p", a->getType());
+ CHECK_OBJ(a);
+ //LOGE("SC_allocGetDimX a=%p type=%p", a, a->getType());
return a->getType()->getDimX();
}
static uint32_t SC_allocGetDimY(RsAllocation va)
{
- GET_TLS();
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
return a->getType()->getDimY();
}
static uint32_t SC_allocGetDimZ(RsAllocation va)
{
- GET_TLS();
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
return a->getType()->getDimZ();
}
static uint32_t SC_allocGetDimLOD(RsAllocation va)
{
- GET_TLS();
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
return a->getType()->getDimLOD();
}
static uint32_t SC_allocGetDimFaces(RsAllocation va)
{
- GET_TLS();
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
return a->getType()->getDimFaces();
}
static const void * SC_getElementAtX(RsAllocation va, uint32_t x)
{
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
const Type *t = a->getType();
+ CHECK_OBJ(t);
const uint8_t *p = (const uint8_t *)a->getPtr();
return &p[t->getElementSizeBytes() * x];
}
@@ -249,7 +250,9 @@
static const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
{
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
const Type *t = a->getType();
+ CHECK_OBJ(t);
const uint8_t *p = (const uint8_t *)a->getPtr();
return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
}
@@ -257,7 +260,9 @@
static const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
{
const Allocation *a = static_cast<const Allocation *>(va);
+ CHECK_OBJ(a);
const Type *t = a->getType();
+ CHECK_OBJ(t);
const uint8_t *p = (const uint8_t *)a->getPtr();
return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
}
@@ -265,9 +270,11 @@
static void SC_setObject(void **vdst, void * vsrc) {
//LOGE("SC_setObject %p,%p %p", vdst, *vdst, vsrc);
if (vsrc) {
+ CHECK_OBJ(vsrc);
static_cast<ObjectBase *>(vsrc)->incSysRef();
}
if (vdst[0]) {
+ CHECK_OBJ(vdst[0]);
static_cast<ObjectBase *>(vdst[0])->decSysRef();
}
*vdst = vsrc;
@@ -276,6 +283,7 @@
static void SC_clearObject(void **vdst) {
//LOGE("SC_clearObject %p,%p", vdst, *vdst);
if (vdst[0]) {
+ CHECK_OBJ(vdst[0]);
static_cast<ObjectBase *>(vdst[0])->decSysRef();
}
*vdst = NULL;
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index fd4c379..7fd6406 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -44,6 +44,8 @@
static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
{
+ CHECK_OBJ_OR_NULL(va);
+ CHECK_OBJ(vpf);
GET_TLS();
rsi_ProgramBindTexture(rsc,
static_cast<ProgramFragment *>(vpf),
@@ -54,6 +56,8 @@
static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
{
+ CHECK_OBJ_OR_NULL(vs);
+ CHECK_OBJ(vpf);
GET_TLS();
rsi_ProgramBindSampler(rsc,
static_cast<ProgramFragment *>(vpf),
@@ -64,24 +68,28 @@
static void SC_bindProgramStore(RsProgramStore pfs)
{
+ CHECK_OBJ_OR_NULL(pfs);
GET_TLS();
rsi_ContextBindProgramStore(rsc, pfs);
}
static void SC_bindProgramFragment(RsProgramFragment pf)
{
+ CHECK_OBJ_OR_NULL(pf);
GET_TLS();
rsi_ContextBindProgramFragment(rsc, pf);
}
static void SC_bindProgramVertex(RsProgramVertex pv)
{
+ CHECK_OBJ_OR_NULL(pv);
GET_TLS();
rsi_ContextBindProgramVertex(rsc, pv);
}
static void SC_bindProgramRaster(RsProgramRaster pv)
{
+ CHECK_OBJ_OR_NULL(pv);
GET_TLS();
rsi_ContextBindProgramRaster(rsc, pv);
}
@@ -112,6 +120,7 @@
static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a)
{
GET_TLS();
+ CHECK_OBJ(vpf);
ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
pf->setConstantColor(rsc, r, g, b, a);
}
@@ -228,6 +237,7 @@
static void SC_drawMesh(RsMesh vsm)
{
+ CHECK_OBJ(vsm);
GET_TLS();
Mesh *sm = static_cast<Mesh *>(vsm);
if (!rsc->setupCheck()) {
@@ -238,6 +248,7 @@
static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex)
{
+ CHECK_OBJ(vsm);
GET_TLS();
Mesh *sm = static_cast<Mesh *>(vsm);
if (!rsc->setupCheck()) {
@@ -248,6 +259,7 @@
static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len)
{
+ CHECK_OBJ(vsm);
GET_TLS();
Mesh *sm = static_cast<Mesh *>(vsm);
if (!rsc->setupCheck()) {
@@ -259,6 +271,7 @@
static void SC_meshComputeBoundingBox(RsMesh vsm, float *minX, float *minY, float *minZ,
float *maxX, float *maxY, float *maxZ)
{
+ CHECK_OBJ(vsm);
GET_TLS();
Mesh *sm = static_cast<Mesh *>(vsm);
sm->computeBBox();
@@ -285,17 +298,20 @@
static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel)
{
+ CHECK_OBJ(va);
GET_TLS();
rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel);
}
static void SC_uploadToTexture(RsAllocation va)
{
+ CHECK_OBJ(va);
GET_TLS();
rsi_AllocationUploadToTexture(rsc, va, false, 0);
}
static void SC_uploadToBufferObject(RsAllocation va)
{
+ CHECK_OBJ(va);
GET_TLS();
rsi_AllocationUploadToBufferObject(rsc, va);
}
@@ -336,6 +352,7 @@
static void SC_DrawTextAlloc(RsAllocation va, int x, int y)
{
+ CHECK_OBJ(va);
GET_TLS();
Allocation *alloc = static_cast<Allocation *>(va);
rsc->mStateFont.renderText(alloc, x, y);
@@ -349,6 +366,7 @@
static void SC_BindFont(RsFont font)
{
+ CHECK_OBJ(font);
GET_TLS();
rsi_ContextBindFont(rsc, font);
}
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 1ee07cd..fc037a3 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -321,8 +321,6 @@
break;
}
-
- int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0;
if ((dim < 0) || (dim > RS_DIMENSION_MAX)) {
LOGE("rsTypeAdd: Bad dimension");
//error
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 040060e..d676f5e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -126,11 +126,14 @@
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format)
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth, uint32_t reqHeight)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeInt32(dpy);
+ data.writeInt32(reqWidth);
+ data.writeInt32(reqHeight);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
*width = reply.readInt32();
@@ -208,10 +211,13 @@
case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32();
+ uint32_t reqWidth = data.readInt32();
+ uint32_t reqHeight = data.readInt32();
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
- status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+ status_t res = captureScreen(dpy, &heap, &w, &h, &f,
+ reqWidth, reqHeight);
reply->writeStrongBinder(heap->asBinder());
reply->writeInt32(w);
reply->writeInt32(h);
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 4096ac6..f270461 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -545,5 +545,55 @@
}
// ----------------------------------------------------------------------------
+
+ScreenshotClient::ScreenshotClient()
+ : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+}
+
+status_t ScreenshotClient::update() {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, 0, 0);
+}
+
+status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, reqWidth, reqHeight);
+}
+
+void ScreenshotClient::release() {
+ mHeap = 0;
+}
+
+void const* ScreenshotClient::getPixels() const {
+ return mHeap->getBase();
+}
+
+uint32_t ScreenshotClient::getWidth() const {
+ return mWidth;
+}
+
+uint32_t ScreenshotClient::getHeight() const {
+ return mHeight;
+}
+
+PixelFormat ScreenshotClient::getFormat() const {
+ return mFormat;
+}
+
+uint32_t ScreenshotClient::getStride() const {
+ return mWidth;
+}
+
+size_t ScreenshotClient::getSize() const {
+ return mHeap->getSize();
+}
+
+// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index f2b029a..1d35e10 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -33,6 +33,9 @@
#include <math.h>
#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
namespace android {
@@ -63,6 +66,10 @@
return sqrtf(x * x + y * y);
}
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
int32_t mask;
@@ -236,16 +243,14 @@
String8 name = mEventHub->getDeviceName(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
- // Write a log message about the added device as a heading for subsequent log messages.
- LOGI("Device added: id=0x%x, name=%s", deviceId, name.string());
-
InputDevice* device = createDevice(deviceId, name, classes);
device->configure();
if (device->isIgnored()) {
- LOGI(INDENT "Sources: none (device is ignored)");
+ LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
} else {
- LOGI(INDENT "Sources: 0x%08x", device->getSources());
+ LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+ device->getSources());
}
bool added = false;
@@ -287,7 +292,6 @@
return;
}
- // Write a log message about the removed device as a heading for subsequent log messages.
if (device->isIgnored()) {
LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
device->getId(), device->getName().string());
@@ -571,59 +575,15 @@
}
void InputReader::dump(String8& dump) {
- dumpDeviceInfo(dump);
-}
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
-static void dumpMotionRange(String8& dump,
- const char* name, const InputDeviceInfo::MotionRange* range) {
- if (range) {
- dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
- name, range->min, range->max, range->flat, range->fuzz);
- }
-}
-
-#define DUMP_MOTION_RANGE(range) \
- dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
-
-void InputReader::dumpDeviceInfo(String8& dump) {
- Vector<int32_t> deviceIds;
- getInputDeviceIds(deviceIds);
-
- InputDeviceInfo deviceInfo;
- for (size_t i = 0; i < deviceIds.size(); i++) {
- int32_t deviceId = deviceIds[i];
-
- status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
- if (result == NAME_NOT_FOUND) {
- continue;
- } else if (result != OK) {
- dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
- result);
- continue;
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ mDevices.valueAt(i)->dump(dump);
}
-
- dump.appendFormat(" Device %d: '%s'\n",
- deviceInfo.getId(), deviceInfo.getName().string());
- dump.appendFormat(" sources = 0x%08x\n",
- deviceInfo.getSources());
- dump.appendFormat(" keyboardType = %d\n",
- deviceInfo.getKeyboardType());
-
- dump.append(" motion ranges:\n");
- DUMP_MOTION_RANGE(X);
- DUMP_MOTION_RANGE(Y);
- DUMP_MOTION_RANGE(PRESSURE);
- DUMP_MOTION_RANGE(SIZE);
- DUMP_MOTION_RANGE(TOUCH_MAJOR);
- DUMP_MOTION_RANGE(TOUCH_MINOR);
- DUMP_MOTION_RANGE(TOOL_MAJOR);
- DUMP_MOTION_RANGE(TOOL_MINOR);
- DUMP_MOTION_RANGE(ORIENTATION);
- }
+ } // release device registy reader lock
}
-#undef DUMP_MOTION_RANGE
-
// --- InputReaderThread ---
@@ -654,6 +614,43 @@
mMappers.clear();
}
+static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo,
+ int32_t rangeType, const char* name) {
+ const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType);
+ if (range) {
+ dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+void InputDevice::dump(String8& dump) {
+ InputDeviceInfo deviceInfo;
+ getDeviceInfo(& deviceInfo);
+
+ dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+ deviceInfo.getName().string());
+ dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+ dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+ if (!deviceInfo.getMotionRanges().isEmpty()) {
+ dump.append(INDENT2 "Motion Ranges:\n");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
+ }
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->dump(dump);
+ }
+}
+
void InputDevice::addMapper(InputMapper* mapper) {
mMappers.add(mapper);
}
@@ -763,6 +760,9 @@
info->addSource(getSources());
}
+void InputMapper::dump(String8& dump) {
+}
+
void InputMapper::configure() {
}
@@ -856,6 +856,19 @@
info->setKeyboardType(mKeyboardType);
}
+void KeyboardInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Keyboard Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dump.appendFormat(INDENT3 "Sources: 0x%x\n", mSources);
+ dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+ dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
+ dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
+ dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+ } // release lock
+}
+
void KeyboardInputMapper::reset() {
for (;;) {
int32_t keyCode, scanCode;
@@ -1044,6 +1057,18 @@
info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
}
+void TrackballInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Trackball Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+ dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+ dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
+ dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+ } // release lock
+}
+
void TrackballInputMapper::initializeLocked() {
mAccumulator.clear();
@@ -1275,6 +1300,21 @@
} // release lock
}
+void TouchInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Touch Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dumpParameters(dump);
+ dumpVirtualKeysLocked(dump);
+ dumpRawAxes(dump);
+ dumpCalibration(dump);
+ dumpSurfaceLocked(dump);
+ dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mLocked.xPrecision);
+ dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mLocked.yPrecision);
+ } // release lock
+}
+
void TouchInputMapper::initializeLocked() {
mCurrentTouch.clear();
mLastTouch.clear();
@@ -1301,16 +1341,13 @@
// Configure basic parameters.
configureParameters();
- logParameters();
// Configure absolute axis information.
configureRawAxes();
- logRawAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
- logCalibration();
{ // acquire lock
AutoMutex _l(mLock);
@@ -1326,16 +1363,13 @@
mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
}
-void TouchInputMapper::logParameters() {
- if (mParameters.useBadTouchFilter) {
- LOGI(INDENT "Bad touch filter enabled.");
- }
- if (mParameters.useAveragingTouchFilter) {
- LOGI(INDENT "Averaging touch filter enabled.");
- }
- if (mParameters.useJumpyTouchFilter) {
- LOGI(INDENT "Jumpy touch filter enabled.");
- }
+void TouchInputMapper::dumpParameters(String8& dump) {
+ dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+ toString(mParameters.useBadTouchFilter));
+ dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+ toString(mParameters.useAveragingTouchFilter));
+ dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+ toString(mParameters.useJumpyTouchFilter));
}
void TouchInputMapper::configureRawAxes() {
@@ -1349,24 +1383,25 @@
mRawAxes.orientation.clear();
}
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
if (axis.valid) {
- LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+ dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
} else {
- LOGI(INDENT "Raw %s axis: unknown range", name);
+ dump.appendFormat(INDENT4 "%s: unknown range\n", name);
}
}
-void TouchInputMapper::logRawAxes() {
- logAxisInfo(mRawAxes.x, "x");
- logAxisInfo(mRawAxes.y, "y");
- logAxisInfo(mRawAxes.pressure, "pressure");
- logAxisInfo(mRawAxes.touchMajor, "touchMajor");
- logAxisInfo(mRawAxes.touchMinor, "touchMinor");
- logAxisInfo(mRawAxes.toolMajor, "toolMajor");
- logAxisInfo(mRawAxes.toolMinor, "toolMinor");
- logAxisInfo(mRawAxes.orientation, "orientation");
+void TouchInputMapper::dumpRawAxes(String8& dump) {
+ dump.append(INDENT3 "Raw Axes:\n");
+ dumpAxisInfo(dump, mRawAxes.x, "X");
+ dumpAxisInfo(dump, mRawAxes.y, "Y");
+ dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
+ dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+ dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+ dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+ dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+ dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
}
bool TouchInputMapper::configureSurfaceLocked() {
@@ -1391,10 +1426,8 @@
bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
if (sizeChanged) {
- LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
- getDeviceId(), getDeviceName().string());
- LOGI(INDENT "Width: %dpx", width);
- LOGI(INDENT "Height: %dpx", height);
+ LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+ getDeviceId(), getDeviceName().string(), width, height);
mLocked.surfaceWidth = width;
mLocked.surfaceHeight = height;
@@ -1562,39 +1595,13 @@
mLocked.orientedRanges.y.fuzz = orientedYScale;
}
- if (sizeChanged) {
- logMotionRangesLocked();
- }
-
return true;
}
-static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
- if (range) {
- LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
- name, range->min, range->max, range->flat, range->fuzz);
- } else {
- LOGI(INDENT "Output %s range: unsupported", name);
- }
-}
-
-void TouchInputMapper::logMotionRangesLocked() {
- logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
- logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
- logMotionRangeInfo(mLocked.orientedRanges.havePressure
- ? & mLocked.orientedRanges.pressure : NULL, "pressure");
- logMotionRangeInfo(mLocked.orientedRanges.haveSize
- ? & mLocked.orientedRanges.size : NULL, "size");
- logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
- ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
- logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
- ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
- logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
- ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
- logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
- ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
- logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
- ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
+ dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
+ dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
+ dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
}
void TouchInputMapper::configureVirtualKeysLocked() {
@@ -1651,9 +1658,21 @@
virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
* touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
- LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
- virtualKey.scanCode, virtualKey.keyCode,
- virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+ }
+}
+
+void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
+ if (!mLocked.virtualKeys.isEmpty()) {
+ dump.append(INDENT3 "Virtual Keys:\n");
+
+ for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
+ dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
+ "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+ i, virtualKey.scanCode, virtualKey.keyCode,
+ virtualKey.hitLeft, virtualKey.hitRight,
+ virtualKey.hitTop, virtualKey.hitBottom);
+ }
}
}
@@ -1861,19 +1880,19 @@
}
}
-void TouchInputMapper::logCalibration() {
- LOGI(INDENT "Calibration:");
+void TouchInputMapper::dumpCalibration(String8& dump) {
+ dump.append(INDENT3 "Calibration:\n");
// Touch Area
switch (mCalibration.touchAreaCalibration) {
case Calibration::TOUCH_AREA_CALIBRATION_NONE:
- LOGI(INDENT INDENT "touch.touchArea.calibration: none");
+ dump.append(INDENT4 "touch.touchArea.calibration: none\n");
break;
case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
- LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
+ dump.append(INDENT4 "touch.touchArea.calibration: geometric\n");
break;
case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
- LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
+ dump.append(INDENT4 "touch.touchArea.calibration: pressure\n");
break;
default:
assert(false);
@@ -1882,40 +1901,43 @@
// Tool Area
switch (mCalibration.toolAreaCalibration) {
case Calibration::TOOL_AREA_CALIBRATION_NONE:
- LOGI(INDENT INDENT "touch.toolArea.calibration: none");
+ dump.append(INDENT4 "touch.toolArea.calibration: none\n");
break;
case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
- LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
+ dump.append(INDENT4 "touch.toolArea.calibration: geometric\n");
break;
case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
- LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
+ dump.append(INDENT4 "touch.toolArea.calibration: linear\n");
break;
default:
assert(false);
}
if (mCalibration.haveToolAreaLinearScale) {
- LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+ dump.appendFormat(INDENT4 "touch.toolArea.linearScale: %0.3f\n",
+ mCalibration.toolAreaLinearScale);
}
if (mCalibration.haveToolAreaLinearBias) {
- LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+ dump.appendFormat(INDENT4 "touch.toolArea.linearBias: %0.3f\n",
+ mCalibration.toolAreaLinearBias);
}
if (mCalibration.haveToolAreaIsSummed) {
- LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+ dump.appendFormat(INDENT4 "touch.toolArea.isSummed: %d\n",
+ mCalibration.toolAreaIsSummed);
}
// Pressure
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_NONE:
- LOGI(INDENT INDENT "touch.pressure.calibration: none");
+ dump.append(INDENT4 "touch.pressure.calibration: none\n");
break;
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- LOGI(INDENT INDENT "touch.pressure.calibration: physical");
+ dump.append(INDENT4 "touch.pressure.calibration: physical\n");
break;
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
+ dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
break;
default:
assert(false);
@@ -1923,10 +1945,10 @@
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
- LOGI(INDENT INDENT "touch.pressure.source: pressure");
+ dump.append(INDENT4 "touch.pressure.source: pressure\n");
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
- LOGI(INDENT INDENT "touch.pressure.source: touch");
+ dump.append(INDENT4 "touch.pressure.source: touch\n");
break;
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
@@ -1935,16 +1957,17 @@
}
if (mCalibration.havePressureScale) {
- LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
+ dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
+ mCalibration.pressureScale);
}
// Size
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_NONE:
- LOGI(INDENT INDENT "touch.size.calibration: none");
+ dump.append(INDENT4 "touch.size.calibration: none\n");
break;
case Calibration::SIZE_CALIBRATION_NORMALIZED:
- LOGI(INDENT INDENT "touch.size.calibration: normalized");
+ dump.append(INDENT4 "touch.size.calibration: normalized\n");
break;
default:
assert(false);
@@ -1953,10 +1976,10 @@
// Orientation
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_NONE:
- LOGI(INDENT INDENT "touch.orientation.calibration: none");
+ dump.append(INDENT4 "touch.orientation.calibration: none\n");
break;
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
+ dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
break;
default:
assert(false);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 890786e..587c8ff 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -316,6 +316,7 @@
mNewPosition = mCblk->server + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
+ mCblk->flags &= ~CBLK_DISABLED_ON;
if (t != 0) {
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
} else {
@@ -842,6 +843,13 @@
cblk->lock.unlock();
}
+ // restart track if it was disabled by audioflinger due to previous underrun
+ if (cblk->flags & CBLK_DISABLED_MSK) {
+ cblk->flags &= ~CBLK_DISABLED_ON;
+ LOGW("obtainBuffer() track %p disabled, restarting", this);
+ mAudioTrack->start();
+ }
+
cblk->waitTimeMs = 0;
if (framesReq > framesAvail) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 97c9003..8e50d39 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -276,20 +276,6 @@
status_t AwesomePlayer::setDataSource(
int fd, int64_t offset, int64_t length) {
-#if 0
- // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/sl.m3u8");
- return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/0440.m3u8");
- // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/0640.m3u8");
- // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/1240_vod.m3u8");
- // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/nasatv/nasatv_96.m3u8");
- // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/nasatv/nasatv_1500.m3u8");
- // return setDataSource("httplive://iphone.video.hsn.com/iPhone_high.m3u8");
- // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/iphonewebcast/webcast090209_all/webcast090209_all.m3u8");
- // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/usat/tt_062209_iphone/hi/prog_index.m3u8");
- // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/usat/tt_googmaps/hi/prog_index.m3u8");
- // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/mtv/ni_spo_25a_rt74137_clip_syn/hi/prog_index.m3u8");
-#endif
-
Mutex::Autolock autoLock(mLock);
reset_l();
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 511ae12..a8f1104 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -21,7 +21,7 @@
namespace android {
-static unsigned parseUE(ABitReader *br) {
+unsigned parseUE(ABitReader *br) {
unsigned numZeroes = 0;
while (br->getBits(1) == 0) {
++numZeroes;
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 3c0b736..868c514 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -31,6 +31,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/hexdump.h>
namespace android {
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 001afc4..9103927 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -93,7 +93,7 @@
}
if (mLastFetchTimeUs < 0) {
- mPlaylistIndex = mPlaylist->size() / 2;
+ mPlaylistIndex = 0;
} else {
if (nextSequenceNumber < mFirstItemSequenceNumber
|| nextSequenceNumber
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index cc405b5..6602852 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -22,9 +22,13 @@
namespace android {
+struct ABitReader;
+
void FindAVCDimensions(
const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+unsigned parseUE(ABitReader *br);
+
} // namespace android
#endif // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 47cca80..bcaab9f 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -21,6 +21,7 @@
#include "ATSParser.h"
#include "AnotherPacketSource.h"
+#include "ESQueue.h"
#include "include/avc_utils.h"
#include <media/stagefright/foundation/ABitReader.h>
@@ -79,6 +80,8 @@
sp<AnotherPacketSource> mSource;
bool mPayloadStarted;
+ ElementaryStreamQueue mQueue;
+
void flush();
void parsePES(ABitReader *br);
@@ -232,7 +235,9 @@
: mElementaryPID(elementaryPID),
mStreamType(streamType),
mBuffer(new ABuffer(128 * 1024)),
- mPayloadStarted(false) {
+ mPayloadStarted(false),
+ mQueue(streamType == 0x1b
+ ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) {
mBuffer->setRange(0, 0);
}
@@ -433,373 +438,31 @@
mBuffer->setRange(0, 0);
}
-static sp<ABuffer> FindNAL(
- const uint8_t *data, size_t size, unsigned nalType,
- size_t *stopOffset) {
- bool foundStart = false;
- size_t startOffset = 0;
-
- size_t offset = 0;
- for (;;) {
- while (offset + 3 < size
- && memcmp("\x00\x00\x00\x01", &data[offset], 4)) {
- ++offset;
- }
-
- if (foundStart) {
- size_t nalSize;
- if (offset + 3 >= size) {
- nalSize = size - startOffset;
- } else {
- nalSize = offset - startOffset;
- }
-
- sp<ABuffer> nal = new ABuffer(nalSize);
- memcpy(nal->data(), &data[startOffset], nalSize);
-
- if (stopOffset != NULL) {
- *stopOffset = startOffset + nalSize;
- }
-
- return nal;
- }
-
- if (offset + 4 >= size) {
- return NULL;
- }
-
- if ((data[offset + 4] & 0x1f) == nalType) {
- foundStart = true;
- startOffset = offset + 4;
- }
-
- offset += 4;
- }
-}
-
-static sp<ABuffer> MakeAVCCodecSpecificData(
- const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
- const uint8_t *data = buffer->data();
- size_t size = buffer->size();
-
- sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
- if (seqParamSet == NULL) {
- return NULL;
- }
-
- FindAVCDimensions(seqParamSet, width, height);
-
- size_t stopOffset;
- sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
- CHECK(picParamSet != NULL);
-
- buffer->setRange(stopOffset, size - stopOffset);
- LOGV("buffer has %d bytes left.", buffer->size());
-
- size_t csdSize =
- 1 + 3 + 1 + 1
- + 2 * 1 + seqParamSet->size()
- + 1 + 2 * 1 + picParamSet->size();
-
- sp<ABuffer> csd = new ABuffer(csdSize);
- uint8_t *out = csd->data();
-
- *out++ = 0x01; // configurationVersion
- memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
- out += 3;
- *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
- *out++ = 0xe0 | 1;
-
- *out++ = seqParamSet->size() >> 8;
- *out++ = seqParamSet->size() & 0xff;
- memcpy(out, seqParamSet->data(), seqParamSet->size());
- out += seqParamSet->size();
-
- *out++ = 1;
-
- *out++ = picParamSet->size() >> 8;
- *out++ = picParamSet->size() & 0xff;
- memcpy(out, picParamSet->data(), picParamSet->size());
-
- return csd;
-}
-
-static bool getNextNALUnit(
- const uint8_t **_data, size_t *_size,
- const uint8_t **nalStart, size_t *nalSize) {
- const uint8_t *data = *_data;
- size_t size = *_size;
-
- // hexdump(data, size);
-
- *nalStart = NULL;
- *nalSize = 0;
-
- if (size == 0) {
- return false;
- }
-
- size_t offset = 0;
- for (;;) {
- CHECK_LT(offset + 2, size);
-
- if (!memcmp("\x00\x00\x01", &data[offset], 3)) {
- break;
- }
-
- CHECK_EQ((unsigned)data[offset], 0x00u);
- ++offset;
- }
-
- offset += 3;
- size_t startOffset = offset;
-
- while (offset + 2 < size
- && memcmp("\x00\x00\x00", &data[offset], 3)
- && memcmp("\x00\x00\x01", &data[offset], 3)) {
- ++offset;
- }
-
- if (offset + 2 >= size) {
- *nalStart = &data[startOffset];
- *nalSize = size - startOffset;
-
- *_data = NULL;
- *_size = 0;
-
- return true;
- }
-
- size_t endOffset = offset;
-
- while (offset + 2 < size && memcmp("\x00\x00\x01", &data[offset], 3)) {
- CHECK_EQ((unsigned)data[offset], 0x00u);
- ++offset;
- }
-
- *nalStart = &data[startOffset];
- *nalSize = endOffset - startOffset;
-
- if (offset + 2 < size) {
- *_data = &data[offset];
- *_size = size - offset;
- } else {
- *_data = NULL;
- *_size = 0;
- }
-
- return true;
-}
-
-sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
- // hexdump(data, size);
-
- const uint8_t *tmpData = data;
- size_t tmpSize = size;
-
- size_t totalSize = 0;
- const uint8_t *nalStart;
- size_t nalSize;
- while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
- // hexdump(nalStart, nalSize);
- totalSize += 4 + nalSize;
- }
-
- sp<ABuffer> buffer = new ABuffer(totalSize);
- size_t offset = 0;
- while (getNextNALUnit(&data, &size, &nalStart, &nalSize)) {
- memcpy(buffer->data() + offset, "\x00\x00\x00\x01", 4);
- memcpy(buffer->data() + offset + 4, nalStart, nalSize);
-
- offset += 4 + nalSize;
- }
-
- return buffer;
-}
-
-static sp<ABuffer> FindMPEG2ADTSConfig(
- const sp<ABuffer> &buffer, int32_t *sampleRate, int32_t *channelCount) {
- ABitReader br(buffer->data(), buffer->size());
-
- CHECK_EQ(br.getBits(12), 0xfffu);
- CHECK_EQ(br.getBits(1), 0u);
- CHECK_EQ(br.getBits(2), 0u);
- br.getBits(1); // protection_absent
- unsigned profile = br.getBits(2);
- LOGV("profile = %u", profile);
- CHECK_NE(profile, 3u);
- unsigned sampling_freq_index = br.getBits(4);
- br.getBits(1); // private_bit
- unsigned channel_configuration = br.getBits(3);
- CHECK_NE(channel_configuration, 0u);
-
- LOGV("sampling_freq_index = %u", sampling_freq_index);
- LOGV("channel_configuration = %u", channel_configuration);
-
- CHECK_LE(sampling_freq_index, 11u);
- static const int32_t kSamplingFreq[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000
- };
- *sampleRate = kSamplingFreq[sampling_freq_index];
-
- *channelCount = channel_configuration;
-
- static const uint8_t kStaticESDS[] = {
- 0x03, 22,
- 0x00, 0x00, // ES_ID
- 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
-
- 0x04, 17,
- 0x40, // Audio ISO/IEC 14496-3
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-
- 0x05, 2,
- // AudioSpecificInfo follows
-
- // oooo offf fccc c000
- // o - audioObjectType
- // f - samplingFreqIndex
- // c - channelConfig
- };
- sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
- memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
-
- csd->data()[sizeof(kStaticESDS)] =
- ((profile + 1) << 3) | (sampling_freq_index >> 1);
-
- csd->data()[sizeof(kStaticESDS) + 1] =
- ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
-
- // hexdump(csd->data(), csd->size());
- return csd;
-}
-
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
const uint8_t *data, size_t size) {
LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
- sp<ABuffer> buffer;
-
- if (mStreamType == 0x1b) {
- buffer = MakeCleanAVCData(data, size);
- } else {
- // hexdump(data, size);
-
- buffer = new ABuffer(size);
- memcpy(buffer->data(), data, size);
- }
-
- if (mSource == NULL) {
- sp<MetaData> meta = new MetaData;
-
- if (mStreamType == 0x1b) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
-
- int32_t width, height;
- sp<ABuffer> csd = MakeAVCCodecSpecificData(buffer, &width, &height);
-
- if (csd == NULL) {
- return;
- }
-
- meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
- } else {
- CHECK_EQ(mStreamType, 0x0fu);
-
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
- int32_t sampleRate, channelCount;
- sp<ABuffer> csd =
- FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
-
- LOGV("sampleRate = %d", sampleRate);
- LOGV("channelCount = %d", channelCount);
-
- meta->setInt32(kKeySampleRate, sampleRate);
- meta->setInt32(kKeyChannelCount, channelCount);
-
- meta->setData(kKeyESDS, 0, csd->data(), csd->size());
- }
-
- LOGV("created source!");
- mSource = new AnotherPacketSource(meta);
-
- // fall through
- }
-
CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
- buffer->meta()->setInt64("time", (PTS * 100) / 9);
+ int64_t timeUs = (PTS * 100) / 9;
- if (mStreamType == 0x0f) {
- extractAACFrames(buffer);
- }
+ status_t err = mQueue.appendData(data, size, timeUs);
+ CHECK_EQ(err, (status_t)OK);
- mSource->queueAccessUnit(buffer);
-}
+ sp<ABuffer> accessUnit;
+ while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) {
+ if (mSource == NULL) {
+ sp<MetaData> meta = mQueue.getFormat();
-// Disassemble one or more ADTS frames into their constituent parts and
-// leave only the concatenated raw_data_blocks in the buffer.
-void ATSParser::Stream::extractAACFrames(const sp<ABuffer> &buffer) {
- size_t dstOffset = 0;
-
- size_t offset = 0;
- while (offset < buffer->size()) {
- CHECK_LE(offset + 7, buffer->size());
-
- ABitReader bits(buffer->data() + offset, buffer->size() - offset);
-
- // adts_fixed_header
-
- CHECK_EQ(bits.getBits(12), 0xfffu);
- bits.skipBits(3); // ID, layer
- bool protection_absent = bits.getBits(1) != 0;
-
- // profile_ObjectType, sampling_frequency_index, private_bits,
- // channel_configuration, original_copy, home
- bits.skipBits(12);
-
- // adts_variable_header
-
- // copyright_identification_bit, copyright_identification_start
- bits.skipBits(2);
-
- unsigned aac_frame_length = bits.getBits(13);
-
- bits.skipBits(11); // adts_buffer_fullness
-
- unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2);
-
- if (number_of_raw_data_blocks_in_frame == 0) {
- size_t scan = offset + aac_frame_length;
-
- offset += 7;
- if (!protection_absent) {
- offset += 2;
+ if (meta != NULL) {
+ LOGV("created source!");
+ mSource = new AnotherPacketSource(meta);
+ mSource->queueAccessUnit(accessUnit);
}
-
- CHECK_LE(scan, buffer->size());
-
- LOGV("found aac raw data block at [0x%08x ; 0x%08x)", offset, scan);
-
- memmove(&buffer->data()[dstOffset], &buffer->data()[offset],
- scan - offset);
-
- dstOffset += scan - offset;
- offset = scan;
} else {
- // To be implemented.
- TRESPASS();
+ mSource->queueAccessUnit(accessUnit);
}
}
- CHECK_EQ(offset, buffer->size());
-
- buffer->setRange(buffer->offset(), dstOffset);
}
sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 3544b4c..4dfc0f7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -5,6 +5,7 @@
LOCAL_SRC_FILES:= \
AnotherPacketSource.cpp \
ATSParser.cpp \
+ ESQueue.cpp \
MPEG2TSExtractor.cpp \
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
new file mode 100644
index 0000000..d87040b
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ESQueue"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "ESQueue.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+
+#include "include/avc_utils.h"
+
+namespace android {
+
+ElementaryStreamQueue::ElementaryStreamQueue(Mode mode)
+ : mMode(mode) {
+}
+
+sp<MetaData> ElementaryStreamQueue::getFormat() {
+ return mFormat;
+}
+
+static status_t getNextNALUnit(
+ const uint8_t **_data, size_t *_size,
+ const uint8_t **nalStart, size_t *nalSize,
+ bool startCodeFollows = false) {
+ const uint8_t *data = *_data;
+ size_t size = *_size;
+
+ *nalStart = NULL;
+ *nalSize = 0;
+
+ if (size == 0) {
+ return -EAGAIN;
+ }
+
+ // Skip any number of leading 0x00.
+
+ size_t offset = 0;
+ while (offset < size && data[offset] == 0x00) {
+ ++offset;
+ }
+
+ if (offset == size) {
+ return -EAGAIN;
+ }
+
+ // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
+
+ if (offset < 2 || data[offset] != 0x01) {
+ return ERROR_MALFORMED;
+ }
+
+ ++offset;
+
+ size_t startOffset = offset;
+
+ for (;;) {
+ while (offset < size && data[offset] != 0x01) {
+ ++offset;
+ }
+
+ if (offset == size) {
+ if (startCodeFollows) {
+ offset = size + 2;
+ break;
+ }
+
+ return -EAGAIN;
+ }
+
+ if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
+ break;
+ }
+
+ ++offset;
+ }
+
+ size_t endOffset = offset - 2;
+ while (data[endOffset - 1] == 0x00) {
+ --endOffset;
+ }
+
+ *nalStart = &data[startOffset];
+ *nalSize = endOffset - startOffset;
+
+ if (offset + 2 < size) {
+ *_data = &data[offset - 2];
+ *_size = size - offset + 2;
+ } else {
+ *_data = NULL;
+ *_size = 0;
+ }
+
+ return OK;
+}
+
+status_t ElementaryStreamQueue::appendData(
+ const void *data, size_t size, int64_t timeUs) {
+ if (mBuffer == NULL || mBuffer->size() == 0) {
+ switch (mMode) {
+ case H264:
+ {
+ if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ case AAC:
+ {
+ uint8_t *ptr = (uint8_t *)data;
+
+ if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+ }
+
+ size_t neededSize = (mBuffer == NULL ? 0 : mBuffer->size()) + size;
+ if (mBuffer == NULL || neededSize > mBuffer->capacity()) {
+ neededSize = (neededSize + 65535) & ~65535;
+
+ LOGI("resizing buffer to size %d", neededSize);
+
+ sp<ABuffer> buffer = new ABuffer(neededSize);
+ if (mBuffer != NULL) {
+ memcpy(buffer->data(), mBuffer->data(), mBuffer->size());
+ buffer->setRange(0, mBuffer->size());
+ } else {
+ buffer->setRange(0, 0);
+ }
+
+ mBuffer = buffer;
+ }
+
+ memcpy(mBuffer->data() + mBuffer->size(), data, size);
+ mBuffer->setRange(0, mBuffer->size() + size);
+
+ mTimestamps.push_back(timeUs);
+
+ return OK;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
+ if (mMode == H264) {
+ return dequeueAccessUnitH264();
+ } else {
+ CHECK_EQ((unsigned)mMode, (unsigned)AAC);
+ return dequeueAccessUnitAAC();
+ }
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
+ Vector<size_t> frameOffsets;
+ Vector<size_t> frameSizes;
+ size_t auSize = 0;
+
+ size_t offset = 0;
+ while (offset + 7 <= mBuffer->size()) {
+ ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset);
+
+ // adts_fixed_header
+
+ CHECK_EQ(bits.getBits(12), 0xfffu);
+ bits.skipBits(3); // ID, layer
+ bool protection_absent = bits.getBits(1) != 0;
+
+ if (mFormat == NULL) {
+ unsigned profile = bits.getBits(2);
+ CHECK_NE(profile, 3u);
+ unsigned sampling_freq_index = bits.getBits(4);
+ bits.getBits(1); // private_bit
+ unsigned channel_configuration = bits.getBits(3);
+ CHECK_NE(channel_configuration, 0u);
+ bits.skipBits(2); // original_copy, home
+
+ mFormat = MakeAACCodecSpecificData(
+ profile, sampling_freq_index, channel_configuration);
+ } else {
+ // profile_ObjectType, sampling_frequency_index, private_bits,
+ // channel_configuration, original_copy, home
+ bits.skipBits(12);
+ }
+
+ // adts_variable_header
+
+ // copyright_identification_bit, copyright_identification_start
+ bits.skipBits(2);
+
+ unsigned aac_frame_length = bits.getBits(13);
+
+ bits.skipBits(11); // adts_buffer_fullness
+
+ unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2);
+
+ if (number_of_raw_data_blocks_in_frame != 0) {
+ // To be implemented.
+ TRESPASS();
+ }
+
+ if (offset + aac_frame_length > mBuffer->size()) {
+ break;
+ }
+
+ size_t headerSize = protection_absent ? 7 : 9;
+
+ frameOffsets.push(offset + headerSize);
+ frameSizes.push(aac_frame_length - headerSize);
+ auSize += aac_frame_length - headerSize;
+
+ offset += aac_frame_length;
+ }
+
+ if (offset == 0) {
+ return NULL;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(auSize);
+ size_t dstOffset = 0;
+ for (size_t i = 0; i < frameOffsets.size(); ++i) {
+ memcpy(accessUnit->data() + dstOffset,
+ mBuffer->data() + frameOffsets.itemAt(i),
+ frameSizes.itemAt(i));
+
+ dstOffset += frameSizes.itemAt(i);
+ }
+
+ memmove(mBuffer->data(), mBuffer->data() + offset,
+ mBuffer->size() - offset);
+ mBuffer->setRange(0, mBuffer->size() - offset);
+
+ CHECK_GT(mTimestamps.size(), 0u);
+ int64_t timeUs = *mTimestamps.begin();
+ mTimestamps.erase(mTimestamps.begin());
+
+ accessUnit->meta()->setInt64("time", timeUs);
+
+ return accessUnit;
+}
+
+// static
+sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration) {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ CHECK_LE(sampling_freq_index, 11u);
+ static const int32_t kSamplingFreq[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000
+ };
+ meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
+ meta->setInt32(kKeyChannelCount, channel_configuration);
+
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05, 2,
+ // AudioSpecificInfo follows
+
+ // oooo offf fccc c000
+ // o - audioObjectType
+ // f - samplingFreqIndex
+ // c - channelConfig
+ };
+ sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+ memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+ csd->data()[sizeof(kStaticESDS)] =
+ ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+ csd->data()[sizeof(kStaticESDS) + 1] =
+ ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+ meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+
+ return meta;
+}
+
+struct NALPosition {
+ size_t nalOffset;
+ size_t nalSize;
+};
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
+ const uint8_t *data = mBuffer->data();
+ size_t size = mBuffer->size();
+
+ Vector<NALPosition> nals;
+
+ size_t totalSize = 0;
+
+ status_t err;
+ const uint8_t *nalStart;
+ size_t nalSize;
+ bool foundSlice = false;
+ while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
+ CHECK_GT(nalSize, 0u);
+
+ unsigned nalType = nalStart[0] & 0x1f;
+ bool flush = false;
+
+ if (nalType == 1 || nalType == 5) {
+ if (foundSlice) {
+ ABitReader br(nalStart + 1, nalSize);
+ unsigned first_mb_in_slice = parseUE(&br);
+
+ if (first_mb_in_slice == 0) {
+ // This slice starts a new frame.
+
+ flush = true;
+ }
+ }
+
+ foundSlice = true;
+ } else if ((nalType == 9 || nalType == 7) && foundSlice) {
+ // Access unit delimiter and SPS will be associated with the
+ // next frame.
+
+ flush = true;
+ }
+
+ if (flush) {
+ // The access unit will contain all nal units up to, but excluding
+ // the current one, separated by 0x00 0x00 0x00 0x01 startcodes.
+
+ size_t auSize = 4 * nals.size() + totalSize;
+ sp<ABuffer> accessUnit = new ABuffer(auSize);
+
+#if !LOG_NDEBUG
+ AString out;
+#endif
+
+ size_t dstOffset = 0;
+ for (size_t i = 0; i < nals.size(); ++i) {
+ const NALPosition &pos = nals.itemAt(i);
+
+ unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f;
+
+#if !LOG_NDEBUG
+ char tmp[128];
+ sprintf(tmp, "0x%02x", nalType);
+ if (i > 0) {
+ out.append(", ");
+ }
+ out.append(tmp);
+#endif
+
+ memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
+
+ memcpy(accessUnit->data() + dstOffset + 4,
+ mBuffer->data() + pos.nalOffset,
+ pos.nalSize);
+
+ dstOffset += pos.nalSize + 4;
+ }
+
+ LOGV("accessUnit contains nal types %s", out.c_str());
+
+ const NALPosition &pos = nals.itemAt(nals.size() - 1);
+ size_t nextScan = pos.nalOffset + pos.nalSize;
+
+ memmove(mBuffer->data(),
+ mBuffer->data() + nextScan,
+ mBuffer->size() - nextScan);
+
+ mBuffer->setRange(0, mBuffer->size() - nextScan);
+
+ CHECK_GT(mTimestamps.size(), 0u);
+ int64_t timeUs = *mTimestamps.begin();
+ mTimestamps.erase(mTimestamps.begin());
+
+ accessUnit->meta()->setInt64("time", timeUs);
+
+ if (mFormat == NULL) {
+ mFormat = MakeAVCCodecSpecificData(accessUnit);
+ }
+
+ return accessUnit;
+ }
+
+ NALPosition pos;
+ pos.nalOffset = nalStart - mBuffer->data();
+ pos.nalSize = nalSize;
+
+ nals.push(pos);
+
+ totalSize += nalSize;
+ }
+ CHECK_EQ(err, (status_t)-EAGAIN);
+
+ return NULL;
+}
+
+static sp<ABuffer> FindNAL(
+ const uint8_t *data, size_t size, unsigned nalType,
+ size_t *stopOffset) {
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ if ((nalStart[0] & 0x1f) == nalType) {
+ sp<ABuffer> buffer = new ABuffer(nalSize);
+ memcpy(buffer->data(), nalStart, nalSize);
+ return buffer;
+ }
+ }
+
+ return NULL;
+}
+
+sp<MetaData> ElementaryStreamQueue::MakeAVCCodecSpecificData(
+ const sp<ABuffer> &accessUnit) {
+ const uint8_t *data = accessUnit->data();
+ size_t size = accessUnit->size();
+
+ sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+ if (seqParamSet == NULL) {
+ return NULL;
+ }
+
+ int32_t width, height;
+ FindAVCDimensions(seqParamSet, &width, &height);
+
+ size_t stopOffset;
+ sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+ CHECK(picParamSet != NULL);
+
+ size_t csdSize =
+ 1 + 3 + 1 + 1
+ + 2 * 1 + seqParamSet->size()
+ + 1 + 2 * 1 + picParamSet->size();
+
+ sp<ABuffer> csd = new ABuffer(csdSize);
+ uint8_t *out = csd->data();
+
+ *out++ = 0x01; // configurationVersion
+ memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
+ out += 3;
+ *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
+ *out++ = 0xe0 | 1;
+
+ *out++ = seqParamSet->size() >> 8;
+ *out++ = seqParamSet->size() & 0xff;
+ memcpy(out, seqParamSet->data(), seqParamSet->size());
+ out += seqParamSet->size();
+
+ *out++ = 1;
+
+ *out++ = picParamSet->size() >> 8;
+ *out++ = picParamSet->size() & 0xff;
+ memcpy(out, picParamSet->data(), picParamSet->size());
+
+#if 0
+ LOGI("AVC seq param set");
+ hexdump(seqParamSet->data(), seqParamSet->size());
+#endif
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+
+ return meta;
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
new file mode 100644
index 0000000..d2e87f2
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ES_QUEUE_H_
+
+#define ES_QUEUE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct MetaData;
+
+struct ElementaryStreamQueue {
+ enum Mode {
+ H264,
+ AAC
+ };
+ ElementaryStreamQueue(Mode mode);
+
+ status_t appendData(const void *data, size_t size, int64_t timeUs);
+
+ sp<ABuffer> dequeueAccessUnit();
+
+ sp<MetaData> getFormat();
+
+private:
+ Mode mMode;
+
+ sp<ABuffer> mBuffer;
+ List<int64_t> mTimestamps;
+
+ sp<MetaData> mFormat;
+
+ sp<ABuffer> dequeueAccessUnitH264();
+ sp<ABuffer> dequeueAccessUnitAAC();
+
+ static sp<MetaData> MakeAACCodecSpecificData(
+ unsigned profile, unsigned sampling_freq_index,
+ unsigned channel_configuration);
+
+ static sp<MetaData> MakeAVCCodecSpecificData(
+ const sp<ABuffer> &accessUnit);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
+};
+
+} // namespace android
+
+#endif // ES_QUEUE_H_
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 2417305..c5257bb 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -157,7 +157,7 @@
}
}
- if (++numPacketsParsed > 1500) {
+ if (++numPacketsParsed > 2500) {
break;
}
}
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
index 79b9b1e..99e8f97 100644
--- a/native/include/android/configuration.h
+++ b/native/include/android/configuration.h
@@ -79,8 +79,8 @@
ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
- ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10,
- ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20,
+ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
+ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2,
ACONFIGURATION_MCC = 0x0001,
ACONFIGURATION_MNC = 0x0002,
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index eb86277..ce10f5b 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -159,7 +159,7 @@
try {
return ObbScanner.getObbInfo(filename);
} catch (IOException e) {
- Log.d(TAG, "Couldn't get OBB info", e);
+ Log.d(TAG, "Couldn't get OBB info for " + filename);
return null;
}
}
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index cfad939..2c13069 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -13,6 +13,6 @@
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
index c299e12..ae90cc8 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
index 157491e..a0e59cf 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png
index 3e317dd..2f66b1d 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
index aea18ed..1626895 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
old mode 100755
new mode 100644
index 1a25a2c..3c2e2b9
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
index 2134d49..bb41db0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index bf24a1f..16a3c17 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -366,6 +366,7 @@
mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
mCarouselView.setDefaultBitmap(mLoadingBitmap);
mCarouselView.setLoadingBitmap(mLoadingBitmap);
+ mCarouselView.setRezInCardCount(3.0f);
mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
mNoRecentsView = (View) findViewById(R.id.no_applications_message);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index 70d4d6a..d4491d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -20,6 +20,8 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews.RemoteView;
@@ -43,7 +45,7 @@
}
if (drawable instanceof AnimationDrawable) {
mAnim = (AnimationDrawable)drawable;
- if (mAttached) {
+ if (isShown()) {
mAnim.start();
}
} else {
@@ -67,9 +69,6 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mAnim != null) {
- mAnim.start();
- }
mAttached = true;
}
@@ -81,5 +80,17 @@
}
mAttached = false;
}
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int vis) {
+ super.onVisibilityChanged(changedView, vis);
+ if (mAnim != null) {
+ if (isShown()) {
+ mAnim.start();
+ } else {
+ mAnim.stop();
+ }
+ }
+ }
}
diff --git a/packages/TtsService/Android.mk b/packages/TtsService/Android.mk
index 75b26a2..a1a3b9f 100644
--- a/packages/TtsService/Android.mk
+++ b/packages/TtsService/Android.mk
@@ -8,7 +8,7 @@
LOCAL_PACKAGE_NAME := TtsService
LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c047522..bdf24d6 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -89,6 +89,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
@@ -152,8 +153,11 @@
// responsible for power management when displayed.
static final int KEYGUARD_LAYER = 14;
static final int KEYGUARD_DIALOG_LAYER = 15;
+ // the drag layer: input for drag-and-drop is associated with this window,
+ // which sits above all other focusable windows
+ static final int DRAG_LAYER = 16;
// things in here CAN NOT take focus, but are shown on top of everything else.
- static final int SYSTEM_OVERLAY_LAYER = 16;
+ static final int SYSTEM_OVERLAY_LAYER = 17;
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -839,6 +843,8 @@
return TOAST_LAYER;
case TYPE_WALLPAPER:
return WALLPAPER_LAYER;
+ case TYPE_DRAG:
+ return DRAG_LAYER;
}
Log.e(TAG, "Unknown window type: " + type);
return APPLICATION_LAYER;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 97b8086..8527059 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1856,6 +1856,8 @@
if (--(track->mRetryCount) <= 0) {
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun
+ cblk->flags |= CBLK_DISABLED_ON;
} else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -2790,7 +2792,7 @@
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
// Force underrun condition to avoid false underrun callback until first data is
- // written to buffer
+ // written to buffer (other flags are cleared)
mCblk->flags = CBLK_UNDERRUN_ON;
} else {
mBuffer = sharedBuffer->pointer();
@@ -2813,7 +2815,7 @@
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
// Force underrun condition to avoid false underrun callback until first data is
- // written to buffer
+ // written to buffer (other flags are cleared)
mCblk->flags = CBLK_UNDERRUN_ON;
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
}
@@ -3794,6 +3796,8 @@
AudioBufferProvider::Buffer buffer;
sp<RecordTrack> activeTrack;
+ nsecs_t lastWarning = 0;
+
// start recording
while (!exitPending()) {
@@ -3935,8 +3939,13 @@
}
// client isn't retrieving buffers fast enough
else {
- if (!mActiveTrack->setOverflow())
- LOGW("RecordThread: buffer overflow");
+ if (!mActiveTrack->setOverflow()) {
+ nsecs_t now = systemTime();
+ if ((now - lastWarning) > kWarningThrottle) {
+ LOGW("RecordThread: buffer overflow");
+ lastWarning = now;
+ }
+ }
// Release the processor for a while before asking for a new buffer.
// This will give the application more chance to read from the buffer and
// clear the overflow.
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6095117..880befd 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -68,7 +68,7 @@
*/
public class ConnectivityService extends IConnectivityManager.Stub {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String TAG = "ConnectivityService";
// how long to wait before switching back to a radio's default network
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 0c3a0e6..28126b9 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -58,6 +58,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -605,6 +606,8 @@
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (FileNotFoundException e) {
+ // Don't be noisy, this is normal if we haven't defined any policies.
} catch (IOException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IndexOutOfBoundsException e) {
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index fe306b3..a960097 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -342,6 +342,7 @@
if (toChannel == null) {
throw new IllegalArgumentException("toChannel must not be null.");
}
+ Slog.d(TAG, "transferring touch focus");
return nativeTransferTouchFocus(fromChannel, toChannel);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 675760f..de28375 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -951,10 +951,11 @@
}
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token);
return;
}
@@ -1048,6 +1049,7 @@
public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
@@ -1058,7 +1060,7 @@
// focus in the window manager, to allow this call to
// be made before input is started in it.
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
- Slog.w(TAG, "Ignoring showSoftInput of: " + client);
+ Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
} catch (RemoteException e) {
@@ -1112,6 +1114,7 @@
public boolean hideSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
+ int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
@@ -1122,7 +1125,8 @@
// focus in the window manager, to allow this call to
// be made before input is started in it.
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
- Slog.w(TAG, "Ignoring hideSoftInput of: " + client);
+ if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+ + uid + ": " + client);
return false;
}
} catch (RemoteException e) {
@@ -1257,7 +1261,8 @@
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
- Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
+ Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of uid "
+ + Binder.getCallingUid() + ": " + client);
}
mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
@@ -1290,7 +1295,8 @@
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
} else if (mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ + " token: " + token);
return;
}
@@ -1306,7 +1312,8 @@
public void hideMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+ if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
+ + Binder.getCallingUid() + " token: " + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -1321,7 +1328,8 @@
public void showMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+ Slog.w(TAG, "Ignoring showMySoftInput of uid "
+ + Binder.getCallingUid() + " token: " + token);
return;
}
long ident = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 8ab65e9..e47de13 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -28,6 +28,7 @@
import java.util.Set;
import android.util.Log;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
@@ -92,10 +93,12 @@
}
boolean dumpMap(PrintWriter out, String titlePrefix, String title,
- String prefix, Map<String, ArrayList<F>> map, String packageName) {
+ String prefix, Map<String, ArrayList<F>> map, String packageName,
+ boolean printFilter) {
String eprefix = prefix + " ";
String fprefix = prefix + " ";
boolean printedSomething = false;
+ Printer printer = null;
for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
ArrayList<F> a = e.getValue();
final int N = a.size();
@@ -115,37 +118,44 @@
}
printedSomething = true;
dumpFilter(out, fprefix, filter);
+ if (printFilter) {
+ if (printer == null) {
+ printer = new PrintWriterPrinter(out);
+ }
+ filter.dump(printer, fprefix + " ");
+ }
}
}
return printedSomething;
}
- public boolean dump(PrintWriter out, String title, String prefix, String packageName) {
+ public boolean dump(PrintWriter out, String title, String prefix, String packageName,
+ boolean printFilter) {
String innerPrefix = prefix + " ";
String sepPrefix = "\n" + prefix;
String curPrefix = title + "\n" + prefix;
if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
- mTypeToFilter, packageName)) {
+ mTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
- mBaseTypeToFilter, packageName)) {
+ mBaseTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
- mWildTypeToFilter, packageName)) {
+ mWildTypeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
- mSchemeToFilter, packageName)) {
+ mSchemeToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
- mActionToFilter, packageName)) {
+ mActionToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
- mTypedActionToFilter, packageName)) {
+ mTypedActionToFilter, packageName, printFilter)) {
curPrefix = sepPrefix;
}
return curPrefix == sepPrefix;
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index aa1bcf7..19ea4e1 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -58,6 +58,7 @@
import android.util.Slog;
import android.util.PrintWriterPrinter;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.location.GpsNetInitiatedHandler;
import com.android.server.location.GeocoderProxy;
@@ -116,13 +117,15 @@
private static boolean sProvidersLoaded = false;
private final Context mContext;
+ private final String mNetworkLocationProviderPackageName;
+ private final String mGeocodeProviderPackageName;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
// Cache the real providers for use in addTestProvider() and removeTestProvider()
- LocationProviderInterface mNetworkLocationProvider;
+ LocationProviderProxy mNetworkLocationProvider;
LocationProviderInterface mGpsLocationProvider;
// Handler messages
@@ -472,20 +475,18 @@
mEnabledProviders.add(passiveProvider.getName());
// initialize external network location and geocoder services
- PackageManager pm = mContext. getPackageManager();
- Resources resources = mContext.getResources();
- String serviceName = resources.getString(
- com.android.internal.R.string.config_networkLocationProvider);
- if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
+ PackageManager pm = mContext.getPackageManager();
+ if (mNetworkLocationProviderPackageName != null &&
+ pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
mNetworkLocationProvider =
new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
- serviceName, mLocationHandler);
+ mNetworkLocationProviderPackageName, mLocationHandler);
addProvider(mNetworkLocationProvider);
}
- serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider);
- if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
- mGeocodeProvider = new GeocoderProxy(mContext, serviceName);
+ if (mGeocodeProviderPackageName != null &&
+ pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
+ mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
}
updateProvidersLocked();
@@ -497,6 +498,12 @@
public LocationManagerService(Context context) {
super();
mContext = context;
+ Resources resources = context.getResources();
+ mNetworkLocationProviderPackageName = resources.getString(
+ com.android.internal.R.string.config_networkLocationProvider);
+ mGeocodeProviderPackageName = resources.getString(
+ com.android.internal.R.string.config_geocodeProvider);
+ mPackageMonitor.register(context, true);
if (LOCAL_LOGV) {
Slog.v(TAG, "Constructed LocationManager Service");
@@ -1921,6 +1928,23 @@
}
};
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ String packageDot = packageName + ".";
+
+ // reconnect to external providers after their packages have been updated
+ if (mNetworkLocationProvider != null &&
+ mNetworkLocationProviderPackageName.startsWith(packageDot)) {
+ mNetworkLocationProvider.reconnect();
+ }
+ if (mGeocodeProvider != null &&
+ mGeocodeProviderPackageName.startsWith(packageDot)) {
+ mGeocodeProvider.reconnect();
+ }
+ }
+ };
+
// Wake locks
private void incrementPendingBroadcasts() {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ca8fc52..d3b338c 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.HexDump;
import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver;
@@ -44,15 +45,22 @@
import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
import android.os.storage.StorageResultCode;
+import android.security.MessageDigest;
import android.util.Slog;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
/**
* MountService implements back-end services for platform storage
@@ -71,6 +79,8 @@
private static final String VOLD_TAG = "VoldConnector";
+ protected static final int MAX_OBBS = 8;
+
/*
* Internal vold volume state constants
*/
@@ -151,15 +161,19 @@
* Mounted OBB tracking information. Used to track the current state of all
* OBBs.
*/
- final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
+ final private Map<Integer, Integer> mObbUidUsage = new HashMap<Integer, Integer>();
+ final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient {
- public ObbState(String filename, IObbActionListener token, int callerUid) {
+ public ObbState(String filename, IObbActionListener token, int callerUid)
+ throws RemoteException {
this.filename = filename;
this.token = token;
this.callerUid = callerUid;
mounted = false;
+
+ getBinder().linkToDeath(this, 0);
}
// OBB source filename
@@ -174,14 +188,33 @@
// Whether this is mounted currently.
boolean mounted;
+ public IBinder getBinder() {
+ return token.asBinder();
+ }
+
@Override
public void binderDied() {
ObbAction action = new UnmountObbAction(this, true);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+ }
- removeObbState(this);
+ public void cleanUp() {
+ getBinder().unlinkToDeath(this, 0);
+ }
- token.asBinder().unlinkToDeath(this, 0);
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ObbState{");
+ sb.append("filename=");
+ sb.append(filename);
+ sb.append(",token=");
+ sb.append(token.toString());
+ sb.append(",callerUid=");
+ sb.append(callerUid);
+ sb.append(",mounted=");
+ sb.append(mounted);
+ sb.append('}');
+ return sb.toString();
}
}
@@ -481,6 +514,34 @@
mPms.updateExternalMediaStatus(true, false);
}
}
+
+ // Remove all OBB mappings and listeners from this path
+ synchronized (mObbMounts) {
+ final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
+
+ final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
+ while (i.hasNext()) {
+ final Entry<String, ObbState> obbEntry = i.next();
+
+ // If this entry's source file is in the volume path that got
+ // unmounted, remove it because it's no longer valid.
+ if (obbEntry.getKey().startsWith(path)) {
+ obbStatesToRemove.add(obbEntry.getValue());
+ }
+ }
+
+ for (final ObbState obbState : obbStatesToRemove) {
+ removeObbState(obbState);
+
+ try {
+ obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Couldn't send unmount notification for OBB: "
+ + obbState.filename);
+ }
+ }
+ }
+
String oldState = mLegacyState;
mLegacyState = state;
@@ -1507,11 +1568,18 @@
public boolean isObbMounted(String filename) {
synchronized (mObbMounts) {
- return mObbPathToStateMap.containsKey(filename);
+ final ObbState obbState = mObbPathToStateMap.get(filename);
+ if (obbState != null) {
+ synchronized (obbState) {
+ return obbState.mounted;
+ }
+ }
}
+ return false;
}
- public void mountObb(String filename, String key, IObbActionListener token) {
+ public void mountObb(String filename, String key, IObbActionListener token)
+ throws RemoteException {
waitForReady();
warnOnNotMounted();
@@ -1525,21 +1593,44 @@
synchronized (mObbMounts) {
if (isObbMounted(filename)) {
- throw new IllegalArgumentException("OBB file is already mounted");
+ try {
+ token.onObbResult(filename, Environment.MEDIA_MOUNTED);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Could not send unmount notification for: " + filename);
+ }
+ return;
}
final int callerUid = Binder.getCallingUid();
+
+ final Integer uidUsage = mObbUidUsage.get(callerUid);
+ if (uidUsage != null && uidUsage > MAX_OBBS) {
+ throw new IllegalStateException("Maximum number of OBBs mounted!");
+ }
+
obbState = new ObbState(filename, token, callerUid);
addObbState(obbState);
}
- try {
- token.asBinder().linkToDeath(obbState, 0);
- } catch (RemoteException rex) {
- Slog.e(TAG, "Failed to link to listener death");
+ String hashedKey = null;
+ if (key != null) {
+ final MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Could not load MD5 algorithm", e);
+ try {
+ token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
+ } catch (RemoteException e1) {
+ Slog.d(TAG, "Could not send unmount notification for: " + filename);
+ }
+ return;
+ }
+
+ hashedKey = HexDump.toHexString(md.digest(key.getBytes()));
}
- MountObbAction action = new MountObbAction(obbState, key);
+ ObbAction action = new MountObbAction(obbState, hashedKey);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
@@ -1557,18 +1648,24 @@
synchronized (mObbMounts) {
if (!isObbMounted(filename)) {
- throw new IllegalArgumentException("OBB is not mounted");
+ try {
+ token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Could not send unmount notification for: " + filename);
+ }
+ return;
}
+
obbState = mObbPathToStateMap.get(filename);
if (Binder.getCallingUid() != obbState.callerUid) {
throw new SecurityException("caller UID does not match original mount caller UID");
- } else if (!token.asBinder().equals(obbState.token.asBinder())) {
+ } else if (!token.asBinder().equals(obbState.getBinder())) {
throw new SecurityException("caller does not match original mount caller");
}
}
- UnmountObbAction action = new UnmountObbAction(obbState, force);
+ ObbAction action = new UnmountObbAction(obbState, force);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
@@ -1577,26 +1674,57 @@
private void addObbState(ObbState obbState) {
synchronized (mObbMounts) {
- List<ObbState> obbStates = mObbMounts.get(obbState.token);
+ List<ObbState> obbStates = mObbMounts.get(obbState.getBinder());
if (obbStates == null) {
obbStates = new ArrayList<ObbState>();
- mObbMounts.put(obbState.token, obbStates);
+ mObbMounts.put(obbState.getBinder(), obbStates);
}
obbStates.add(obbState);
mObbPathToStateMap.put(obbState.filename, obbState);
+
+ // Track the number of OBBs used by this UID.
+ final int uid = obbState.callerUid;
+ final Integer uidUsage = mObbUidUsage.get(uid);
+ if (uidUsage == null) {
+ mObbUidUsage.put(uid, 1);
+ } else {
+ mObbUidUsage.put(uid, uidUsage + 1);
+ }
}
}
private void removeObbState(ObbState obbState) {
synchronized (mObbMounts) {
- final List<ObbState> obbStates = mObbMounts.get(obbState.token);
+ final List<ObbState> obbStates = mObbMounts.get(obbState.getBinder());
if (obbStates != null) {
obbStates.remove(obbState);
}
if (obbStates == null || obbStates.isEmpty()) {
- mObbMounts.remove(obbState.token);
+ mObbMounts.remove(obbState.getBinder());
+ obbState.cleanUp();
}
mObbPathToStateMap.remove(obbState.filename);
+
+ // Track the number of OBBs used by this UID.
+ final int uid = obbState.callerUid;
+ final Integer uidUsage = mObbUidUsage.get(uid);
+ if (uidUsage == null) {
+ Slog.e(TAG, "Called removeObbState for UID that isn't in map: " + uid);
+ } else {
+ final int newUsage = uidUsage - 1;
+ if (newUsage == 0) {
+ mObbUidUsage.remove(uid);
+ } else {
+ mObbUidUsage.put(uid, newUsage);
+ }
+ }
+ }
+ }
+
+ private void replaceObbState(ObbState oldObbState, ObbState newObbState) {
+ synchronized (mObbMounts) {
+ removeObbState(oldObbState);
+ addObbState(newObbState);
}
}
@@ -1627,20 +1755,16 @@
Slog.e(TAG, "Failed to bind to media container service");
action.handleError();
return;
- } else {
- // Once we bind to the service, the first
- // pending request will be processed.
- mActions.add(action);
- }
- } else {
- // Already bound to the service. Just make
- // sure we trigger off processing the first request.
- if (mActions.size() == 0) {
- mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
}
mActions.add(action);
+ break;
}
+
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mActions.add(action);
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
break;
}
case OBB_MCS_BOUND: {
@@ -1773,6 +1897,29 @@
abstract void handleExecute() throws RemoteException, IOException;
abstract void handleError();
+
+ protected ObbInfo getObbInfo() throws IOException {
+ ObbInfo obbInfo;
+ try {
+ obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
+ + mObbState.filename);
+ obbInfo = null;
+ }
+ if (obbInfo == null) {
+ throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+ }
+ return obbInfo;
+ }
+
+ protected void sendNewStatusOrIgnore(String filename, String status) {
+ try {
+ mObbState.token.onObbResult(filename, status);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ }
}
class MountObbAction extends ObbAction {
@@ -1783,56 +1930,89 @@
mKey = key;
}
- public void handleExecute() throws RemoteException, IOException {
- final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
- if (obbInfo == null) {
- throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+ public void handleExecute() throws IOException, RemoteException {
+ final ObbInfo obbInfo = getObbInfo();
+
+ /*
+ * If someone tried to trick us with some weird characters, rectify
+ * it here.
+ */
+ if (!mObbState.filename.equals(obbInfo.filename)) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
+ + obbInfo.filename);
+
+ synchronized (mObbMounts) {
+ /*
+ * If the real filename is already mounted, discard this
+ * state and notify the caller that the OBB is already
+ * mounted.
+ */
+ if (isObbMounted(obbInfo.filename)) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
+
+ removeObbState(mObbState);
+ sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
+ return;
+ }
+
+ /*
+ * It's not already mounted, so we have to replace the state
+ * with the state containing the actual filename.
+ */
+ ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
+ mObbState.callerUid);
+ replaceObbState(mObbState, newObbState);
+ mObbState = newObbState;
+ }
}
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
- if (mKey == null) {
- mKey = "none";
- }
-
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
- mObbState.callerUid);
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
+ boolean mounted = false;
+ int rc;
+ synchronized (mObbState) {
+ if (mObbState.mounted) {
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+ return;
}
- }
- if (rc == StorageResultCode.OperationSucceeded) {
+ rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
+ mObbState.callerUid);
try {
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
- } catch (RemoteException e) {
- Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
}
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ mObbState.mounted = mounted = true;
+ }
+ }
+
+ if (mounted) {
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
} else {
Slog.e(TAG, "Couldn't mount OBB file: " + rc);
// We didn't succeed, so remove this from the mount-set.
removeObbState(mObbState);
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
}
}
public void handleError() {
removeObbState(mObbState);
- try {
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
- }
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
}
@Override
@@ -1858,51 +2038,69 @@
mForceUnmount = force;
}
- public void handleExecute() throws RemoteException, IOException {
- final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
- if (obbInfo == null) {
- throw new IOException("Couldn't read OBB file: " + mObbState.filename);
- }
+ public void handleExecute() throws IOException {
+ final ObbInfo obbInfo = getObbInfo();
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb unmount %s%s", mObbState.filename,
- (mForceUnmount ? " force" : ""));
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
+ /*
+ * If someone tried to trick us with some weird characters, rectify
+ * it here.
+ */
+ synchronized (mObbMounts) {
+ if (!isObbMounted(obbInfo.filename)) {
+ removeObbState(mObbState);
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+ return;
+ }
+
+ if (!mObbState.filename.equals(obbInfo.filename)) {
+ removeObbState(mObbState);
+ mObbState = mObbPathToStateMap.get(obbInfo.filename);
}
}
- if (rc == StorageResultCode.OperationSucceeded) {
+ boolean unmounted = false;
+ synchronized (mObbState) {
+ if (!mObbState.mounted) {
+ sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
+ return;
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ // If it's not mounted then we've already won.
+ rc = StorageResultCode.OperationSucceeded;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ mObbState.mounted = false;
+ unmounted = true;
+ }
+ }
+
+ if (unmounted) {
removeObbState(mObbState);
- try {
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
- } catch (RemoteException e) {
- Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
- }
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
} else {
- try {
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
- } catch (RemoteException e) {
- Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
- }
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
}
}
public void handleError() {
removeObbState(mObbState);
- try {
- mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
- }
+ sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
}
@Override
@@ -1917,9 +2115,33 @@
sb.append(mObbState.callerUid);
sb.append(",token=");
sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+ sb.append(",binder=");
+ sb.append(mObbState.getBinder().toString());
sb.append('}');
return sb.toString();
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ActivityManager from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " without permission " + android.Manifest.permission.DUMP);
+ return;
+ }
+
+ pw.println(" mObbMounts:");
+
+ synchronized (mObbMounts) {
+ final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
+
+ for (final List<ObbState> obbStates : obbStateLists) {
+ for (final ObbState obbState : obbStates) {
+ pw.print(" "); pw.println(obbState.toString());
+ }
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 05abd99..8dbd3e7 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -55,7 +55,7 @@
class NetworkManagementService extends INetworkManagementService.Stub {
private static final String TAG = "NetworkManagmentService";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
class NetdResponseCode {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 8f90756..1f97bee 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1102,27 +1102,6 @@
final File permFile = new File(Environment.getRootDirectory(),
"etc/permissions/platform.xml");
readPermissionsFromXml(permFile);
-
- StringBuilder sb = new StringBuilder(128);
- sb.append("Libs:");
- Iterator<String> it = mSharedLibraries.keySet().iterator();
- while (it.hasNext()) {
- sb.append(' ');
- String name = it.next();
- sb.append(name);
- sb.append(':');
- sb.append(mSharedLibraries.get(name));
- }
- Log.i(TAG, sb.toString());
-
- sb.setLength(0);
- sb.append("Features:");
- it = mAvailableFeatures.keySet().iterator();
- while (it.hasNext()) {
- sb.append(' ');
- sb.append(it.next());
- }
- Log.i(TAG, sb.toString());
}
private void readPermissionsFromXml(File permFile) {
@@ -2632,7 +2611,7 @@
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
Log.i(TAG, "Package " + ps.name + " at " + scanFile
- + "ignored: updated version " + ps.versionCode
+ + " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
@@ -6866,7 +6845,18 @@
return;
}
+ boolean dumpStar = true;
+ boolean dumpLibs = false;
+ boolean dumpFeatures = false;
+ boolean dumpResolvers = false;
+ boolean dumpPermissions = false;
+ boolean dumpPackages = false;
+ boolean dumpSharedUsers = false;
+ boolean dumpMessages = false;
+ boolean dumpProviders = false;
+
String packageName = null;
+ boolean showFilters = false;
int opti = 0;
while (opti < args.length) {
@@ -6879,10 +6869,22 @@
// Right now we only know how to print all.
} else if ("-h".equals(opt)) {
pw.println("Package manager dump options:");
- pw.println(" [-h] [cmd] ...");
+ pw.println(" [-h] [-f] [cmd] ...");
+ pw.println(" -f: print details of intent filters");
+ pw.println(" -h: print this help");
pw.println(" cmd may be one of:");
- pw.println(" [package.name]: info about given package");
+ pw.println(" l[ibraries]: list known shared libraries");
+ pw.println(" f[ibraries]: list device features");
+ pw.println(" r[esolvers]: dump intent resolvers");
+ pw.println(" perm[issions]: dump permissions");
+ pw.println(" prov[iders]: dump content providers");
+ pw.println(" p[ackages]: dump installed packages");
+ pw.println(" s[hared-users]: dump shared user IDs");
+ pw.println(" m[essages]: print collected runtime messages");
+ pw.println(" <package.name>: info about given package");
return;
+ } else if ("-f".equals(opt)) {
+ showFilters = true;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
@@ -6895,32 +6897,87 @@
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
+ } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
+ dumpStar = false;
+ dumpLibs = true;
+ } else if ("f".equals(cmd) || "features".equals(cmd)) {
+ dumpStar = false;
+ dumpFeatures = true;
+ } else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
+ dumpStar = false;
+ dumpResolvers = true;
+ } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
+ dumpStar = false;
+ dumpPermissions = true;
+ } else if ("p".equals(cmd) || "packages".equals(cmd)) {
+ dumpStar = false;
+ dumpPackages = true;
+ } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
+ dumpStar = false;
+ dumpSharedUsers = true;
+ } else if ("prov".equals(cmd) || "providers".equals(cmd)) {
+ dumpStar = false;
+ dumpProviders = true;
+ } else if ("m".equals(cmd) || "messages".equals(cmd)) {
+ dumpStar = false;
+ dumpMessages = true;
}
}
boolean printedTitle = false;
synchronized (mPackages) {
- if (mActivities.dump(pw, "Activity Resolver Table:", " ", packageName)) {
+ if ((dumpStar || dumpLibs) && packageName == null) {
+ if (printedTitle) pw.println(" ");
printedTitle = true;
+ pw.println("Libraries:");
+ Iterator<String> it = mSharedLibraries.keySet().iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ pw.print(" ");
+ pw.print(name);
+ pw.print(" -> ");
+ pw.println(mSharedLibraries.get(name));
+ }
}
- if (mReceivers.dump(pw, printedTitle
- ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
- " ", packageName)) {
+
+ if ((dumpStar || dumpFeatures) && packageName == null) {
+ if (printedTitle) pw.println(" ");
printedTitle = true;
+ pw.println("Features:");
+ Iterator<String> it = mAvailableFeatures.keySet().iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ pw.print(" ");
+ pw.println(name);
+ }
}
- if (mServices.dump(pw, printedTitle
- ? "\nService Resolver Table:" : "Service Resolver Table:",
- " ", packageName)) {
- printedTitle = true;
+
+ if (dumpStar || dumpResolvers) {
+ if (mActivities.dump(pw, printedTitle
+ ? "\nActivity Resolver Table:" : "Activity Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mReceivers.dump(pw, printedTitle
+ ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mServices.dump(pw, printedTitle
+ ? "\nService Resolver Table:" : "Service Resolver Table:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
+ if (mSettings.mPreferredActivities.dump(pw, printedTitle
+ ? "\nPreferred Activities:" : "Preferred Activities:",
+ " ", packageName, showFilters)) {
+ printedTitle = true;
+ }
}
- if (mSettings.mPreferredActivities.dump(pw, printedTitle
- ? "\nPreferred Activities:" : "Preferred Activities:",
- " ", packageName)) {
- printedTitle = true;
- }
+
boolean printedSomething = false;
- {
+ if (dumpStar || dumpPermissions) {
for (BasePermission p : mSettings.mPermissions.values()) {
if (packageName != null && !packageName.equals(p.sourcePackage)) {
continue;
@@ -6947,9 +7004,27 @@
}
}
}
+
+ if (dumpStar || dumpProviders) {
+ printedSomething = false;
+ for (PackageParser.Provider p : mProviders.values()) {
+ if (packageName != null && !packageName.equals(p.info.packageName)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Registered ContentProviders:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
+ pw.println(p.toString());
+ }
+ }
+
printedSomething = false;
SharedUserSetting packageSharedUser = null;
- {
+ if (dumpStar || dumpPackages) {
for (PackageSetting ps : mSettings.mPackages.values()) {
if (packageName != null && !packageName.equals(ps.realName)
&& !packageName.equals(ps.name)) {
@@ -7052,52 +7127,54 @@
}
}
printedSomething = false;
- if (mSettings.mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e
- : mSettings.mRenamedPackages.entrySet()) {
- if (packageName != null && !packageName.equals(e.getKey())
- && !packageName.equals(e.getValue())) {
- continue;
+ if (dumpStar || dumpPackages) {
+ if (mSettings.mRenamedPackages.size() > 0) {
+ for (HashMap.Entry<String, String> e
+ : mSettings.mRenamedPackages.entrySet()) {
+ if (packageName != null && !packageName.equals(e.getKey())
+ && !packageName.equals(e.getValue())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
+ pw.println(e.getValue());
}
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Renamed packages:");
- printedSomething = true;
- printedTitle = true;
+ }
+ printedSomething = false;
+ if (mSettings.mDisabledSysPackages.size() > 0) {
+ for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Hidden system packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+ if (ps.realName != null) {
+ pw.print(" compat name="); pw.println(ps.name);
+ }
+ pw.print(" userId="); pw.println(ps.userId);
+ pw.print(" sharedUser="); pw.println(ps.sharedUser);
+ pw.print(" codePath="); pw.println(ps.codePathString);
+ pw.print(" resourcePath="); pw.println(ps.resourcePathString);
}
- pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
- pw.println(e.getValue());
}
}
printedSomething = false;
- if (mSettings.mDisabledSysPackages.size() > 0) {
- for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Hidden system packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
- }
- pw.print(" userId="); pw.println(ps.userId);
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- pw.print(" codePath="); pw.println(ps.codePathString);
- pw.print(" resourcePath="); pw.println(ps.resourcePathString);
- }
- }
- printedSomething = false;
- {
+ if (dumpStar || dumpSharedUsers) {
for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
if (packageName != null && su != packageSharedUser) {
continue;
@@ -7120,11 +7197,11 @@
}
}
- if (packageName == null) {
+ if ((dumpStar || dumpMessages) && packageName == null) {
if (printedTitle) pw.println(" ");
printedTitle = true;
pw.println("Settings parse messages:");
- pw.println(mSettings.mReadMessages.toString());
+ pw.print(mSettings.mReadMessages.toString());
pw.println(" ");
pw.println("Package warning messages:");
@@ -7135,29 +7212,12 @@
int avail = in.available();
byte[] data = new byte[avail];
in.read(data);
- pw.println(new String(data));
+ pw.print(new String(data));
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
}
-
- synchronized (mProviders) {
- boolean printedSomething = false;
- for (PackageParser.Provider p : mProviders.values()) {
- if (packageName != null && !packageName.equals(p.info.packageName)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Registered ContentProviders:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
- pw.println(p.toString());
- }
- }
}
static final class BasePermission {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 4532c1c..29a9a7e 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -871,7 +871,7 @@
mWakeLockState = mLocks.gatherState();
// goes in the middle to reduce flicker
if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
- userActivity(SystemClock.uptimeMillis(), false);
+ userActivity(SystemClock.uptimeMillis(), -1, false, OTHER_EVENT, false);
}
setPowerState(mWakeLockState | mUserState);
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 7100cc5..0c1417d 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -40,6 +40,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -51,6 +52,8 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -93,6 +96,7 @@
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Display;
+import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
@@ -104,6 +108,8 @@
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputHandler;
+import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -159,6 +165,7 @@
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
+ static final boolean DEBUG_DRAG = true;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
@@ -486,6 +493,311 @@
boolean mTurnOnScreen;
/**
+ * Drag/drop state
+ */
+ class DragState {
+ IBinder mToken;
+ Surface mSurface;
+ boolean mLocalOnly;
+ ClipData mData;
+ ClipDescription mDataDescription;
+ float mThumbOffsetX, mThumbOffsetY;
+ InputChannel mServerChannel, mClientChannel;
+ WindowState mTargetWindow;
+ ArrayList<WindowState> mNotifiedWindows;
+ boolean mDragEnded;
+
+ private final Rect tmpRect = new Rect();
+
+ DragState(IBinder token, Surface surface, boolean localOnly) {
+ mToken = token;
+ mSurface = surface;
+ mLocalOnly = localOnly;
+ mNotifiedWindows = new ArrayList<WindowState>();
+ }
+
+ void reset() {
+ if (mSurface != null) {
+ mSurface.destroy();
+ }
+ mSurface = null;
+ mLocalOnly = false;
+ mToken = null;
+ mData = null;
+ mThumbOffsetX = mThumbOffsetY = 0;
+ mNotifiedWindows = null;
+ }
+
+ void register() {
+ if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel");
+ if (mClientChannel != null) {
+ Slog.e(TAG, "Duplicate register of drag input channel");
+ } else {
+ InputChannel[] channels = InputChannel.openInputChannelPair("drag");
+ mServerChannel = channels[0];
+ mClientChannel = channels[1];
+ mInputManager.registerInputChannel(mServerChannel);
+ InputQueue.registerInputChannel(mClientChannel, mDragInputHandler,
+ mH.getLooper().getQueue());
+ }
+ }
+
+ void unregister() {
+ if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel");
+ if (mClientChannel == null) {
+ Slog.e(TAG, "Unregister of nonexistent drag input channel");
+ } else {
+ mInputManager.unregisterInputChannel(mServerChannel);
+ InputQueue.unregisterInputChannel(mClientChannel);
+ mClientChannel.dispose();
+ mClientChannel = null;
+ mServerChannel = null;
+ }
+ }
+
+ /* call out to each visible window/session informing it about the drag
+ */
+ void broadcastDragStartedLw() {
+ // Cache a base-class instance of the clip metadata so that parceling
+ // works correctly in calling out to the apps.
+ mDataDescription = new ClipDescription(mData);
+ mNotifiedWindows.clear();
+
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "broadcasting DRAG_STARTED of " + mDataDescription);
+ }
+
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+ mDataDescription, null);
+ for (WindowState ws : mWindows) {
+ sendDragStartedLw(ws, evt);
+ }
+ evt.recycle();
+ }
+
+ /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+ * designated window is potentially a drop recipient. There are race situations
+ * around DRAG_ENDED broadcast, so we make sure that once we've declared that
+ * the drag has ended, we never send out another DRAG_STARTED for this drag action.
+ */
+ private void sendDragStartedLw(WindowState newWin, final DragEvent event) {
+ if (!mDragEnded && newWin.isPotentialDragTarget()) {
+ try {
+ newWin.mClient.dispatchDragEvent(event);
+ // track each window that we've notified that the drag is starting
+ mNotifiedWindows.add(newWin);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to drag-start window " + newWin);
+ }
+ }
+ }
+
+ /* helper - construct and send a DRAG_STARTED event only if the window has not
+ * previously been notified, i.e. it became visible after the drag operation
+ * was begun. This is a rare case.
+ */
+ private void sendDragStartedIfNeededLw(WindowState newWin) {
+ // If we have sent the drag-started, we needn't do so again
+ for (WindowState ws : mNotifiedWindows) {
+ if (ws == newWin) {
+ return;
+ }
+ }
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
+ }
+ DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+ mDataDescription, null);
+ sendDragStartedLw(newWin, event);
+ event.recycle();
+ }
+
+ void broadcastDragEnded() {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "broadcasting DRAG_ENDED");
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, 0, 0, null, null);
+ synchronized (mWindowMap) {
+ for (WindowState ws: mNotifiedWindows) {
+ try {
+ ws.mClient.dispatchDragEvent(evt);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to drag-end window " + ws);
+ }
+ }
+ mNotifiedWindows.clear();
+ mDragEnded = true;
+ }
+ evt.recycle();
+ }
+
+ void notifyMoveLw(float x, float y) {
+ WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ try {
+ // have we dragged over a new window?
+ if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow);
+ }
+ // force DRAG_EXITED_EVENT if appropriate
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
+ 0, 0, null, null);
+ mTargetWindow.mClient.dispatchDragEvent(evt);
+ evt.recycle();
+ }
+ if (touchedWin != null) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin);
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
+ x, y, null, null);
+ touchedWin.mClient.dispatchDragEvent(evt);
+ evt.recycle();
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "can't send drag notification to windows");
+ }
+ mTargetWindow = touchedWin;
+ }
+
+ // Tell the drop target about the data, and then broadcast the drag-ended notification
+ void notifyDropLw(float x, float y) {
+ WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ if (touchedWin != null) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DROP to " + touchedWin);
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, x, y, null, mData);
+ try {
+ touchedWin.mClient.dispatchDragEvent(evt);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "can't send drop notification to win " + touchedWin);
+ }
+ evt.recycle();
+ }
+ }
+
+ // Find the visible, touch-deliverable window under the given point
+ private WindowState getTouchedWinAtPointLw(float xf, float yf) {
+ WindowState touchedWin = null;
+ final int x = (int) xf;
+ final int y = (int) yf;
+ final ArrayList<WindowState> windows = mWindows;
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ WindowState child = windows.get(i);
+ final int flags = child.mAttrs.flags;
+ if (!child.isVisibleLw()) {
+ // not visible == don't tell about drags
+ continue;
+ }
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ // not touchable == don't tell about drags
+ continue;
+ }
+ // account for the window's decor etc
+ tmpRect.set(child.mFrame);
+ if (child.mTouchableInsets == ViewTreeObserver
+ .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
+ // The point is inside of the window if it is
+ // inside the frame, AND the content part of that
+ // frame that was given by the application.
+ tmpRect.left += child.mGivenContentInsets.left;
+ tmpRect.top += child.mGivenContentInsets.top;
+ tmpRect.right -= child.mGivenContentInsets.right;
+ tmpRect.bottom -= child.mGivenContentInsets.bottom;
+ } else if (child.mTouchableInsets == ViewTreeObserver
+ .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
+ // The point is inside of the window if it is
+ // inside the frame, AND the visible part of that
+ // frame that was given by the application.
+ tmpRect.left += child.mGivenVisibleInsets.left;
+ tmpRect.top += child.mGivenVisibleInsets.top;
+ tmpRect.right -= child.mGivenVisibleInsets.right;
+ tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+ }
+ final int touchFlags = flags &
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ if (tmpRect.contains(x, y) || touchFlags == 0) {
+ // Found it
+ touchedWin = child;
+ break;
+ }
+ }
+
+ return touchedWin;
+ }
+ }
+
+ DragState mDragState = null;
+ private final InputHandler mDragInputHandler = new BaseInputHandler() {
+ @Override
+ public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ boolean endDrag = false;
+ final float newX = event.getRawX();
+ final float newY = event.getRawY();
+
+ try {
+ if (mDragState != null) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (DEBUG_DRAG) {
+ Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
+ }
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ synchronized (mWindowMap) {
+ // move the surface to the latest touch point
+ mDragState.mSurface.openTransaction();
+ mDragState.mSurface.setPosition((int)(newX - mDragState.mThumbOffsetX),
+ (int)(newY - mDragState.mThumbOffsetY));
+ mDragState.mSurface.closeTransaction();
+
+ // tell the involved window(s) where we are
+ mDragState.notifyMoveLw(newX, newY);
+ }
+ } break;
+
+ case MotionEvent.ACTION_UP: {
+ if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+ + newX + "," + newY);
+ synchronized (mWindowMap) {
+ mDragState.notifyDropLw(newX, newY);
+ }
+ endDrag = true;
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (DEBUG_DRAG) Slog.d(TAG, "Drag cancelled!");
+ endDrag = true;
+ } break;
+ }
+
+ if (endDrag) {
+ if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state");
+ // tell all the windows that the drag has ended
+ mDragState.broadcastDragEnded();
+
+ // stop intercepting input
+ mDragState.unregister();
+ mInputMonitor.updateInputWindowsLw();
+
+ // free our resources and drop all the object references
+ mDragState.reset();
+ mDragState = null;
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception caught by drag handleMotion", e);
+ } finally {
+ finishedCallback.run();
+ }
+ }
+ };
+
+ /**
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
*/
@@ -5046,7 +5358,61 @@
mPolicy.adjustConfigurationLw(config);
return true;
}
-
+
+ // -------------------------------------------------------------
+ // Drag and drop
+ // -------------------------------------------------------------
+
+ IBinder prepareDragSurface(IWindow window, SurfaceSession session,
+ boolean localOnly, int width, int height, Surface outSurface) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "prepare drag surface: w=" + width + " h=" + height
+ + " local=" + localOnly + " win=" + window
+ + " asbinder=" + window.asBinder());
+ }
+
+ final int callerPid = Binder.getCallingPid();
+ final long origId = Binder.clearCallingIdentity();
+ IBinder token = null;
+
+ try {
+ synchronized (mWindowMap) {
+ try {
+ // !!! TODO: fail if the given window does not currently have touch focus?
+
+ if (mDragState == null) {
+ Surface surface = new Surface(session, callerPid, "drag surface", 0,
+ width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ outSurface.copyFrom(surface);
+ token = new Binder();
+ mDragState = new DragState(token, surface, localOnly);
+ mDragState.mSurface = surface;
+ mDragState.mLocalOnly = localOnly;
+ token = mDragState.mToken = new Binder();
+
+ // 5 second timeout for this window to actually begin the drag
+ mH.removeMessages(H.DRAG_START_TIMEOUT, window);
+ Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, window.asBinder());
+ mH.sendMessageDelayed(msg, 5000);
+ } else {
+ Slog.w(TAG, "Drag already in progress");
+ }
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.e(TAG, "Can't allocate drag surface w=" + width + " h=" + height, e);
+ if (mDragState != null) {
+ mDragState.reset();
+ mDragState = null;
+ }
+
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return token;
+ }
+
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
@@ -5145,7 +5511,42 @@
return null;
}
-
+
+ private void addDragInputWindow(InputWindowList windowList) {
+ final InputWindow inputWindow = windowList.add();
+ inputWindow.inputChannel = mDragState.mServerChannel;
+ inputWindow.name = "drag";
+ inputWindow.layoutParamsFlags = 0;
+ inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+ inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ inputWindow.visible = true;
+ inputWindow.canReceiveKeys = false;
+ inputWindow.hasFocus = true;
+ inputWindow.hasWallpaper = false;
+ inputWindow.paused = false;
+ inputWindow.layer = mPolicy.windowTypeToLayerLw(inputWindow.layoutParamsType)
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ inputWindow.ownerPid = Process.myPid();
+ inputWindow.ownerUid = Process.myUid();
+
+ // The drag window covers the entire display
+ inputWindow.frameLeft = 0;
+ inputWindow.frameTop = 0;
+ inputWindow.frameRight = mDisplay.getWidth();
+ inputWindow.frameBottom = mDisplay.getHeight();
+
+ inputWindow.visibleFrameLeft = inputWindow.frameLeft;
+ inputWindow.visibleFrameTop = inputWindow.frameTop;
+ inputWindow.visibleFrameRight = inputWindow.frameRight;
+ inputWindow.visibleFrameBottom = inputWindow.frameBottom;
+
+ inputWindow.touchableAreaLeft = inputWindow.frameLeft;
+ inputWindow.touchableAreaTop = inputWindow.frameTop;
+ inputWindow.touchableAreaRight = inputWindow.frameRight;
+ inputWindow.touchableAreaBottom = inputWindow.frameBottom;
+ }
+
/* Updates the cached window information provided to the input dispatcher. */
public void updateInputWindowsLw() {
// Populate the input window list with information about all of the windows that
@@ -5154,6 +5555,16 @@
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
final ArrayList<WindowState> windows = mWindows;
+
+ // If there's a drag in flight, provide a pseudowindow to catch drag input
+ final boolean inDrag = (mDragState != null);
+ if (inDrag) {
+ if (DEBUG_DRAG) {
+ Log.d(TAG, "Inserting drag window");
+ }
+ addDragInputWindow(mTempInputWindows);
+ }
+
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
final WindowState child = windows.get(i);
@@ -5169,7 +5580,13 @@
final boolean isVisible = child.isVisibleLw();
final boolean hasWallpaper = (child == mWallpaperTarget)
&& (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
-
+
+ // If there's a drag in progress and 'child' is a potential drop target,
+ // make sure it's been told about the drag
+ if (inDrag && isVisible) {
+ mDragState.sendDragStartedIfNeededLw(child);
+ }
+
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
inputWindow.inputChannel = child.mInputChannel;
@@ -5741,6 +6158,87 @@
}
}
+ /* Drag/drop */
+ public IBinder prepareDrag(IWindow window, boolean localOnly,
+ int width, int height, Surface outSurface) {
+ return prepareDragSurface(window, mSurfaceSession, localOnly,
+ width, height, outSurface);
+ }
+
+ public boolean performDrag(IWindow window, IBinder dragToken,
+ float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "perform drag: win=" + window + " data=" + data);
+ }
+
+ synchronized (mWindowMap) {
+ if (mDragState == null) {
+ Slog.w(TAG, "No drag prepared");
+ throw new IllegalStateException("performDrag() without prepareDrag()");
+ }
+
+ if (dragToken != mDragState.mToken) {
+ Slog.w(TAG, "Performing mismatched drag");
+ throw new IllegalStateException("performDrag() does not match prepareDrag()");
+ }
+
+ WindowState callingWin = windowForClientLocked(null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG, "Bad requesting window " + window);
+ return false; // !!! TODO: throw here?
+ }
+
+ // !!! TODO: if input is not still focused on the initiating window, fail
+ // the drag initiation (e.g. an alarm window popped up just as the application
+ // called performDrag()
+
+ mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+ // !!! TODO: call into the input monitor to sever the current touch event flow
+ // and redirect to the drag "window"; also extract the current touch (x, y)
+ // in screen coordinates
+
+ mDragState.register();
+ mInputMonitor.updateInputWindowsLw();
+ mInputManager.transferTouchFocus(callingWin.mInputChannel,
+ mDragState.mServerChannel);
+
+ mDragState.mData = data;
+ mDragState.broadcastDragStartedLw();
+
+ // remember the thumb offsets for later
+ mDragState.mThumbOffsetX = thumbCenterX;
+ mDragState.mThumbOffsetY = thumbCenterY;
+
+ // Make the surface visible at the proper location
+ final Surface surface = mDragState.mSurface;
+ surface.openTransaction();
+ try {
+ surface.setPosition((int)(touchX - thumbCenterX),
+ (int)(touchY - thumbCenterY));
+ surface.setAlpha(.5f);
+ surface.show();
+ } finally {
+ surface.closeTransaction();
+ }
+ }
+
+ return true; // success!
+ }
+
+ public void dragRecipientEntered(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Drag into new candidate view @ " + window);
+ }
+ }
+
+ public void dragRecipientExited(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Drag from old candidate view @ " + window);
+ }
+ }
+
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
@@ -6882,6 +7380,15 @@
}
/**
+ * Can this window possibly be a drag/drop target? The test here is
+ * a combination of the above "visible now" with the check that the
+ * Input Manager uses when discarding windows from input consideration.
+ */
+ boolean isPotentialDragTarget() {
+ return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+ }
+
+ /**
* Same as isVisible(), but we also count it as visible between the
* call to IWindowSession.add() and the first relayout().
*/
@@ -7810,6 +8317,7 @@
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
public static final int REPORT_WINDOWS_CHANGE = 19;
+ public static final int DRAG_START_TIMEOUT = 20;
private Session mLastReportedHold;
@@ -8152,6 +8660,20 @@
break;
}
+ case DRAG_START_TIMEOUT: {
+ IBinder win = (IBinder)msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG, "Timeout starting drag by win " + win);
+ }
+ synchronized (mWindowMap) {
+ // !!! TODO: ANR the app that has failed to start the drag in time
+ if (mDragState != null) {
+ mDragState.reset();
+ mDragState = null;
+ }
+ }
+ }
+
}
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5c4b919..1eab7fc7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -113,9 +113,15 @@
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
@@ -6123,6 +6129,76 @@
return mSystemReady;
}
+ private static File getCalledPreBootReceiversFile() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ File fname = new File(systemDir, "called_pre_boots.dat");
+ return fname;
+ }
+
+ private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
+ ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
+ File file = getCalledPreBootReceiversFile();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
+ int vers = dis.readInt();
+ String codename = dis.readUTF();
+ if (vers == android.os.Build.VERSION.SDK_INT
+ && codename.equals(android.os.Build.VERSION.CODENAME)) {
+ int num = dis.readInt();
+ while (num > 0) {
+ num--;
+ String pkg = dis.readUTF();
+ String cls = dis.readUTF();
+ lastDoneReceivers.add(new ComponentName(pkg, cls));
+ }
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure reading last done pre-boot receivers", e);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return lastDoneReceivers;
+ }
+
+ private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) {
+ File file = getCalledPreBootReceiversFile();
+ FileOutputStream fos = null;
+ DataOutputStream dos = null;
+ try {
+ Slog.i(TAG, "Writing new set of last done pre-boot receivers...");
+ fos = new FileOutputStream(file);
+ dos = new DataOutputStream(new BufferedOutputStream(fos, 2048));
+ dos.writeInt(android.os.Build.VERSION.SDK_INT);
+ dos.writeUTF(android.os.Build.VERSION.CODENAME);
+ dos.writeInt(list.size());
+ for (int i=0; i<list.size(); i++) {
+ dos.writeUTF(list.get(i).getPackageName());
+ dos.writeUTF(list.get(i).getClassName());
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure writing last done pre-boot receivers", e);
+ file.delete();
+ } finally {
+ if (dos != null) {
+ try {
+ dos.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
public void systemReady(final Runnable goingCallback) {
// In the simulator, startRunning will never have been called, which
// normally sets a few crucial variables. Do it here instead.
@@ -6157,9 +6233,24 @@
}
}
intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+
+ ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+
+ final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
- intent.setComponent(new ComponentName(ai.packageName, ai.name));
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ if (lastDoneReceivers.contains(comp)) {
+ ris.remove(i);
+ i--;
+ }
+ }
+
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ doneReceivers.add(comp);
+ intent.setComponent(comp);
IIntentReceiver finisher = null;
if (i == ris.size()-1) {
finisher = new IIntentReceiver.Stub() {
@@ -6174,6 +6265,7 @@
synchronized (ActivityManagerService.this) {
mDidUpdate = true;
}
+ writeLastDonePreBootReceivers(doneReceivers);
systemReady(goingCallback);
}
});
@@ -6213,19 +6305,19 @@
}
}
- if (procsToKill != null) {
- synchronized(this) {
+ synchronized(this) {
+ if (procsToKill != null) {
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
removeProcessLocked(proc, true);
}
-
- // Now that we have cleaned up any update processes, we
- // are ready to start launching real processes and know that
- // we won't trample on them any more.
- mProcessesReady = true;
}
+
+ // Now that we have cleaned up any update processes, we
+ // are ready to start launching real processes and know that
+ // we won't trample on them any more.
+ mProcessesReady = true;
}
Slog.i(TAG, "System now ready");
@@ -7741,7 +7833,7 @@
pw.println(" ");
pw.println("Receiver Resolver Table:");
- mReceiverResolver.dump(pw, null, " ", null);
+ mReceiverResolver.dump(pw, null, " ", null, false);
needSep = true;
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index bf4db80..6bd89cc 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -444,10 +444,11 @@
sb.append(shortComponentName);
sb.append(": ");
TimeUtils.formatDuration(thisTime, sb);
- sb.append(" (total ");
- TimeUtils.formatDuration(totalTime, sb);
- sb.append(totalTime);
- sb.append(")");
+ if (thisTime != totalTime) {
+ sb.append(" (total ");
+ TimeUtils.formatDuration(totalTime, sb);
+ sb.append(")");
+ }
Log.i(ActivityManagerService.TAG, sb.toString());
}
stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a4497d6..30395c0 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1881,7 +1881,7 @@
String resultWho, int requestCode,
int callingPid, int callingUid, boolean onlyIfNeeded,
boolean componentSpecified) {
- Slog.i(TAG, "Starting activity: " + intent);
+ Slog.i(TAG, "Starting: " + intent);
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 3f15d0a..8463b5a 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -57,6 +57,7 @@
public final class UsageStatsService extends IUsageStats.Stub {
public static final String SERVICE_NAME = "usagestats";
private static final boolean localLOGV = false;
+ private static final boolean REPORT_UNEXPECTED = false;
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
@@ -404,11 +405,11 @@
new Thread("UsageStatsService_DiskWriter") {
public void run() {
try {
- Slog.d(TAG, "Disk writer thread starting.");
+ if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
writeStatsToFile(true);
} finally {
mUnforcedDiskWriteRunning.set(false);
- Slog.d(TAG, "Disk writer thread ending.");
+ if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
}
}
}.start();
@@ -458,7 +459,7 @@
}
}
}
- Slog.d(TAG, "Dumped usage stats.");
+ if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
}
private void writeStatsFLOCK(File file) throws IOException {
@@ -493,7 +494,7 @@
}
public void shutdown() {
- Slog.w(TAG, "Writing usage stats before shutdown...");
+ Slog.i(TAG, "Writing usage stats before shutdown...");
writeStatsToFile(true);
}
@@ -520,7 +521,7 @@
if (mLastResumedPkg != null) {
// We last resumed some other package... just pause it now
// to recover.
- Slog.i(TAG, "Unexpected resume of " + pkgName
+ if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
+ " while already resumed in " + mLastResumedPkg);
PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
if (pus != null) {
@@ -559,7 +560,7 @@
return;
}
if (!mIsResumed) {
- Slog.i(TAG, "Something wrong here, didn't expect "
+ if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
+ pkgName + " to be paused");
return;
}
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 3c05da2..d9b49fd 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -50,17 +50,24 @@
mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
+ public void reconnect() {
+ synchronized (mServiceConnection) {
+ mContext.unbindService(mServiceConnection);
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
private class Connection implements ServiceConnection {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected " + className);
- synchronized (this) {
+ synchronized (mServiceConnection) {
mProvider = IGeocodeProvider.Stub.asInterface(service);
}
}
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected " + className);
- synchronized (this) {
+ synchronized (mServiceConnection) {
mProvider = null;
}
}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 7dc9920..ef2056b 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -45,6 +45,7 @@
private final Context mContext;
private final String mName;
+ private final String mServiceName;
private ILocationProvider mProvider;
private Handler mHandler;
private final Connection mServiceConnection = new Connection();
@@ -65,14 +66,24 @@
Handler handler) {
mContext = context;
mName = name;
+ mServiceName = serviceName;
mHandler = handler;
mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE);
}
+ public void reconnect() {
+ synchronized (mServiceConnection) {
+ // unbind first
+ mContext.unbindService(mServiceConnection);
+ mContext.bindService(new Intent(mServiceName), mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+ }
+
private class Connection implements ServiceConnection {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
- synchronized (this) {
+ synchronized (mServiceConnection) {
mProvider = ILocationProvider.Stub.asInterface(service);
if (mProvider != null) {
mHandler.post(mServiceConnectedTask);
@@ -82,7 +93,7 @@
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
- synchronized (this) {
+ synchronized (mServiceConnection) {
mProvider = null;
}
}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e1e54fc..bf6840c 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -1289,6 +1289,7 @@
static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env,
jclass clazz, jobject fromChannelObj, jobject toChannelObj) {
if (checkInputManagerUnitialized(env)) {
+ LOGD("input manager uninitialized; bailing");
return false;
}
@@ -1298,6 +1299,7 @@
android_view_InputChannel_getInputChannel(env, toChannelObj);
if (fromChannel == NULL || toChannel == NULL) {
+ LOGD("bailing because from=%p to=%p", fromChannel, toChannel);
return false;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1b21a8d..fb76720 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -271,6 +271,17 @@
}
}
+void Layer::drawForSreenShot() const
+{
+ bool currentFixedSize = mFixedSize;
+ bool currentBlending = mNeedsBlending;
+ const_cast<Layer*>(this)->mFixedSize = false;
+ const_cast<Layer*>(this)->mFixedSize = true;
+ LayerBase::drawForSreenShot();
+ const_cast<Layer*>(this)->mFixedSize = currentFixedSize;
+ const_cast<Layer*>(this)->mNeedsBlending = currentBlending;
+}
+
void Layer::onDraw(const Region& clip) const
{
Texture tex(mBufferManager.getActiveTexture());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 188da6a..caa6d17 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -70,6 +70,7 @@
// LayerBase interface
virtual void setGeometry(hwc_layer_t* hwcl);
virtual void setPerFrameData(hwc_layer_t* hwcl);
+ virtual void drawForSreenShot() const;
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 3d049a7..14191cb 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -326,6 +326,12 @@
onDraw(clip);
}
+void LayerBase::drawForSreenShot() const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ onDraw( Region(hw.bounds()) );
+}
+
void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
GLclampf green, GLclampf blue,
GLclampf alpha) const
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index c66dc34..bdee05b 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -121,6 +121,7 @@
* to perform the actual drawing.
*/
virtual void draw(const Region& clip) const;
+ virtual void drawForSreenShot() const;
/**
* onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index fdf9abc..c060895 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -132,6 +132,12 @@
LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);
}
+void LayerBuffer::drawForSreenShot() const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ clearWithOpenGL( Region(hw.bounds()) );
+}
+
void LayerBuffer::onDraw(const Region& clip) const
{
sp<Source> source(getSource());
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index 1c0bf83..fece858 100644
--- a/services/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -64,6 +64,7 @@
virtual sp<LayerBaseClient::Surface> createSurface() const;
virtual status_t ditch();
virtual void onDraw(const Region& clip) const;
+ virtual void drawForSreenShot() const;
virtual uint32_t doTransaction(uint32_t flags);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 17b98a6..e6bdfd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1653,9 +1653,117 @@
// ---------------------------------------------------------------------------
+status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* w, uint32_t* h, PixelFormat* f,
+ uint32_t sw, uint32_t sh)
+{
+ status_t result = PERMISSION_DENIED;
+
+ // only one display supported for now
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ if (!GLExtensions::getInstance().haveFramebufferObject())
+ return INVALID_OPERATION;
+
+ // get screen geometry
+ const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+ const uint32_t hw_w = hw.getWidth();
+ const uint32_t hw_h = hw.getHeight();
+
+ if ((sw > hw_w) || (sh > hw_h))
+ return BAD_VALUE;
+
+ sw = (!sw) ? hw_w : sw;
+ sh = (!sh) ? hw_h : sh;
+ const size_t size = sw * sh * 4;
+
+ // make sure to clear all GL error flags
+ while ( glGetError() != GL_NO_ERROR ) ;
+
+ // create a FBO
+ GLuint name, tname;
+ glGenRenderbuffersOES(1, &tname);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+ glGenFramebuffersOES(1, &name);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+ GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+ GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+ if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+ // invert everything, b/c glReadPixel() below will invert the FB
+ glViewport(0, 0, sw, sh);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrthof(0, hw_w, 0, hw_h, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+
+ // redraw the screen entirely...
+ glClearColor(0,0,0,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer(layers[i]);
+ layer->drawForSreenShot();
+ }
+
+ // XXX: this is needed on tegra
+ glScissor(0, 0, sw, sh);
+
+ // check for errors and return screen capture
+ if (glGetError() != GL_NO_ERROR) {
+ // error while rendering
+ result = INVALID_OPERATION;
+ } else {
+ // allocate shared memory large enough to hold the
+ // screen capture
+ sp<MemoryHeapBase> base(
+ new MemoryHeapBase(size, 0, "screen-capture") );
+ void* const ptr = base->getBase();
+ if (ptr) {
+ // capture the screen with glReadPixels()
+ glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+ if (glGetError() == GL_NO_ERROR) {
+ *heap = base;
+ *w = sw;
+ *h = sh;
+ *f = PIXEL_FORMAT_RGBA_8888;
+ result = NO_ERROR;
+ }
+ } else {
+ result = NO_MEMORY;
+ }
+ }
+
+ glEnable(GL_SCISSOR_TEST);
+ glViewport(0, 0, hw_w, hw_h);
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+
+ } else {
+ result = BAD_VALUE;
+ }
+
+ // release FBO resources
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ glDeleteRenderbuffersOES(1, &tname);
+ glDeleteFramebuffersOES(1, &name);
+ return result;
+}
+
+
status_t SurfaceFlinger::captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format)
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t sw, uint32_t sh)
{
// only one display supported for now
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1671,12 +1779,15 @@
uint32_t* w;
uint32_t* h;
PixelFormat* f;
+ uint32_t sw;
+ uint32_t sh;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
- sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+ sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
+ uint32_t sw, uint32_t sh)
: flinger(flinger), dpy(dpy),
- heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+ heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
{
}
status_t getResult() const {
@@ -1689,94 +1800,15 @@
if (flinger->mSecureFrameBuffer)
return true;
- // make sure to clear all GL error flags
- while ( glGetError() != GL_NO_ERROR ) ;
+ result = flinger->captureScreenImplLocked(dpy,
+ heap, w, h, f, sw, sh);
- // get screen geometry
- const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
- const uint32_t sw = hw.getWidth();
- const uint32_t sh = hw.getHeight();
- const Region screenBounds(hw.bounds());
- const size_t size = sw * sh * 4;
-
- // create a FBO
- GLuint name, tname;
- glGenRenderbuffersOES(1, &tname);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
- glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
- GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
- if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-
- // invert everything, b/c glReadPixel() below will invert the FB
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glOrthof(0, sw, 0, sh, 0, 1);
- glMatrixMode(GL_MODELVIEW);
-
- // redraw the screen entirely...
- glClearColor(0,0,0,1);
- glClear(GL_COLOR_BUFFER_BIT);
- const Vector< sp<LayerBase> >& layers(
- flinger->mVisibleLayersSortedByZ);
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer(layers[i]);
- if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
- // we cannot render LayerBuffer because it doens't
- // use OpenGL, and won't show-up in the FBO.
- continue;
- }
- layer->draw(screenBounds);
- }
-
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
-
- // check for errors and return screen capture
- if (glGetError() != GL_NO_ERROR) {
- // error while rendering
- result = INVALID_OPERATION;
- } else {
- // allocate shared memory large enough to hold the
- // screen capture
- sp<MemoryHeapBase> base(
- new MemoryHeapBase(size, 0, "screen-capture") );
- void* const ptr = base->getBase();
- if (ptr) {
- // capture the screen with glReadPixels()
- glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
- if (glGetError() == GL_NO_ERROR) {
- *heap = base;
- *w = sw;
- *h = sh;
- *f = PIXEL_FORMAT_RGBA_8888;
- result = NO_ERROR;
- }
- } else {
- result = NO_MEMORY;
- }
- }
- } else {
- result = BAD_VALUE;
- }
-
- // release FBO resources
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteRenderbuffersOES(1, &tname);
- glDeleteFramebuffersOES(1, &name);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
- dpy, heap, width, height, format);
+ dpy, heap, width, height, format, sw, sh);
status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6e9ecbd..732e57e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -197,7 +197,9 @@
sp<IMemoryHeap>* heap,
uint32_t* width,
uint32_t* height,
- PixelFormat* format);
+ PixelFormat* format,
+ uint32_t reqWidth,
+ uint32_t reqHeight);
void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy);
@@ -319,6 +321,11 @@
void commitTransaction();
+ status_t captureScreenImplLocked(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth = 0, uint32_t reqHeight = 0);
+
friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() {
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
index 9e893f4..6cf1504 100644
--- a/services/surfaceflinger/tests/screencap/screencap.cpp
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -42,7 +42,7 @@
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
- status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+ status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
if (err != NO_ERROR) {
fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
exit(0);
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index ae9750d..a967850 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -25,9 +25,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -37,7 +38,7 @@
public class CallerInfoAsyncQuery {
- private static final boolean DBG = false;
+ private static final boolean DBG = true; // STOPSHIP: disable debugging before ship
private static final String LOG_TAG = "CallerInfoAsyncQuery";
private static final int EVENT_NEW_QUERY = 1;
@@ -190,7 +191,7 @@
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- if (DBG) log("query complete for token: " + token);
+ if (DBG) log("##### onQueryComplete() ##### query complete for token: " + token);
//get the cookie and notify the listener.
CookieWrapper cw = (CookieWrapper) cookie;
@@ -228,6 +229,8 @@
mCallerInfo = new CallerInfo().markAsVoiceMail();
} else {
mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
+ if (DBG) log("==> Got mCallerInfo: " + mCallerInfo);
+
// Use the number entered by the user for display.
if (!TextUtils.isEmpty(cw.number)) {
CountryDetector detector = (CountryDetector) mQueryContext.getSystemService(
@@ -243,7 +246,7 @@
//notify that we can clean up the queue after this.
CookieWrapper endMarker = new CookieWrapper();
endMarker.event = EVENT_END_OF_QUEUE;
- startQuery (token, endMarker, null, null, null, null, null);
+ startQuery(token, endMarker, null, null, null, null, null);
}
//notify the listener that the query is complete.
@@ -279,24 +282,82 @@
cw.cookie = cookie;
cw.event = EVENT_NEW_QUERY;
- c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
+ c.mHandler.startQuery(token, cw, contactRef, null, null, null, null);
return c;
}
/**
- * Factory method to start query with a number
+ * Factory method to start the query based on a number.
+ *
+ * Note: if the number contains an "@" character we treat it
+ * as a SIP address, and look it up directly in the Data table
+ * rather than using the PhoneLookup table.
+ * TODO: But eventually we should expose two separate methods, one for
+ * numbers and one for SIP addresses, and then have
+ * PhoneUtils.startGetCallerInfo() decide which one to call based on
+ * the phone type of the incoming connection.
*/
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
OnQueryCompleteListener listener, Object cookie) {
- //construct the URI object and start Query.
- Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ if (DBG) {
+ log("##### CallerInfoAsyncQuery startQuery()... #####");
+ log("- number: " + number);
+ log("- cookie: " + cookie);
+ }
+
+ // Construct the URI object and query params, and start the query.
+
+ Uri contactRef;
+ String selection;
+ String[] selectionArgs;
+
+ if (PhoneNumberUtils.isUriNumber(number)) {
+ // "number" is really a SIP address.
+ if (DBG) log(" - Treating number as a SIP address: " + number);
+
+ // We look up SIP addresses directly in the Data table:
+ contactRef = Data.CONTENT_URI;
+
+ // Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
+ //
+ // Also note we use "upper(data1)" in the WHERE clause, and
+ // uppercase the incoming SIP address, in order to do a
+ // case-insensitive match.
+ //
+ // TODO: need to confirm that the use of upper() doesn't
+ // prevent us from using the index! (Linear scan of the whole
+ // contacts DB can be very slow.)
+ //
+ // TODO: May also need to normalize by adding "sip:" as a
+ // prefix, if we start storing SIP addresses that way in the
+ // database.
+
+ selection = "upper(" + Data.DATA1 + ")=?"
+ + " AND "
+ + Data.MIMETYPE + "='" + SipAddress.CONTENT_ITEM_TYPE + "'";
+ selectionArgs = new String[] { number.toUpperCase() };
+
+ } else {
+ // "number" is a regular phone number. Use the PhoneLookup table:
+ contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ selection = null;
+ selectionArgs = null;
+ }
+
+ if (DBG) {
+ log("==> contactRef: " + contactRef);
+ log("==> selection: " + selection);
+ if (selectionArgs != null) {
+ for (int i = 0; i < selectionArgs.length; i++) {
+ log("==> selectionArgs[" + i + "]: " + selectionArgs[i]);
+ }
+ }
+ }
CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
c.allocate(context, contactRef);
- if (DBG) log("starting query for number: " + number + " handler: " + c.toString());
-
//create cookieWrapper, start query
CookieWrapper cw = new CookieWrapper();
cw.listener = listener;
@@ -312,10 +373,15 @@
cw.event = EVENT_NEW_QUERY;
}
- c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
-
+ c.mHandler.startQuery(token,
+ cw, // cookie
+ contactRef, // uri
+ null, // projection
+ selection, // selection
+ selectionArgs, // selectionArgs
+ null); // orderBy
return c;
- }
+ }
/**
* Method to add listeners to a currently running query
@@ -331,7 +397,7 @@
cw.cookie = cookie;
cw.event = EVENT_ADD_LISTENER;
- mHandler.startQuery (token, cw, null, null, null, null, null);
+ mHandler.startQuery(token, cw, null, null, null, null, null);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index d753973..52839be 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -35,7 +35,7 @@
*
*/
public abstract class DataConnectionTracker extends Handler {
- protected static final boolean DBG = true;
+ protected static final boolean DBG = false;
protected final String LOG_TAG = "DataConnectionTracker";
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d93852c..189d97d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -56,7 +56,7 @@
* 0, with the resulting code of 0x20.
*
* Note this mapping is also equivalent to that used by both the
- * IS5 and the IS-91 encodings. For the former this is defined
+ * IA5 and the IS-91 encodings. For the former this is defined
* using CCITT Rec. T.50 Tables 1 and 3. For the latter IS 637 B,
* Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
* and hence only maps entries up to the '_' character.
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index e0eac74..37dd971 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -133,14 +133,27 @@
return false;
}
- SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
- Log.v(LOG_TAG, " ++++++ taking call from: "
- + sipAudioCall.getPeerProfile().getUriString());
- String localUri = sipAudioCall.getLocalProfile().getUriString();
- if (localUri.equals(mProfile.getUriString())) {
- boolean makeCallWait = foregroundCall.getState().isAlive();
- ringingCall.initIncomingCall(sipAudioCall, makeCallWait);
- return true;
+ try {
+ SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
+ Log.d(LOG_TAG, "+++ taking call from: "
+ + sipAudioCall.getPeerProfile().getUriString());
+ String localUri = sipAudioCall.getLocalProfile().getUriString();
+ if (localUri.equals(mProfile.getUriString())) {
+ boolean makeCallWait = foregroundCall.getState().isAlive();
+ ringingCall.initIncomingCall(sipAudioCall, makeCallWait);
+ if (sipAudioCall.getState()
+ != SipSession.State.INCOMING_CALL) {
+ // Peer cancelled the call!
+ Log.d(LOG_TAG, " call cancelled !!");
+ ringingCall.reset();
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ // Peer may cancel the call at any time during the time we hook
+ // up ringingCall with sipAudioCall. Clean up ringingCall when
+ // that happens.
+ ringingCall.reset();
}
return false;
}
@@ -361,6 +374,11 @@
}
private class SipCall extends SipCallBase {
+ void reset() {
+ connections.clear();
+ setState(Call.State.IDLE);
+ }
+
void switchWith(SipCall that) {
synchronized (SipPhone.class) {
SipCall tmp = new SipCall();
@@ -447,6 +465,7 @@
if (state.isAlive()) {
Log.d(LOG_TAG, "hang up call: " + getState() + ": " + this
+ " on phone " + getPhone());
+ setState(State.DISCONNECTING);
CallStateException excp = null;
for (Connection c : connections) {
try {
@@ -456,7 +475,6 @@
}
}
if (excp != null) throw excp;
- setState(State.DISCONNECTING);
} else {
Log.d(LOG_TAG, "hang up dead call: " + getState() + ": "
+ this + " on phone " + getPhone());
@@ -633,13 +651,20 @@
}
synchronized (SipPhone.class) {
setState(Call.State.DISCONNECTED);
- mSipAudioCall.close();
- mOwner.onConnectionEnded(SipConnection.this);
- Log.v(LOG_TAG, "-------- connection ended: "
- + mPeer.getUriString() + ": "
- + mSipAudioCall.getState() + ", cause: "
- + getDisconnectCause() + ", on phone "
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ mSipAudioCall = null;
+ String sessionState = (sipAudioCall == null)
+ ? ""
+ : (sipAudioCall.getState() + ", ");
+ Log.v(LOG_TAG, "--- connection ended: "
+ + mPeer.getUriString() + ": " + sessionState
+ + "cause: " + getDisconnectCause() + ", on phone "
+ getPhone());
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.close();
+ }
+ mOwner.onConnectionEnded(SipConnection.this);
}
}
@@ -793,14 +818,17 @@
synchronized (SipPhone.class) {
Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+ mState + ": on phone " + getPhone().getPhoneName());
+ if (!mState.isAlive()) return;
try {
- if (mState.isAlive()) {
- if (mSipAudioCall != null) mSipAudioCall.endCall();
- setState(Call.State.DISCONNECTING);
- setDisconnectCause(DisconnectCause.LOCAL);
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.endCall();
}
} catch (SipException e) {
throw new CallStateException("hangup(): " + e);
+ } finally {
+ mAdapter.onCallEnded(DisconnectCause.LOCAL);
}
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index af71a0f..4be41b9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -164,6 +164,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="GradientsActivity"
+ android:label="_Gradients">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity
android:name="ShadersActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
new file mode 100644
index 0000000..b70f3a9
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
@@ -0,0 +1,108 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GradientsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private final Paint mPaint;
+ private final float mDrawWidth;
+ private final float mDrawHeight;
+ private final LinearGradient mGradient;
+ private final Matrix mMatrix;
+
+ ShadersView(Context c) {
+ super(c);
+
+ mDrawWidth = 200;
+ mDrawHeight = 200;
+
+ mGradient = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
+ mMatrix = new Matrix();
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ // Gradients
+ canvas.save();
+ float top = 40.0f;
+ float right = 40.0f + mDrawWidth;
+ float left = 40.0f;
+ float bottom = 40.0f + mDrawHeight;
+
+ mPaint.setShader(mGradient);
+
+ mMatrix.setScale(1, mDrawWidth);
+ mMatrix.postRotate(90);
+ mMatrix.postTranslate(right, top);
+ mGradient.setLocalMatrix(mMatrix);
+ canvas.drawRect(right - mDrawWidth, top, right, top + mDrawHeight, mPaint);
+
+ top += 40.0f + mDrawHeight;
+ bottom += 40.0f + mDrawHeight;
+
+ mMatrix.setScale(1, mDrawHeight);
+ mMatrix.postTranslate(left, top);
+ mGradient.setLocalMatrix(mMatrix);
+ canvas.drawRect(left, top, right, top + mDrawHeight, mPaint);
+
+ left += 40.0f + mDrawWidth;
+ right += 40.0f + mDrawWidth;
+ top -= 40.0f + mDrawHeight;
+ bottom -= 40.0f + mDrawHeight;
+
+ mMatrix.setScale(1, mDrawHeight);
+ mMatrix.postRotate(180);
+ mMatrix.postTranslate(left, bottom);
+ mGradient.setLocalMatrix(mMatrix);
+ canvas.drawRect(left, bottom - mDrawHeight, right, bottom, mPaint);
+
+ top += 40.0f + mDrawHeight;
+ bottom += 40.0f + mDrawHeight;
+
+ mMatrix.setScale(1, mDrawWidth);
+ mMatrix.postRotate(-90);
+ mMatrix.postTranslate(left, top);
+ mGradient.setLocalMatrix(mMatrix);
+ canvas.drawRect(left, top, left + mDrawWidth, bottom, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
index 9c8e7ec..2db1071 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
@@ -77,8 +77,13 @@
m2.setScale(0.5f, 0.5f);
mScaledShader.setLocalMatrix(m2);
- mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+ Matrix m3 = new Matrix();
+ m3.setScale(mDrawHeight, 1.0f);
+ m3.postRotate(-90.0f);
+ m3.postTranslate(0.0f, mDrawHeight);
+ mHorGradient.setLocalMatrix(m3);
mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
index f47b00f..763169a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
@@ -87,6 +87,7 @@
ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(adapter);
list.setCacheColorHint(0);
+ list.setVerticalFadingEdgeEnabled(true);
registerForContextMenu(list);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index f91f601..0553f5e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -30,6 +30,8 @@
import com.android.tools.layoutlib.create.MethodAdapter;
import com.android.tools.layoutlib.create.OverrideMethod;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -46,6 +48,7 @@
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.BridgeInflater;
+import android.view.DragEvent;
import android.view.InputChannel;
import android.view.IWindow;
import android.view.IWindowSession;
@@ -1073,6 +1076,33 @@
}
@SuppressWarnings("unused")
+ public IBinder prepareDrag(IWindow window, boolean localOnly,
+ int thumbnailWidth, int thumbnailHeight, Surface outSurface)
+ throws RemoteException {
+ // pass for now
+ return null;
+ }
+
+ @SuppressWarnings("unused")
+ public boolean performDrag(IWindow window, IBinder dragToken,
+ float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data)
+ throws RemoteException {
+ // pass for now
+ return false;
+ }
+
+ @SuppressWarnings("unused")
+ public void dragRecipientEntered(IWindow window) throws RemoteException {
+ // pass for now
+ }
+
+ @SuppressWarnings("unused")
+ public void dragRecipientExited(IWindow window) throws RemoteException {
+ // pass for now
+ }
+
+ @SuppressWarnings("unused")
public void setWallpaperPosition(IBinder window, float x, float y,
float xStep, float yStep) {
// pass for now.
@@ -1170,6 +1200,11 @@
// pass for now.
}
+ @SuppressWarnings("unused")
+ public void dispatchDragEvent(DragEvent event) {
+ // pass for now.
+ }
+
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/obbtool/mkobb.sh b/tools/obbtool/mkobb.sh
index f4cae9a1..1987696 100755
--- a/tools/obbtool/mkobb.sh
+++ b/tools/obbtool/mkobb.sh
@@ -21,7 +21,7 @@
MOUNTDIR=/tmp
# Presets. Changing these will probably break your OBB on the device
-CRYPTO=blowfish
+CRYPTO=twofish
FS=vfat
MKFS=mkfs.vfat
LOSETUP=losetup
@@ -122,7 +122,12 @@
rmdir ${temp_mount}
fi
if [ "x${loop_dev}" != "x" ]; then \
- ${LOSETUPBIN} -d ${loop_dev}
+ if [ ${use_crypto} -eq 1 ]; then \
+ dmsetup remove -f ${loop_dev}
+ ${LOSETUPBIN} -d ${old_loop_dev}
+ else \
+ ${LOSETUPBIN} -d ${loop_dev}
+ fi
fi
if [ "x${tempfile}" != "x" -a -f "${tempfile}" ]; then \
rm -f ${tempfile}
@@ -202,7 +207,7 @@
tempfile=$(tempfile -d ${outdir}) || ( echo "ERROR: couldn't create temporary file in ${outdir}"; exit 1 )
-block_count=`du --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'`
+block_count=`du -s --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'`
if [ $? -ne 0 ]; then \
echo "ERROR: Couldn't read size of input directory ${directory}"
exit 1
@@ -216,12 +221,14 @@
loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next unused device"; exit 1 )
+${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+
if [ ${use_crypto} -eq 1 ]; then \
- keyfile=$(tempfile -d ${outdir}) || ( echo "ERROR: could not create temporary key file"; exit 1 )
- ${LOSETUPBIN} -p 5 -e ${CRYPTO} ${loop_dev} ${tempfile} 5< ${keyfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
- rm -f ${keyfile}
-else \
- ${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+ hashed_key=`echo -n "${key}" | md5sum | awk '{ print $1 }'`
+ unique_dm_name=`basename ${tempfile}`
+ echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${hashed_key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name}
+ old_loop_dev=${loop_dev}
+ loop_dev=/dev/mapper/${unique_dm_name}
fi
#
@@ -252,7 +259,12 @@
#
umount ${temp_mount}
rmdir ${temp_mount}
-${LOSETUPBIN} -d ${loop_dev}
+if [ ${use_crypto} -eq 1 ]; then \
+ dmsetup remove -f ${loop_dev}
+ ${LOSETUPBIN} -d ${old_loop_dev}
+else \
+ ${LOSETUPBIN} -d ${loop_dev}
+fi
mv ${tempfile} ${filename}
trap - ERR
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index dfa6841..3877aeb 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -80,8 +80,7 @@
*/
public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
- // TODO: add rest of the codecs when the native part is done.
- private static final AudioCodec[] sCodecs = {GSM, PCMU, PCMA};
+ private static final AudioCodec[] sCodecs = {GSM_EFR, AMR, GSM, PCMU, PCMA};
private AudioCodec(int type, String rtpmap, String fmtp) {
this.type = type;
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 59631c1..a589fe9 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -173,7 +173,7 @@
SipRegistrationListener listener) throws SipException {
try {
mSipService.open3(localProfile, incomingCallBroadcastAction,
- createRelay(listener));
+ createRelay(listener, localProfile.getUriString()));
} catch (RemoteException e) {
throw new SipException("open()", e);
}
@@ -191,7 +191,7 @@
SipRegistrationListener listener) throws SipException {
try {
mSipService.setRegistrationListener(
- localProfileUri, createRelay(listener));
+ localProfileUri, createRelay(listener, localProfileUri));
} catch (RemoteException e) {
throw new SipException("setRegistrationListener()", e);
}
@@ -425,8 +425,8 @@
public void register(SipProfile localProfile, int expiryTime,
SipRegistrationListener listener) throws SipException {
try {
- ISipSession session = mSipService.createSession(
- localProfile, createRelay(listener));
+ ISipSession session = mSipService.createSession(localProfile,
+ createRelay(listener, localProfile.getUriString()));
session.register(expiryTime);
} catch (RemoteException e) {
throw new SipException("register()", e);
@@ -446,8 +446,8 @@
public void unregister(SipProfile localProfile,
SipRegistrationListener listener) throws SipException {
try {
- ISipSession session = mSipService.createSession(
- localProfile, createRelay(listener));
+ ISipSession session = mSipService.createSession(localProfile,
+ createRelay(listener, localProfile.getUriString()));
session.unregister();
} catch (RemoteException e) {
throw new SipException("unregister()", e);
@@ -475,8 +475,8 @@
}
private static ISipSessionListener createRelay(
- SipRegistrationListener listener) {
- return ((listener == null) ? null : new ListenerRelay(listener));
+ SipRegistrationListener listener, String uri) {
+ return ((listener == null) ? null : new ListenerRelay(listener, uri));
}
/**
@@ -512,15 +512,19 @@
private static class ListenerRelay extends SipSessionAdapter {
private SipRegistrationListener mListener;
+ private String mUri;
// listener must not be null
- public ListenerRelay(SipRegistrationListener listener) {
+ public ListenerRelay(SipRegistrationListener listener, String uri) {
mListener = listener;
+ mUri = uri;
}
private String getUri(ISipSession session) {
try {
- return session.getLocalProfile().getUriString();
+ return ((session == null)
+ ? mUri
+ : session.getLocalProfile().getUriString());
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 0ff5586..405dff8 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -39,6 +39,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -49,6 +50,7 @@
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
@@ -64,6 +66,7 @@
*/
public final class SipService extends ISipService.Stub {
private static final String TAG = "SipService";
+ private static final boolean DEBUGV = false;
private static final boolean DEBUG = true;
private static final boolean DEBUG_TIMER = DEBUG && false;
private static final int EXPIRY_TIME = 3600;
@@ -119,17 +122,19 @@
}
public synchronized SipProfile[] getListOfProfiles() {
- SipProfile[] profiles = new SipProfile[mSipGroups.size()];
- int i = 0;
+ boolean isCallerRadio = isCallerRadio();
+ ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
for (SipSessionGroupExt group : mSipGroups.values()) {
- profiles[i++] = group.getLocalProfile();
+ if (isCallerRadio || isCallerCreator(group)) {
+ profiles.add(group.getLocalProfile());
+ }
}
- return profiles;
+ return profiles.toArray(new SipProfile[profiles.size()]);
}
public void open(SipProfile localProfile) {
localProfile.setCallingUid(Binder.getCallingUid());
- if (localProfile.getAutoRegistration()) {
+ if (localProfile.getAutoRegistration() && isCallerRadio()) {
openToReceiveCalls(localProfile);
} else {
openToMakeCalls(localProfile);
@@ -153,8 +158,14 @@
String incomingCallBroadcastAction, ISipSessionListener listener) {
localProfile.setCallingUid(Binder.getCallingUid());
if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
- throw new RuntimeException(
- "empty broadcast action for incoming call");
+ Log.w(TAG, "empty broadcast action for incoming call");
+ return;
+ }
+ if (incomingCallBroadcastAction.equals(
+ SipManager.ACTION_SIP_INCOMING_CALL) && !isCallerRadio()) {
+ Log.w(TAG, "failed to open the profile; "
+ + "the action string is reserved");
+ return;
}
if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
+ incomingCallBroadcastAction + ": " + listener);
@@ -171,29 +182,64 @@
}
}
+ private boolean isCallerCreator(SipSessionGroupExt group) {
+ SipProfile profile = group.getLocalProfile();
+ return (profile.getCallingUid() == Binder.getCallingUid());
+ }
+
+ private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
+ return (isCallerRadio() || isCallerCreator(group));
+ }
+
+ private boolean isCallerRadio() {
+ return (Binder.getCallingUid() == Process.PHONE_UID);
+ }
+
public synchronized void close(String localProfileUri) {
- SipSessionGroupExt group = mSipGroups.remove(localProfileUri);
- if (group != null) {
- notifyProfileRemoved(group.getLocalProfile());
- group.close();
- if (isWifiOn() && !anyOpened()) releaseWifiLock();
+ SipSessionGroupExt group = mSipGroups.get(localProfileUri);
+ if (group == null) return;
+ if (!isCallerCreatorOrRadio(group)) {
+ Log.d(TAG, "only creator or radio can close this profile");
+ return;
}
+
+ group = mSipGroups.remove(localProfileUri);
+ notifyProfileRemoved(group.getLocalProfile());
+ group.close();
+ if (isWifiOn() && !anyOpened()) releaseWifiLock();
}
public synchronized boolean isOpened(String localProfileUri) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- return ((group != null) ? group.isOpened() : false);
+ if (group == null) return false;
+ if (isCallerCreatorOrRadio(group)) {
+ return group.isOpened();
+ } else {
+ Log.i(TAG, "only creator or radio can query on the profile");
+ return false;
+ }
}
public synchronized boolean isRegistered(String localProfileUri) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- return ((group != null) ? group.isRegistered() : false);
+ if (group == null) return false;
+ if (isCallerCreatorOrRadio(group)) {
+ return group.isRegistered();
+ } else {
+ Log.i(TAG, "only creator or radio can query on the profile");
+ return false;
+ }
}
public synchronized void setRegistrationListener(String localProfileUri,
ISipSessionListener listener) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group != null) group.setListener(listener);
+ if (group == null) return;
+ if (isCallerCreator(group)) {
+ group.setListener(listener);
+ } else {
+ Log.i(TAG, "only creator can set listener on the profile");
+ }
}
public synchronized ISipSession createSession(SipProfile localProfile,
@@ -234,6 +280,8 @@
group = new SipSessionGroupExt(localProfile, null, null);
mSipGroups.put(key, group);
notifyProfileAdded(localProfile);
+ } else if (!isCallerCreator(group)) {
+ throw new SipException("only creator can access the profile");
}
return group;
}
@@ -244,6 +292,9 @@
String key = localProfile.getUriString();
SipSessionGroupExt group = mSipGroups.get(key);
if (group != null) {
+ if (!isCallerCreator(group)) {
+ throw new SipException("only creator can access the profile");
+ }
group.setIncomingCallBroadcastAction(
incomingCallBroadcastAction);
group.setListener(listener);
@@ -510,32 +561,44 @@
}
}
+ // KeepAliveProcess is controlled by AutoRegistrationProcess.
+ // All methods will be invoked in sync with SipService.this except realRun()
private class KeepAliveProcess implements Runnable {
private static final String TAG = "\\KEEPALIVE/";
private static final int INTERVAL = 10;
private SipSessionGroup.SipSessionImpl mSession;
+ private boolean mRunning = false;
public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
mSession = session;
}
public void start() {
+ if (mRunning) return;
+ mRunning = true;
mTimer.set(INTERVAL * 1000, this);
}
+ // timeout handler
public void run() {
+ if (!mRunning) return;
+ final SipSessionGroup.SipSessionImpl session = mSession;
+
// delegate to mExecutor
getExecutor().addTask(new Runnable() {
public void run() {
- realRun();
+ realRun(session);
}
});
}
- private void realRun() {
+ // real timeout handler
+ private void realRun(SipSessionGroup.SipSessionImpl session) {
synchronized (SipService.this) {
- SipSessionGroup.SipSessionImpl session = mSession.duplicate();
- if (DEBUG) Log.d(TAG, "~~~ keepalive");
+ if (notCurrentSession(session)) return;
+
+ session = session.duplicate();
+ if (DEBUGV) Log.v(TAG, "~~~ keepalive");
mTimer.cancel(this);
session.sendKeepAlive();
if (session.isReRegisterRequired()) {
@@ -547,8 +610,14 @@
}
public void stop() {
+ mRunning = false;
+ mSession = null;
mTimer.cancel(this);
}
+
+ private boolean notCurrentSession(ISipSession session) {
+ return (session != mSession) || !mRunning;
+ }
}
private class AutoRegistrationProcess extends SipSessionAdapter
@@ -561,13 +630,15 @@
private long mExpiryTime;
private int mErrorCode;
private String mErrorMessage;
+ private boolean mRunning = false;
private String getAction() {
return toString();
}
public void start(SipSessionGroup group) {
- if (mSession == null) {
+ if (!mRunning) {
+ mRunning = true;
mBackoff = 1;
mSession = (SipSessionGroup.SipSessionImpl)
group.createSession(this);
@@ -584,35 +655,24 @@
}
public void stop() {
- stop(false);
- }
-
- private void stopButKeepStates() {
- stop(true);
- }
-
- private void stop(boolean keepStates) {
- if (mSession == null) return;
+ if (!mRunning) return;
+ mRunning = false;
+ mSession.setListener(null);
if (mConnected && mRegistered) mSession.unregister();
+
mTimer.cancel(this);
if (mKeepAliveProcess != null) {
mKeepAliveProcess.stop();
mKeepAliveProcess = null;
}
- if (!keepStates) {
- mSession = null;
- mRegistered = false;
- }
- }
- private boolean isStopped() {
- return (mSession == null);
+ mRegistered = false;
+ setListener(mProxy.getListener());
}
public void setListener(ISipSessionListener listener) {
synchronized (SipService.this) {
mProxy.setListener(listener);
- if (mSession == null) return;
try {
int state = (mSession == null)
@@ -632,6 +692,18 @@
mProxy.onRegistrationFailed(mSession, mErrorCode,
mErrorMessage);
}
+ } else if (!mConnected) {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.DATA_CONNECTION_LOST,
+ "no data connection");
+ } else if (!mRunning) {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.CLIENT_ERROR,
+ "registration not running");
+ } else {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.IN_PROGRESS,
+ String.valueOf(state));
}
} catch (Throwable t) {
Log.w(TAG, "setListener(): " + t);
@@ -643,21 +715,29 @@
return mRegistered;
}
+ // timeout handler
public void run() {
- // delegate to mExecutor
- getExecutor().addTask(new Runnable() {
- public void run() {
- realRun();
- }
- });
+ synchronized (SipService.this) {
+ if (!mRunning) return;
+ final SipSessionGroup.SipSessionImpl session = mSession;
+
+ // delegate to mExecutor
+ getExecutor().addTask(new Runnable() {
+ public void run() {
+ realRun(session);
+ }
+ });
+ }
}
- private void realRun() {
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
- if (DEBUG) Log.d(TAG, "~~~ registering");
+ // real timeout handler
+ private void realRun(SipSessionGroup.SipSessionImpl session) {
synchronized (SipService.this) {
- if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
+ if (notCurrentSession(session)) return;
+ mErrorCode = SipErrorCode.NO_ERROR;
+ mErrorMessage = null;
+ if (DEBUG) Log.d(TAG, "~~~ registering");
+ if (mConnected) session.register(EXPIRY_TIME);
}
}
@@ -697,22 +777,29 @@
public void onRegistering(ISipSession session) {
if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
+
mRegistered = false;
mProxy.onRegistering(session);
}
}
+ private boolean notCurrentSession(ISipSession session) {
+ if (session != mSession) {
+ ((SipSessionGroup.SipSessionImpl) session).setListener(null);
+ return true;
+ }
+ return !mRunning;
+ }
+
@Override
public void onRegistrationDone(ISipSession session, int duration) {
if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
mProxy.onRegistrationDone(session, duration);
- if (isStopped()) return;
-
if (duration > 0) {
mSession.clearReRegisterRequired();
mExpiryTime = SystemClock.elapsedRealtime()
@@ -751,17 +838,18 @@
if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
+ SipErrorCode.toString(errorCode) + ": " + message);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
- mErrorCode = errorCode;
- mErrorMessage = message;
- mProxy.onRegistrationFailed(session, errorCode, message);
+ if (notCurrentSession(session)) return;
if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
if (DEBUG) Log.d(TAG, " pause auto-registration");
- stopButKeepStates();
- } else if (!isStopped()) {
+ stop();
+ } else {
onError();
}
+
+ mErrorCode = errorCode;
+ mErrorMessage = message;
+ mProxy.onRegistrationFailed(session, errorCode, message);
}
}
@@ -769,14 +857,11 @@
public void onRegistrationTimeout(ISipSession session) {
if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
+
mErrorCode = SipErrorCode.TIME_OUT;
mProxy.onRegistrationTimeout(session);
-
- if (!isStopped()) {
- mRegistered = false;
- onError();
- }
+ onError();
}
}
@@ -883,6 +968,7 @@
mConnected = connected;
}
+ // timeout handler
@Override
public void run() {
// delegate to mExecutor
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 4321d7b..c68fa1b 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -334,12 +334,12 @@
if (isRequestEvent(Request.INVITE, evt)) {
RequestEvent event = (RequestEvent) evt;
SipSessionImpl newSession = new SipSessionImpl(mProxy);
+ newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mServerTransaction = mSipHelper.sendRinging(event,
generateTag());
newSession.mDialog = newSession.mServerTransaction.getDialog();
newSession.mInviteReceived = event;
newSession.mPeerProfile = createPeerProfile(event.getRequest());
- newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mPeerSessionDescription =
extractContent(event.getRequest());
addSipSession(newSession);
@@ -708,7 +708,6 @@
case SipSession.State.PINGING:
reset();
mReRegisterFlag = true;
- mState = SipSession.State.READY_TO_CALL;
break;
default:
@@ -877,6 +876,7 @@
private boolean readyForCall(EventObject evt) throws SipException {
// expect MakeCallCommand, RegisterCommand, DEREGISTER
if (evt instanceof MakeCallCommand) {
+ mState = SipSession.State.OUTGOING_CALL;
MakeCallCommand cmd = (MakeCallCommand) evt;
mPeerProfile = cmd.getPeerProfile();
mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
@@ -884,25 +884,24 @@
generateTag());
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.OUTGOING_CALL;
mProxy.onCalling(this);
startSessionTimer(cmd.getTimeout());
return true;
} else if (evt instanceof RegisterCommand) {
+ mState = SipSession.State.REGISTERING;
int duration = ((RegisterCommand) evt).getDuration();
mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
generateTag(), duration);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.REGISTERING;
mProxy.onRegistering(this);
return true;
} else if (DEREGISTER == evt) {
+ mState = SipSession.State.DEREGISTERING;
mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
generateTag(), 0);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.DEREGISTERING;
mProxy.onRegistering(this);
return true;
}
@@ -913,11 +912,11 @@
// expect MakeCallCommand(answering) , END_CALL cmd , Cancel
if (evt instanceof MakeCallCommand) {
// answer call
+ mState = SipSession.State.INCOMING_CALL_ANSWERING;
mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
mLocalProfile,
((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction);
- mState = SipSession.State.INCOMING_CALL_ANSWERING;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
} else if (END_CALL == evt) {
@@ -1009,8 +1008,8 @@
// RFC says that UA should not send out cancel when no
// response comes back yet. We are cheating for not checking
// response.
- mSipHelper.sendCancel(mClientTransaction);
mState = SipSession.State.OUTGOING_CALL_CANCELING;
+ mSipHelper.sendCancel(mClientTransaction);
startSessionTimer(CANCEL_CALL_TIMER);
return true;
}
@@ -1065,8 +1064,8 @@
return true;
} else if (isRequestEvent(Request.INVITE, evt)) {
// got Re-INVITE
- RequestEvent event = mInviteReceived = (RequestEvent) evt;
mState = SipSession.State.INCOMING_CALL;
+ RequestEvent event = mInviteReceived = (RequestEvent) evt;
mPeerSessionDescription = extractContent(event.getRequest());
mServerTransaction = null;
mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@@ -1077,9 +1076,9 @@
return true;
} else if (evt instanceof MakeCallCommand) {
// to change call
+ mState = SipSession.State.OUTGOING_CALL;
mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription());
- mState = SipSession.State.OUTGOING_CALL;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
new file mode 100644
index 0000000..f3ecac2
--- /dev/null
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyrightm (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.
+ */
+
+#include <string.h>
+
+#include "AudioCodec.h"
+
+#include "gsmamr_dec.h"
+#include "gsmamr_enc.h"
+
+namespace {
+
+const int gFrameBits[8] = {95, 103, 118, 134, 148, 159, 204, 244};
+
+//------------------------------------------------------------------------------
+
+// See RFC 4867 for the encoding details.
+
+class AmrCodec : public AudioCodec
+{
+public:
+ AmrCodec() {
+ if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
+ mEncoder = NULL;
+ }
+ if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
+ mDecoder = NULL;
+ }
+ }
+
+ ~AmrCodec() {
+ if (mEncoder) {
+ AMREncodeExit(&mEncoder, &mSidSync);
+ }
+ if (mDecoder) {
+ GSMDecodeFrameExit(&mDecoder);
+ }
+ }
+
+ int set(int sampleRate, const char *fmtp);
+ int encode(void *payload, int16_t *samples);
+ int decode(int16_t *samples, void *payload, int length);
+
+private:
+ void *mEncoder;
+ void *mSidSync;
+ void *mDecoder;
+
+ int mMode;
+ int mModeSet;
+ bool mOctetAligned;
+};
+
+int AmrCodec::set(int sampleRate, const char *fmtp)
+{
+ // These parameters are not supported.
+ if (strcasestr(fmtp, "crc=1") || strcasestr(fmtp, "robust-sorting=1") ||
+ strcasestr(fmtp, "interleaving=")) {
+ return -1;
+ }
+
+ // Handle mode-set and octet-align.
+ char *modes = strcasestr(fmtp, "mode-set=");
+ if (modes) {
+ mMode = 0;
+ mModeSet = 0;
+ for (char c = *modes; c && c != ' '; c = *++modes) {
+ if (c >= '0' && c <= '7') {
+ int mode = c - '0';
+ if (mode > mMode) {
+ mMode = mode;
+ }
+ mModeSet |= 1 << mode;
+ }
+ }
+ } else {
+ mMode = 7;
+ mModeSet = 0xFF;
+ }
+ mOctetAligned = (strcasestr(fmtp, "octet-align=1") != NULL);
+
+ // TODO: handle mode-change-*.
+
+ return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
+}
+
+int AmrCodec::encode(void *payload, int16_t *samples)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+
+ int length = AMREncode(mEncoder, mSidSync, (Mode)mMode,
+ samples, bytes + 1, &type, AMR_TX_WMF);
+
+ if (type != mMode || length != (8 + gFrameBits[mMode] + 7) >> 3) {
+ return -1;
+ }
+
+ if (mOctetAligned) {
+ bytes[0] = 0xF0;
+ bytes[1] = (mMode << 3) | 0x04;
+ ++length;
+ } else {
+ // CMR = 15 (4-bit), F = 0 (1-bit), FT = mMode (4-bit), Q = 1 (1-bit).
+ bytes[0] = 0xFF;
+ bytes[1] = 0xC0 | (mMode << 1) | 1;
+
+ // Shift left 6 bits and update the length.
+ bytes[length + 1] = 0;
+ for (int i = 0; i <= length; ++i) {
+ bytes[i] = (bytes[i] << 6) | (bytes[i + 1] >> 2);
+ }
+ length = (10 + gFrameBits[mMode] + 7) >> 3;
+ }
+ return length;
+}
+
+int AmrCodec::decode(int16_t *samples, void *payload, int length)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+ if (length < 2) {
+ return -1;
+ }
+ int request = bytes[0] >> 4;
+
+ if (mOctetAligned) {
+ if ((bytes[1] & 0xC4) != 0x04) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)(bytes[1] >> 3);
+ if (length != (16 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+ length -= 2;
+ bytes += 2;
+ } else {
+ if ((bytes[0] & 0x0C) || !(bytes[1] & 0x40)) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)((bytes[0] << 1 | bytes[1] >> 7) & 0x07);
+ if (length != (10 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+
+ // Shift left 2 bits and update the length.
+ --length;
+ for (int i = 1; i < length; ++i) {
+ bytes[i] = (bytes[i] << 2) | (bytes[i + 1] >> 6);
+ }
+ bytes[length] <<= 2;
+ length = (gFrameBits[type] + 7) >> 3;
+ ++bytes;
+ }
+
+ if (AMRDecode(mDecoder, type, bytes, samples, MIME_IETF) != length) {
+ return -1;
+ }
+
+ // Handle CMR
+ if (request < 8 && request != mMode) {
+ for (int i = request; i >= 0; --i) {
+ if (mModeSet & (1 << i)) {
+ mMode = request;
+ break;
+ }
+ }
+ }
+
+ return 160;
+}
+
+//------------------------------------------------------------------------------
+
+// See RFC 3551 for the encoding details.
+
+class GsmEfrCodec : public AudioCodec
+{
+public:
+ GsmEfrCodec() {
+ if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
+ mEncoder = NULL;
+ }
+ if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
+ mDecoder = NULL;
+ }
+ }
+
+ ~GsmEfrCodec() {
+ if (mEncoder) {
+ AMREncodeExit(&mEncoder, &mSidSync);
+ }
+ if (mDecoder) {
+ GSMDecodeFrameExit(&mDecoder);
+ }
+ }
+
+ int set(int sampleRate, const char *fmtp) {
+ return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
+ }
+
+ int encode(void *payload, int16_t *samples);
+ int decode(int16_t *samples, void *payload, int length);
+
+private:
+ void *mEncoder;
+ void *mSidSync;
+ void *mDecoder;
+};
+
+int GsmEfrCodec::encode(void *payload, int16_t *samples)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+
+ int length = AMREncode(mEncoder, mSidSync, MR122,
+ samples, bytes, &type, AMR_TX_WMF);
+
+ if (type == AMR_122 && length == 32) {
+ bytes[0] = 0xC0 | (bytes[1] >> 4);
+ for (int i = 1; i < 31; ++i) {
+ bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
+ }
+ return 31;
+ }
+ return -1;
+}
+
+int GsmEfrCodec::decode(int16_t *samples, void *payload, int length)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ if (length == 31 && (bytes[0] >> 4) == 0x0C) {
+ for (int i = 0; i < 30; ++i) {
+ bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
+ }
+ bytes[30] <<= 4;
+
+ if (AMRDecode(mDecoder, AMR_122, bytes, samples, MIME_IETF) == 31) {
+ return 160;
+ }
+ }
+ return -1;
+}
+
+} // namespace
+
+AudioCodec *newAmrCodec()
+{
+ return new AmrCodec;
+}
+
+AudioCodec *newGsmEfrCodec()
+{
+ return new GsmEfrCodec;
+}
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
index 29683bd..5909c0d 100644
--- a/voip/jni/rtp/Android.mk
+++ b/voip/jni/rtp/Android.mk
@@ -27,6 +27,7 @@
rtp_jni.cpp
LOCAL_SRC_FILES += \
+ AmrCodec.cpp \
G711Codec.cpp \
GsmCodec.cpp
@@ -34,13 +35,20 @@
libnativehelper \
libcutils \
libutils \
- libmedia
+ libmedia \
+ libstagefright
LOCAL_STATIC_LIBRARIES := libgsm
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
- external/libgsm/inc
+ external/libgsm/inc \
+ frameworks/base/media/libstagefright/codecs/amrnb/common/include \
+ frameworks/base/media/libstagefright/codecs/amrnb/common/ \
+ frameworks/base/media/libstagefright/codecs/amrnb/enc/include \
+ frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
+ frameworks/base/media/libstagefright/codecs/amrnb/dec/include \
+ frameworks/base/media/libstagefright/codecs/amrnb/dec/src
LOCAL_CFLAGS += -fvisibility=hidden
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index fc33ef2..2267ea0 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -21,6 +21,8 @@
extern AudioCodec *newAlawCodec();
extern AudioCodec *newUlawCodec();
extern AudioCodec *newGsmCodec();
+extern AudioCodec *newAmrCodec();
+extern AudioCodec *newGsmEfrCodec();
struct AudioCodecType {
const char *name;
@@ -29,6 +31,8 @@
{"PCMA", newAlawCodec},
{"PCMU", newUlawCodec},
{"GSM", newGsmCodec},
+ {"AMR", newAmrCodec},
+ {"GSM-EFR", newGsmEfrCodec},
{NULL, NULL},
};
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index f09edb6..5214518 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -57,9 +57,9 @@
// a modulo operation on the index while accessing the array. However modulo can
// be expensive on some platforms, such as ARM. Thus we round up the size of the
// array to the nearest power of 2 and then use bitwise-and instead of modulo.
-// Currently we make it 256ms long and assume packet interval is 32ms or less.
-// The first 64ms is the place where samples get mixed. The rest 192ms is the
-// real jitter buffer. For a stream at 8000Hz it takes 4096 bytes. These numbers
+// Currently we make it 512ms long and assume packet interval is 40ms or less.
+// The first 80ms is the place where samples get mixed. The rest 432ms is the
+// real jitter buffer. For a stream at 8000Hz it takes 8192 bytes. These numbers
// are chosen by experiments and each of them can be adjusted as needed.
// Other notes:
@@ -69,7 +69,11 @@
// milliseconds. No floating points.
// + If we cannot get enough CPU, we drop samples and simulate packet loss.
// + Resampling is not done yet, so streams in one group must use the same rate.
-// For the first release we might only support 8kHz and 16kHz.
+// For the first release only 8000Hz is supported.
+
+#define BUFFER_SIZE 512
+#define HISTORY_SIZE 80
+#define MEASURE_PERIOD 2000
class AudioStream
{
@@ -85,7 +89,6 @@
void encode(int tick, AudioStream *chain);
void decode(int tick);
-private:
enum {
NORMAL = 0,
SEND_ONLY = 1,
@@ -93,6 +96,7 @@
LAST_MODE = 2,
};
+private:
int mMode;
int mSocket;
sockaddr_storage mRemote;
@@ -111,6 +115,7 @@
int mBufferMask;
int mBufferHead;
int mBufferTail;
+ int mLatencyTimer;
int mLatencyScore;
uint16_t mSequence;
@@ -159,12 +164,13 @@
mInterval = mSampleCount / mSampleRate;
// Allocate jitter buffer.
- for (mBufferMask = 8192; mBufferMask < sampleRate; mBufferMask <<= 1);
- mBufferMask >>= 2;
+ for (mBufferMask = 8; mBufferMask < mSampleRate; mBufferMask <<= 1);
+ mBufferMask *= BUFFER_SIZE;
mBuffer = new int16_t[mBufferMask];
--mBufferMask;
mBufferHead = 0;
mBufferTail = 0;
+ mLatencyTimer = 0;
mLatencyScore = 0;
// Initialize random bits.
@@ -196,8 +202,8 @@
}
}
- LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket,
- (codec ? codec->name : "RAW"), mSampleRate, mInterval);
+ LOGD("stream[%d] is configured as %s %dkHz %dms mode %d", mSocket,
+ (codec ? codec->name : "RAW"), mSampleRate, mInterval, mMode);
return true;
}
@@ -247,7 +253,7 @@
mTick += skipped * mInterval;
mSequence += skipped;
mTimestamp += skipped * mSampleCount;
- LOGD("stream[%d] skips %d packets", mSocket, skipped);
+ LOGV("stream[%d] skips %d packets", mSocket, skipped);
}
tick = mTick;
@@ -296,7 +302,7 @@
if (!mixed) {
if ((mTick ^ mLogThrottle) >> 10) {
mLogThrottle = mTick;
- LOGD("stream[%d] no data", mSocket);
+ LOGV("stream[%d] no data", mSocket);
}
return;
}
@@ -324,7 +330,7 @@
buffer[2] = mSsrc;
int length = mCodec->encode(&buffer[3], samples);
if (length <= 0) {
- LOGD("stream[%d] encoder error", mSocket);
+ LOGV("stream[%d] encoder error", mSocket);
return;
}
sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote,
@@ -340,31 +346,37 @@
}
// Make sure mBufferHead and mBufferTail are reasonable.
- if ((unsigned int)(tick + 256 - mBufferHead) > 1024) {
- mBufferHead = tick - 64;
+ if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
+ mBufferHead = tick - HISTORY_SIZE;
mBufferTail = mBufferHead;
}
- if (tick - mBufferHead > 64) {
+ if (tick - mBufferHead > HISTORY_SIZE) {
// Throw away outdated samples.
- mBufferHead = tick - 64;
+ mBufferHead = tick - HISTORY_SIZE;
if (mBufferTail - mBufferHead < 0) {
mBufferTail = mBufferHead;
}
}
- if (mBufferTail - tick <= 80) {
- mLatencyScore = tick;
- } else if (tick - mLatencyScore >= 5000) {
- // Reset the jitter buffer to 40ms if the latency keeps larger than 80ms
- // in the past 5s. This rarely happens, so let us just keep it simple.
- LOGD("stream[%d] latency control", mSocket);
- mBufferTail = tick + 40;
+ // Adjust the jitter buffer if the latency keeps larger than two times of the
+ // packet interval in the past two seconds.
+ int score = mBufferTail - tick - mInterval * 2;
+ if (mLatencyScore > score) {
+ mLatencyScore = score;
+ }
+ if (mLatencyScore <= 0) {
+ mLatencyTimer = tick;
+ mLatencyScore = score;
+ } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
+ LOGV("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
+ mBufferTail -= mLatencyScore;
+ mLatencyTimer = tick;
}
- if (mBufferTail - mBufferHead > 256 - mInterval) {
+ if (mBufferTail - mBufferHead > BUFFER_SIZE - mInterval) {
// Buffer overflow. Drop the packet.
- LOGD("stream[%d] buffer overflow", mSocket);
+ LOGV("stream[%d] buffer overflow", mSocket);
recv(mSocket, &c, 1, MSG_DONTWAIT);
return;
}
@@ -388,7 +400,7 @@
// reliable but at least they can be used to identify duplicates?
if (length < 12 || length > (int)sizeof(buffer) ||
(ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
- LOGD("stream[%d] malformed packet", mSocket);
+ LOGV("stream[%d] malformed packet", mSocket);
return;
}
int offset = 12 + ((buffer[0] & 0x0F) << 2);
@@ -408,22 +420,22 @@
}
}
if (length <= 0) {
- LOGD("stream[%d] decoder error", mSocket);
+ LOGV("stream[%d] decoder error", mSocket);
return;
}
if (tick - mBufferTail > 0) {
- // Buffer underrun. Reset the jitter buffer to 40ms.
- LOGD("stream[%d] buffer underrun", mSocket);
+ // Buffer underrun. Reset the jitter buffer.
+ LOGV("stream[%d] buffer underrun", mSocket);
if (mBufferTail - mBufferHead <= 0) {
- mBufferHead = tick + 40;
+ mBufferHead = tick + mInterval;
mBufferTail = mBufferHead;
} else {
- int tail = (tick + 40) * mSampleRate;
+ int tail = (tick + mInterval) * mSampleRate;
for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
mBuffer[i & mBufferMask] = 0;
}
- mBufferTail = tick + 40;
+ mBufferTail = tick + mInterval;
}
}
@@ -450,7 +462,6 @@
bool add(AudioStream *stream);
bool remove(int socket);
-private:
enum {
ON_HOLD = 0,
MUTED = 1,
@@ -459,6 +470,7 @@
LAST_MODE = 3,
};
+private:
AudioStream *mChain;
int mEventQueue;
volatile int mDtmfEvent;
@@ -680,7 +692,7 @@
int count = 0;
for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- if (!stream->mTick || tick - stream->mTick >= 0) {
+ if (tick - stream->mTick >= 0) {
stream->encode(tick, chain);
}
if (deadline - stream->mTick > 0) {
@@ -764,11 +776,14 @@
char c;
while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
- // Start your engine!
- track.start();
+ // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
+ // disabled due to buffer underrun while waiting for AudioRecord.
if (mode != MUTED) {
record.start();
+ int16_t one;
+ record.read(&one, sizeof(one));
}
+ track.start();
while (!exitPending()) {
int16_t output[sampleCount];
@@ -794,34 +809,30 @@
track.releaseBuffer(&buffer);
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
LOGE("cannot write to AudioTrack");
- break;
+ return true;
}
}
if (toRead > 0) {
AudioRecord::Buffer buffer;
- buffer.frameCount = record.frameCount();
+ buffer.frameCount = toRead;
status_t status = record.obtainBuffer(&buffer, 1);
if (status == NO_ERROR) {
- int count = ((int)buffer.frameCount < toRead) ?
- buffer.frameCount : toRead;
- memcpy(&input[sampleCount - toRead], buffer.i8, count * 2);
- toRead -= count;
- if (buffer.frameCount < record.frameCount()) {
- buffer.frameCount = count;
- }
+ int offset = sampleCount - toRead;
+ memcpy(&input[offset], buffer.i8, buffer.size);
+ toRead -= buffer.frameCount;
record.releaseBuffer(&buffer);
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
LOGE("cannot read from AudioRecord");
- break;
+ return true;
}
}
}
if (chances <= 0) {
- LOGE("device loop timeout");
- break;
+ LOGW("device loop timeout");
+ while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
}
if (mode != MUTED) {
@@ -934,6 +945,10 @@
void setMode(JNIEnv *env, jobject thiz, jint mode)
{
+ if (mode < 0 || mode > AudioGroup::LAST_MODE) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
if (group && !group->setMode(mode)) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp
index 091afa9..a467acf 100644
--- a/voip/jni/rtp/G711Codec.cpp
+++ b/voip/jni/rtp/G711Codec.cpp
@@ -18,7 +18,7 @@
namespace {
-int8_t gExponents[128] = {
+const int8_t gExponents[128] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
diff --git a/voip/jni/rtp/RtpStream.cpp b/voip/jni/rtp/RtpStream.cpp
index 33b88e43..f5efc17 100644
--- a/voip/jni/rtp/RtpStream.cpp
+++ b/voip/jni/rtp/RtpStream.cpp
@@ -88,13 +88,11 @@
jint dup(JNIEnv *env, jobject thiz)
{
- int socket1 = env->GetIntField(thiz, gNative);
- int socket2 = ::dup(socket1);
- if (socket2 == -1) {
+ int socket = ::dup(env->GetIntField(thiz, gNative));
+ if (socket == -1) {
jniThrowException(env, "java/lang/IllegalStateException", strerror(errno));
}
- LOGD("dup %d to %d", socket1, socket2);
- return socket2;
+ return socket;
}
void close(JNIEnv *env, jobject thiz)
@@ -102,7 +100,6 @@
int socket = env->GetIntField(thiz, gNative);
::close(socket);
env->SetIntField(thiz, gNative, -1);
- LOGD("close %d", socket);
}
JNINativeMethod gMethods[] = {