Merge "NetworkStatsService: check kernel bandwidth support before enabling"
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index acf2f2f1..1c9285e 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -432,17 +432,17 @@
// compatible with large screens, so diddle it.
CompatibilityInfo.computeCompatibleScaling(inoutDm, inoutDm);
} else {
- inoutDm.widthPixels = inoutDm.unscaledWidthPixels;
- inoutDm.heightPixels = inoutDm.unscaledHeightPixels;
+ inoutDm.widthPixels = inoutDm.noncompatWidthPixels;
+ inoutDm.heightPixels = inoutDm.noncompatHeightPixels;
}
if (isScalingRequired()) {
float invertedRatio = applicationInvertedScale;
- inoutDm.density *= invertedRatio;
+ inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
inoutDm.densityDpi = (int)((inoutDm.density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
- inoutDm.scaledDensity *= invertedRatio;
- inoutDm.xdpi *= invertedRatio;
- inoutDm.ydpi *= invertedRatio;
+ inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
+ inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
+ inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
}
@@ -471,8 +471,8 @@
* @return Returns the scaling factor for the window.
*/
public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) {
- final int width = dm.unscaledWidthPixels;
- final int height = dm.unscaledHeightPixels;
+ final int width = dm.noncompatWidthPixels;
+ final int height = dm.noncompatHeightPixels;
int shortSize, longSize;
if (width < height) {
shortSize = width;
@@ -532,7 +532,9 @@
sb.append(applicationDensity);
sb.append("dpi");
if (isScalingRequired()) {
- sb.append(" scaling");
+ sb.append(" ");
+ sb.append(applicationScale);
+ sb.append("x");
}
if (!supportsScreen()) {
sb.append(" resizing");
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index bd8b1a4..324c9fd 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1416,23 +1416,19 @@
}
if (metrics != null) {
mMetrics.setTo(metrics);
- // NOTE: We should re-arrange this code to create a Display
- // with the CompatibilityInfo that is used everywhere we deal
- // with the display in relation to this app, rather than
- // doing the conversion here. This impl should be okay because
- // we make sure to return a compatible display in the places
- // where there are public APIs to retrieve the display... but
- // it would be cleaner and more maintainble to just be
- // consistently dealing with a compatible display everywhere in
- // the framework.
- if (mCompatibilityInfo != null) {
- mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
- }
}
+ // NOTE: We should re-arrange this code to create a Display
+ // with the CompatibilityInfo that is used everywhere we deal
+ // with the display in relation to this app, rather than
+ // doing the conversion here. This impl should be okay because
+ // we make sure to return a compatible display in the places
+ // where there are public APIs to retrieve the display... but
+ // it would be cleaner and more maintainble to just be
+ // consistently dealing with a compatible display everywhere in
+ // the framework.
if (mCompatibilityInfo != null) {
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
}
- mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
int configChanges = 0xfffffff;
if (config != null) {
mTmpConfig.setTo(config);
@@ -1448,6 +1444,7 @@
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
}
+ mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
String locale = null;
if (mConfiguration.locale != null) {
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d594567..519b980 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -119,13 +119,37 @@
* being applied.
* @hide
*/
- public int unscaledWidthPixels;
+ public int noncompatWidthPixels;
/**
* The reported display height prior to any compatibility mode scaling
* being applied.
* @hide
*/
- public int unscaledHeightPixels;
+ public int noncompatHeightPixels;
+ /**
+ * The reported display density prior to any compatibility mode scaling
+ * being applied.
+ * @hide
+ */
+ public float noncompatDensity;
+ /**
+ * The reported scaled density prior to any compatibility mode scaling
+ * being applied.
+ * @hide
+ */
+ public float noncompatScaledDensity;
+ /**
+ * The reported display xdpi prior to any compatibility mode scaling
+ * being applied.
+ * @hide
+ */
+ public float noncompatXdpi;
+ /**
+ * The reported display ydpi prior to any compatibility mode scaling
+ * being applied.
+ * @hide
+ */
+ public float noncompatYdpi;
public DisplayMetrics() {
}
@@ -138,8 +162,12 @@
scaledDensity = o.scaledDensity;
xdpi = o.xdpi;
ydpi = o.ydpi;
- unscaledWidthPixels = o.unscaledWidthPixels;
- unscaledHeightPixels = o.unscaledHeightPixels;
+ noncompatWidthPixels = o.noncompatWidthPixels;
+ noncompatHeightPixels = o.noncompatHeightPixels;
+ noncompatDensity = o.noncompatDensity;
+ noncompatScaledDensity = o.noncompatScaledDensity;
+ noncompatXdpi = o.noncompatXdpi;
+ noncompatYdpi = o.noncompatYdpi;
}
public void setToDefaults() {
@@ -150,8 +178,8 @@
scaledDensity = density;
xdpi = DENSITY_DEVICE;
ydpi = DENSITY_DEVICE;
- unscaledWidthPixels = 0;
- unscaledHeightPixels = 0;
+ noncompatWidthPixels = 0;
+ noncompatHeightPixels = 0;
}
@Override
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3fa8dfd..2be5a49 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -107,8 +107,8 @@
CompatibilityInfo ci;
if (doCompat && (ci=mCompatibilityInfo.getIfNeeded()) != null) {
synchronized (mTmpMetrics) {
- mTmpMetrics.unscaledWidthPixels = outSize.x;
- mTmpMetrics.unscaledHeightPixels = outSize.y;
+ mTmpMetrics.noncompatWidthPixels = outSize.x;
+ mTmpMetrics.noncompatHeightPixels = outSize.y;
mTmpMetrics.density = mDensity;
ci.applyToDisplayMetrics(mTmpMetrics);
outSize.x = mTmpMetrics.widthPixels;
@@ -268,14 +268,15 @@
}
private void getNonSizeMetrics(DisplayMetrics outMetrics) {
- outMetrics.density = mDensity;
outMetrics.densityDpi = (int)((mDensity*DisplayMetrics.DENSITY_DEFAULT)+.5f);
- outMetrics.scaledDensity= outMetrics.density;
- outMetrics.xdpi = mDpiX;
- outMetrics.ydpi = mDpiY;
- outMetrics.unscaledWidthPixels = outMetrics.widthPixels;
- outMetrics.unscaledHeightPixels = outMetrics.heightPixels;
+ outMetrics.noncompatWidthPixels = outMetrics.widthPixels;
+ outMetrics.noncompatHeightPixels = outMetrics.heightPixels;
+
+ outMetrics.density = outMetrics.noncompatDensity = mDensity;
+ outMetrics.scaledDensity = outMetrics.noncompatScaledDensity = outMetrics.density;
+ outMetrics.xdpi = outMetrics.noncompatXdpi = mDpiX;
+ outMetrics.ydpi = outMetrics.noncompatYdpi = mDpiY;
}
static IWindowManager getWindowManager() {
diff --git a/core/jni/android/graphics/ParcelSurfaceTexture.cpp b/core/jni/android/graphics/ParcelSurfaceTexture.cpp
index 40966e1..754485f 100644
--- a/core/jni/android/graphics/ParcelSurfaceTexture.cpp
+++ b/core/jni/android/graphics/ParcelSurfaceTexture.cpp
@@ -17,9 +17,11 @@
#define LOG_TAG "ParcelSurfaceTexture"
#include <gui/SurfaceTextureClient.h>
+#include <surfaceflinger/Surface.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
+#include <android_runtime/android_view_Surface.h>
#include <utils/Log.h>
@@ -96,7 +98,16 @@
}
}
-static void ParcelSurfaceTexture_init(JNIEnv* env, jobject thiz, jobject jSurfaceTexture)
+static void ParcelSurfaceTexture_initFromSurface(
+ JNIEnv* env, jobject thiz, jobject jSurface)
+{
+ sp<Surface> surface(Surface_getSurface(env, jSurface));
+ sp<ISurfaceTexture> iSurfaceTexture(surface->getSurfaceTexture());
+ ParcelSurfaceTexture_setISurfaceTexture(env, thiz, iSurfaceTexture);
+}
+
+static void ParcelSurfaceTexture_initFromSurfaceTexture(
+ JNIEnv* env, jobject thiz, jobject jSurfaceTexture)
{
sp<ISurfaceTexture> iSurfaceTexture(
SurfaceTexture_getSurfaceTexture(env, jSurfaceTexture));
@@ -131,8 +142,10 @@
static JNINativeMethod gParcelSurfaceTextureMethods[] = {
{"nativeClassInit", "()V", (void*)ParcelSurfaceTexture_classInit },
- {"nativeInit", "(Landroid/graphics/SurfaceTexture;)V",
- (void *)ParcelSurfaceTexture_init },
+ {"nativeInitFromSurface", "(Landroid/view/Surface;)V",
+ (void *)ParcelSurfaceTexture_initFromSurface },
+ {"nativeInitFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V",
+ (void *)ParcelSurfaceTexture_initFromSurfaceTexture },
{ "nativeFinalize", "()V", (void *)ParcelSurfaceTexture_finalize },
{ "nativeWriteToParcel", "(Landroid/os/Parcel;I)V",
(void *)ParcelSurfaceTexture_writeToParcel },
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 0d28cb1..a8cb6f7 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -212,6 +212,12 @@
return surfaceTexture->getTimestamp();
}
+static jint SurfaceTexture_getQueuedCount(JNIEnv* env, jobject thiz)
+{
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ return surfaceTexture->getQueuedCount();
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gSurfaceTextureMethods[] = {
@@ -221,7 +227,8 @@
{"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
{"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },
{"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix },
- {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp }
+ {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp },
+ {"nativeGetQueuedCount", "()I", (void*)SurfaceTexture_getQueuedCount }
};
int register_android_graphics_SurfaceTexture(JNIEnv* env)
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 70c2f7b..0dc9293 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -156,7 +156,7 @@
static sp<Surface> getSurface(JNIEnv* env, jobject clazz)
{
- sp<Surface> result((Surface*)env->GetIntField(clazz, so.surface));
+ sp<Surface> result(Surface_getSurface(env, clazz));
if (result == 0) {
/*
* if this method is called from the WindowManager's process, it means
@@ -189,6 +189,11 @@
return env->IsInstanceOf(obj, surfaceClass);
}
+sp<Surface> Surface_getSurface(JNIEnv* env, jobject clazz) {
+ sp<Surface> surface((Surface*)env->GetIntField(clazz, so.surface));
+ return surface;
+}
+
static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface)
{
Surface* const p = (Surface*)env->GetIntField(clazz, so.surface);
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 75b8ead..abea85b 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -271,7 +271,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/media/index.html">
<span class="en">Media</span>
- </a><span class="new">updated!</span></li>
+ </a><span class="new">updated</span></li>
<li>
<a href="<?cs var:toroot ?>guide/topics/clipboard/copy-paste.html">
<span class="en">Copy and Paste</span>
@@ -302,11 +302,13 @@
</li> -->
<!--<li><a style="color:gray;">Localization</a></li> -->
<li><a href="<?cs var:toroot ?>guide/topics/appwidgets/index.html">
- <span class="en">App Widgets</span>
- </a></li>
+ <span class="en">App Widgets</span></a>
+ <span class="new">updated</span>
+ </li>
<li><a href="<?cs var:toroot?>guide/topics/wireless/bluetooth.html">
- <span class="en">Bluetooth</span>
- </a></li>
+ <span class="en">Bluetooth</span></a>
+ <span class="new">updated</span>
+ </li>
<li><a href="<?cs var:toroot?>guide/topics/nfc/index.html">
<span class="en">Near Field Communication</span>
</a></li>
@@ -337,8 +339,8 @@
</ul>
</li>
<li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html">
- <span class="en">Device Administration</span>
- </a>
+ <span class="en">Device Administration</span></a>
+ <span class="new">updated</span>
</li>
<li class="toggle-list">
<div>
diff --git a/docs/html/guide/topics/fundamentals/activities.jd b/docs/html/guide/topics/fundamentals/activities.jd
index 5cc1b45..cb453da 100644
--- a/docs/html/guide/topics/fundamentals/activities.jd
+++ b/docs/html/guide/topics/fundamentals/activities.jd
@@ -145,7 +145,7 @@
<h3 id="Declaring">Declaring the activity in the manifest</h3>
<p>You must declare your activity in the manifest file in order for it to
-be accessible to the system. To decalare your activity, open your manifest file and add an <a
+be accessible to the system. To declare your activity, open your manifest file and add an <a
href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element
as a child of the <a
href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
@@ -163,9 +163,16 @@
<p>There are several other attributes that you can include in this element, to define properties
such as the label for the activity, an icon for the activity, or a theme to style the activity's
-UI. See the <a
+UI. The <a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">{@code android:name}</a>
+attribute is the only required attribute—it specifies the class name of the activity. Once
+you publish your application, you should not change this name, because if you do, you might break
+some functionality, such as application shortcuts (read the blog post, <a
+href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Things
+That Cannot Change</a>).</p>
+
+<p>See the <a
href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element
-reference for more information about available attributes.</p>
+reference for more information about declaring your activity in the manifest.</p>
<h4>Using intent filters</h4>
diff --git a/docs/html/guide/topics/fundamentals/services.jd b/docs/html/guide/topics/fundamentals/services.jd
index d3ef70a..9c38897 100644
--- a/docs/html/guide/topics/fundamentals/services.jd
+++ b/docs/html/guide/topics/fundamentals/services.jd
@@ -203,7 +203,7 @@
<p>Like activities (and other components), you must declare all services in your application's
manifest file.</p>
-<p>To decalare your service, add a <a
+<p>To declare your service, add a <a
href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
as a child of the <a
href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
@@ -222,9 +222,17 @@
<p>There are other attributes you can include in the <a
href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element to
define properties such as permissions required to start the service and the process in
-which the service should run. See the <a
+which the service should run. The <a
+href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a>
+attribute is the only required attribute—it specifies the class name of the service. Once
+you publish your application, you should not change this name, because if you do, you might break
+some functionality where explicit intents are used to reference your service (read the blog post, <a
+href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Things
+That Cannot Change</a>).
+
+<p>See the <a
href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
-reference for more information.</p>
+reference for more information about declaring your service in the manifest.</p>
<p>Just like an activity, a service can define intent filters that allow other components to
invoke the service using implicit intents. By declaring intent filters, components
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index c910686..3486212 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -507,6 +507,10 @@
package name specified in the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>
element.
+<p>Once you publish your application, you <a
+href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">should not
+change this name</a> (unless you've set <code><a
+href="#exported">android:exported</a>="false"</code>).</p>
<p>
There is no default. The name must be specified.
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index 598e88f..d737a67 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -47,12 +47,15 @@
to "{@code http://schemas.android.com/apk/res/android}".</dd>
<dt><a name="package"></a>{@code package}</dt>
-<dd>A full Java package name for the application. The name should
+<dd>A full Java-language-style package name for the application. The name should
be unique. The name may contain uppercase or lowercase letters ('A'
through 'Z'), numbers, and underscores ('_'). However, individual
-package name parts may only start with letters. For example, applications
-published by Google could have names in the form
-<code>com.google.app.<i>application_name</i></code>.
+package name parts may only start with letters.
+
+<p>To avoid conflicts with other developers, you should use Internet domain ownership as the
+basis for your package names (in reverse). For example, applications published by Google start with
+<code>com.google</code>. You should also never use the <code>com.example</code> namespace when
+publishing your applications.</p>
<p>
The package name serves as a unique identifier for the application.
@@ -66,6 +69,12 @@
element's
<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">taskAffinity</a></code> attribute).
</p>
+
+ <p class="caution"><strong>Caution:</strong> Once you publish your application, you
+<strong>cannot change the package name</strong>. The package name defines your application's
+identity, so if you change it, then it is considered to be a different application and users of
+the previous version cannot update to the new version.</p>
+
</dd>
<dt><a name="uid"></a>{@code android:sharedUserId}</dt>
diff --git a/docs/html/guide/topics/manifest/receiver-element.jd b/docs/html/guide/topics/manifest/receiver-element.jd
index 7012c0f..8416c0c 100644
--- a/docs/html/guide/topics/manifest/receiver-element.jd
+++ b/docs/html/guide/topics/manifest/receiver-element.jd
@@ -122,6 +122,11 @@
"{@code . ReportReceiver}"), it is appended to the package name specified in
the <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> element.
+<p>Once you publish your application, you <a
+href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">should not
+change this name</a> (unless you've set <code><a
+href="#exported">android:exported</a>="false"</code>).</p>
+
<p>
There is no default. The name must be specified.
</p></dd>
diff --git a/docs/html/guide/topics/manifest/service-element.jd b/docs/html/guide/topics/manifest/service-element.jd
index d9a81b3..82d1f6a 100644
--- a/docs/html/guide/topics/manifest/service-element.jd
+++ b/docs/html/guide/topics/manifest/service-element.jd
@@ -6,7 +6,7 @@
<dl class="xml">
<dt>syntax:</dt>
<dd><pre class="stx"><service android:<a href="#enabled">enabled</a>=["true" | "false"]
- android:<a href="#exported">exported[</a>="true" | "false"]
+ android:<a href="#exported">exported</a>=["true" | "false"]
android:<a href="#icon">icon</a>="<i>drawable resource</i>"
android:<a href="#label">label</a>="<i>string resource</i>"
android:<a href="#nm">name</a>="<i>string</i>"
@@ -121,6 +121,11 @@
it is appended to the package name specified in the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> element.
+<p>Once you publish your application, you <a
+href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">should not
+change this name</a> (unless you've set <code><a
+href="#exported">android:exported</a>="false"</code>).</p>
+
<p>
There is no default. The name must be specified.
</p></dd>
diff --git a/docs/html/guide/topics/wireless/bluetooth.jd b/docs/html/guide/topics/wireless/bluetooth.jd
index a6c46d2..0af1d2c 100644
--- a/docs/html/guide/topics/wireless/bluetooth.jd
+++ b/docs/html/guide/topics/wireless/bluetooth.jd
@@ -1,57 +1,61 @@
page.title=Bluetooth
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
-
- <h2>Quickview</h2>
- <ul>
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Quickview</h2>
+ <ul>
<li>Android's bluetooth APIs allow your application to perform wireless data transactions with
-other devices</li>
- </ul>
-
- <h2>In this document</h2>
- <ol>
- <li><a href="#TheBasics">The Basics</a></li>
- <li><a href="#Permissions">Bluetooth Permissions</a></li>
- <li><a href="#SettingUp">Setting Up Bluetooth</a></li>
- <li><a href="#FindingDevices">Finding Devices</a>
- <ol>
- <li><a href="#QueryingPairedDevices">Querying paired devices</a></li>
- <li><a href="#DiscoveringDevices">Discovering devices</a></li>
- </ol></li>
- <li><a href="#ConnectingDevices">Connecting Devices</a>
- <ol>
- <li><a href="#ConnectingAsAServer">Connecting as a server</a></li>
- <li><a href="#ConnectingAsAClient">Connecting as a client</a></li>
- </ol></li>
+other devices</li>
+ </ul>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#TheBasics">The Basics</a></li>
+ <li><a href="#Permissions">Bluetooth Permissions</a></li>
+ <li><a href="#SettingUp">Setting Up Bluetooth</a></li>
+ <li><a href="#FindingDevices">Finding Devices</a>
+ <ol>
+ <li><a href="#QueryingPairedDevices">Querying paired devices</a></li>
+ <li><a href="#DiscoveringDevices">Discovering devices</a></li>
+ </ol></li>
+ <li><a href="#ConnectingDevices">Connecting Devices</a>
+ <ol>
+ <li><a href="#ConnectingAsAServer">Connecting as a server</a></li>
+ <li><a href="#ConnectingAsAClient">Connecting as a client</a></li>
+ </ol></li>
<li><a href="#ManagingAConnection">Managing a Connection</a></li>
- </ol>
-
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.bluetooth.BluetoothAdapter}</li>
- <li>{@link android.bluetooth.BluetoothDevice}</li>
- <li>{@link android.bluetooth.BluetoothSocket}</li>
- <li>{@link android.bluetooth.BluetoothServerSocket}</li>
- </ol>
-
- <h2>Related samples</h2>
- <ol>
- <li><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></li>
- </ol>
-
-</div>
-</div>
-
-
+ <li><a href="#Profiles">Working with Profiles</a>
+ <ol>
+ <li><a href="#AT-Commands">Vendor-specific AT commands</a>
+ </ol></li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.bluetooth.BluetoothAdapter}</li>
+ <li>{@link android.bluetooth.BluetoothDevice}</li>
+ <li>{@link android.bluetooth.BluetoothSocket}</li>
+ <li>{@link android.bluetooth.BluetoothServerSocket}</li>
+ </ol>
+
+ <h2>Related samples</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></li>
+ </ol>
+
+</div>
+</div>
+
+
<p>The Android platform includes support for the Bluetooth network stack,
which allows a device to wirelessly exchange data with other Bluetooth devices.
The application framework provides access to the Bluetooth functionality through
the Android Bluetooth APIs. These APIs let applications wirelessly
connect to other Bluetooth devices, enabling point-to-point and multipoint
-wireless features.</p>
-
+wireless features.</p>
+
<p>Using the Bluetooth APIs, an Android application can perform the
following:</p>
<ul>
@@ -69,14 +73,14 @@
<p>This document describes how to use the Android Bluetooth APIs to accomplish
the four major tasks necessary to communicate using Bluetooth: setting up
Bluetooth, finding devices that are either paired or available in the local
-area, connecting devices, and transferring data between devices.</p>
-
+area, connecting devices, and transferring data between devices.</p>
+
<p>All of the Bluetooth APIs are available in the {@link android.bluetooth}
-package. Here's a summary of the classes you will need to create Bluetooth
-connections:</p>
-
-<dl>
-<dt>{@link android.bluetooth.BluetoothAdapter}</dt>
+package. Here's a summary of the classes and interfaces you will need to create Bluetooth
+connections:</p>
+
+<dl>
+<dt>{@link android.bluetooth.BluetoothAdapter}</dt>
<dd>Represents the local Bluetooth adapter (Bluetooth radio). The
{@link android.bluetooth.BluetoothAdapter} is the entry-point for all Bluetooth
interaction. Using this,
@@ -84,51 +88,72 @@
devices, instantiate a {@link android.bluetooth.BluetoothDevice} using a known
MAC address, and create a {@link android.bluetooth.BluetoothServerSocket} to
listen for communications
-from other devices.</dd>
-
-<dt>{@link android.bluetooth.BluetoothDevice}</dt>
+from other devices.</dd>
+
+<dt>{@link android.bluetooth.BluetoothDevice}</dt>
<dd>Represents a remote Bluetooth device. Use this to request a connection
with a remote device through a {@link android.bluetooth.BluetoothSocket} or
query information about the
-device such as its name, address, class, and bonding state.</dd>
-
-<dt>{@link android.bluetooth.BluetoothSocket}</dt>
+device such as its name, address, class, and bonding state.</dd>
+
+<dt>{@link android.bluetooth.BluetoothSocket}</dt>
<dd>Represents the interface for a Bluetooth socket (similar to a TCP
{@link java.net.Socket}). This is the connection point that allows
an application to exchange data with another Bluetooth device via InputStream
-and OutputStream.</dd>
-
-<dt>{@link android.bluetooth.BluetoothServerSocket}</dt>
+and OutputStream.</dd>
+
+<dt>{@link android.bluetooth.BluetoothServerSocket}</dt>
<dd>Represents an open server socket that listens for incoming requests
(similar to a TCP {@link java.net.ServerSocket}). In order to connect two
Android devices, one device must open a server socket with this class. When a
remote Bluetooth device makes a connection request to the this device, the
{@link android.bluetooth.BluetoothServerSocket} will return a connected {@link
android.bluetooth.BluetoothSocket} when the
-connection is accepted.</dd>
-
-<dt>{@link android.bluetooth.BluetoothClass}</dt>
+connection is accepted.</dd>
+
+<dt>{@link android.bluetooth.BluetoothClass}</dt>
<dd>Describes the general characteristics and capabilities of a Bluetooth
device. This is a read-only set of properties that define the device's major and
minor device classes and its services. However, this does not reliably describe
all Bluetooth profiles and services supported by the device, but is useful as a
-hint to the device type.</dd>
-</dl>
+hint to the device type.</dd>
+
+<dt>{@link android.bluetooth.BluetoothProfile}</dt> <dd>An interface that
+represents a Bluetooth profile. A <em>Bluetooth profile</em> is a wireless
+interface specification for Bluetooth-based communication between devices. An
+example is the Hands-Free profile. For more discussion of profiles, see <a
+href="#Profiles">Working with Profiles</a></dd>
+<dt>{@link android.bluetooth.BluetoothHeadset}</dt> <dd>Provides support for
+Bluetooth headsets to be used with mobile phones. This includes both Bluetooth
+Headset and Hands-Free (v1.5) profiles.</dd>
+<dt>{@link android.bluetooth.BluetoothA2dp}</dt> <dd> Defines how high quality
+audio can be streamed from one device to another over a Bluetooth connection.
+"A2DP" stands for Advanced Audio Distribution Profile.</dd>
+<dt>{@link android.bluetooth.BluetoothProfile.ServiceListener}</dt>
-<h2 id="Permissions">Bluetooth Permissions</h2>
-
+<dd>An interface that notifies {@link android.bluetooth.BluetoothProfile} IPC
+clients when they have been connected to or disconnected from the service (that
+is, the internal service that runs a particular profile). </dd>
+
+</dl>
+
+
+
+
+<h2 id="Permissions">Bluetooth Permissions</h2>
+
<p>In order to use Bluetooth features in your application, you need to declare
at least one of two Bluetooth permissions: {@link
android.Manifest.permission#BLUETOOTH} and {@link
-android.Manifest.permission#BLUETOOTH_ADMIN}.</p>
-
+android.Manifest.permission#BLUETOOTH_ADMIN}.</p>
+
<p>You must request the {@link android.Manifest.permission#BLUETOOTH} permission
in order to perform any Bluetooth communication, such as requesting a
-connection, accepting a connection, and transferring data.</p>
-
+connection, accepting a connection, and transferring data.</p>
+
<p>You must request the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
permission in order to initiate device discovery or manipulate Bluetooth
settings. Most applications need this permission solely for the
@@ -136,40 +161,40 @@
permission should not be used, unless the application is a "power manager" that
will modify Bluetooth settings upon user request. <strong>Note:</strong> If you
use {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then must
-also have the {@link android.Manifest.permission#BLUETOOTH} permission.</p>
-
+also have the {@link android.Manifest.permission#BLUETOOTH} permission.</p>
+
<p>Declare the Bluetooth permission(s) in your application manifest file. For
-example:</p>
-
-<pre>
+example:</p>
+
+<pre>
<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
...
</manifest>
-</pre>
-
+</pre>
+
<p>See the <a
-href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a>
-reference for more information about declaring application permissions.</p>
-
-
-<h2 id="SettingUp">Setting Up Bluetooth</h2>
-
-<div class="figure" style="width:200px">
-<img src="{@docRoot}images/bt_enable_request.png" />
+href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a>
+reference for more information about declaring application permissions.</p>
+
+
+<h2 id="SettingUp">Setting Up Bluetooth</h2>
+
+<div class="figure" style="width:200px">
+<img src="{@docRoot}images/bt_enable_request.png" />
<strong>Figure 1:</strong> The enabling Bluetooth dialog.
-</div>
-
+</div>
+
<p>Before your application can communicate over Bluetooth, you need to verify
-that Bluetooth is supported on the device, and if so, ensure that it is enabled.</p>
-
+that Bluetooth is supported on the device, and if so, ensure that it is enabled.</p>
+
<p>If Bluetooth is not supported, then you should gracefully disable any
Bluetooth features. If Bluetooth is supported, but disabled, then you can request that the
user enable Bluetooth without leaving your application. This setup is
-accomplished in two steps, using the {@link android.bluetooth.BluetoothAdapter}.</p>
-
-
-<ol>
+accomplished in two steps, using the {@link android.bluetooth.BluetoothAdapter}.</p>
+
+
+<ol>
<li>Get the {@link android.bluetooth.BluetoothAdapter}
<p>The {@link android.bluetooth.BluetoothAdapter} is required for any and all Bluetooth
activity. To get the {@link android.bluetooth.BluetoothAdapter}, call the static {@link
@@ -178,15 +203,15 @@
Bluetooth adapter (the Bluetooth radio). There's one Bluetooth adapter for the
entire system, and your application can interact with it using this object. If
{@link android.bluetooth.BluetoothAdapter#getDefaultAdapter()} returns null,
-then the device does not support Bluetooth and your story ends here. For example:</p>
-<pre>
+then the device does not support Bluetooth and your story ends here. For example:</p>
+<pre>
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
-</pre>
-</li>
-
+</pre>
+</li>
+
<li>Enable Bluetooth
<p>Next, you need to ensure that Bluetooth is enabled. Call {@link
android.bluetooth.BluetoothAdapter#isEnabled()} to check whether Bluetooth is
@@ -195,26 +220,26 @@
android.app.Activity#startActivityForResult(Intent,int) startActivityForResult()}
with the {@link android.bluetooth.BluetoothAdapter#ACTION_REQUEST_ENABLE} action Intent.
This will issue a request to enable Bluetooth through the system settings (without
-stopping your application). For example:</p>
-<pre>
+stopping your application). For example:</p>
+<pre>
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
-</pre>
-
+</pre>
+
<p>A dialog will appear requesting user permission to enable Bluetooth, as shown
in Figure 1. If the user responds "Yes," the system will begin to enable Bluetooth
-and focus will return to your application once the process completes (or fails).</p>
+and focus will return to your application once the process completes (or fails).</p>
<p>If enabling Bluetooth succeeds, your Activity will receive the {@link
android.app.Activity#RESULT_OK} result code in the {@link
android.app.Activity#onActivityResult(int,int,Intent) onActivityResult()}
callback. If Bluetooth was not enabled
due to an error (or the user responded "No") then the result code will be {@link
-android.app.Activity#RESULT_CANCELED}.</p>
-</li>
-</ol>
-
+android.app.Activity#RESULT_CANCELED}.</p>
+</li>
+</ol>
+
<p>Optionally, your application can also listen for the
{@link android.bluetooth.BluetoothAdapter#ACTION_STATE_CHANGED} broadcast Intent, which
the system will broadcast whenever the Bluetooth state has changed. This broadcast contains
@@ -226,21 +251,21 @@
android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}, and {@link
android.bluetooth.BluetoothAdapter#STATE_OFF}. Listening for this
broadcast can be useful to detect changes made to the Bluetooth state while your
-app is running.</p>
-
+app is running.</p>
+
<p class="note"><strong>Tip:</strong> Enabling discoverability will automatically
enable Bluetooth. If you plan to consistently enable device discoverability before
performing Bluetooth activity, you can skip
step 2 above. Read about <a href="#EnablingDiscoverability">enabling discoverability</a>,
-below.</p>
-
-
-<h2 id="FindingDevices">Finding Devices</h2>
-
+below.</p>
+
+
+<h2 id="FindingDevices">Finding Devices</h2>
+
<p>Using the {@link android.bluetooth.BluetoothAdapter}, you can find remote Bluetooth
devices either through device discovery or by querying the list of paired (bonded)
-devices.</p>
-
+devices.</p>
+
<p>Device discovery is a scanning procedure that searches the local area for
Bluetooth enabled devices and then requesting some information about each one
(this is sometimes referred to as "discovering," "inquiring" or "scanning").
@@ -249,15 +274,15 @@
discoverable, it will respond to the discovery request by sharing some
information, such as the device name, class, and its unique MAC address. Using
this information, the device performing discovery can then choose to initiate a
-connection to the discovered device.</p>
-
+connection to the discovered device.</p>
+
<p>Once a connection is made with a remote device for the first time, a pairing
request is automatically presented to the user. When a device is
paired, the basic information about that device (such as the device name, class,
and MAC address) is saved and can be read using the Bluetooth APIs. Using the
known MAC address for a remote device, a connection can be initiated with it at
-any time without performing discovery (assuming the device is within range).</p>
-
+any time without performing discovery (assuming the device is within range).</p>
+
<p>Remember there is a difference between being paired and being connected. To
be paired means that two devices are aware of each other's existence, have a
shared link-key that can be used for authentication, and are capable of
@@ -265,28 +290,28 @@
the devices currently share an RFCOMM channel and are able to transmit data with
each other. The current Android Bluetooth API's require devices to be paired
before an RFCOMM connection can be established. (Pairing is automatically performed
-when you initiate an encrypted connection with the Bluetooth APIs.)</p>
-
+when you initiate an encrypted connection with the Bluetooth APIs.)</p>
+
<p>The following sections describe how to find devices that have been paired, or
-discover new devices using device discovery.</p>
-
+discover new devices using device discovery.</p>
+
<p class="note"><strong>Note:</strong> Android-powered devices are not
discoverable by default. A user can make
the device discoverable for a limited time through the system settings, or an
application can request that the user enable discoverability without leaving the
-application. How to <a href="#EnablingDiscoverability">enable discoverability</a>
-is discussed below.</p>
-
-
-<h3 id="QueryingPairedDevices">Querying paired devices</h3>
-
+application. How to <a href="#EnablingDiscoverability">enable discoverability</a>
+is discussed below.</p>
+
+
+<h3 id="QueryingPairedDevices">Querying paired devices</h3>
+
<p>Before performing device discovery, its worth querying the set
of paired devices to see if the desired device is already known. To do so,
call {@link android.bluetooth.BluetoothAdapter#getBondedDevices()}. This
will return a Set of {@link android.bluetooth.BluetoothDevice}s representing
paired devices. For example, you can query all paired devices and then
-show the name of each device to the user, using an ArrayAdapter:</p>
-<pre>
+show the name of each device to the user, using an ArrayAdapter:</p>
+<pre>
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
@@ -296,24 +321,24 @@
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
-</pre>
-
+</pre>
+
<p>All that's needed from the {@link android.bluetooth.BluetoothDevice} object
in order to initiate a connection is the MAC address. In this example, it's saved
as a part of an ArrayAdapter that's shown to the user. The MAC address can later
be extracted in order to initiate the connection. You can learn more about creating
-a connection in the section about <a href="#ConnectingDevices">Connecting Devices</a>.</p>
-
-
-<h3 id="DiscoveringDevices">Discovering devices</h3>
-
+a connection in the section about <a href="#ConnectingDevices">Connecting Devices</a>.</p>
+
+
+<h3 id="DiscoveringDevices">Discovering devices</h3>
+
<p>To start discovering devices, simply call {@link
android.bluetooth.BluetoothAdapter#startDiscovery()}. The
process is asynchronous and the method will immediately return with a boolean
indicating whether discovery has successfully started. The discovery process
usually involves an inquiry scan of about 12 seconds, followed by a page scan of
-each found device to retrieve its Bluetooth name.</p>
-
+each found device to retrieve its Bluetooth name.</p>
+
<p>Your application must register a BroadcastReceiver for the
{@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent in
order to receive information about each
@@ -324,8 +349,8 @@
{@link android.bluetooth.BluetoothDevice#EXTRA_CLASS}, containing a
{@link android.bluetooth.BluetoothDevice} and a {@link
android.bluetooth.BluetoothClass}, respectively. For example, here's how you can
-register to handle the broadcast when devices are discovered:</p>
-<pre>
+register to handle the broadcast when devices are discovered:</p>
+<pre>
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -342,15 +367,15 @@
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
-</pre>
-
+</pre>
+
<p>All that's needed from the {@link android.bluetooth.BluetoothDevice} object
in order to initiate a
connection is the MAC address. In this example, it's saved as a part of an
ArrayAdapter that's shown to the user. The MAC address can later be extracted in
order to initiate the connection. You can learn more about creating a connection
-in the section about <a href="#ConnectingDevices">Connecting Devices</a>.</p>
-
+in the section about <a href="#ConnectingDevices">Connecting Devices</a>.</p>
+
<p class="caution"><strong>Caution:</strong> Performing device discovery is
a heavy procedure for the Bluetooth
adapter and will consume a lot of its resources. Once you have found a device to
@@ -359,41 +384,44 @@
attempting a connection. Also, if you
already hold a connection with a device, then performing discovery can
significantly reduce the bandwidth available for the connection, so you should
-not perform discovery while connected.</p>
-
-<h4 id="EnablingDiscoverability">Enabling discoverability</h4>
-
+not perform discovery while connected.</p>
+
+<h4 id="EnablingDiscoverability">Enabling discoverability</h4>
+
<p>If you would like to make the local device discoverable to other devices,
call {@link android.app.Activity#startActivityForResult(Intent,int)} with the
-{@link android.bluetooth.BluetoothAdapter#ACTION_REQUEST_DISCOVERABLE} action Intent.
-This will issue a request to enable discoverable mode through the system settings (without
-stopping your application). By default, the device will become discoverable for
-120 seconds. You can define a different duration by adding the
-{@link android.bluetooth.BluetoothAdapter#EXTRA_DISCOVERABLE_DURATION} Intent extra
-(maximum duration is 300 seconds). For example:</p>
-<pre>
-Intent discoverableIntent = new
+{@link android.bluetooth.BluetoothAdapter#ACTION_REQUEST_DISCOVERABLE} action
+Intent. This will issue a request to enable discoverable mode through the system
+settings (without stopping your application). By default, the device will become
+discoverable for 120 seconds. You can define a different duration by adding the
+{@link android.bluetooth.BluetoothAdapter#EXTRA_DISCOVERABLE_DURATION} Intent
+extra. The maximum duration an app can set is 3600 seconds, and a value of 0
+means the device is always discoverable. Any value below 0 or above 3600 is
+automatically set to 120 secs). For example, this snippet sets the duration to
+300:</p>
+
+<pre>Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
-</pre>
-
-<div class="figure" style="width:200px">
-<img src="{@docRoot}images/bt_enable_discoverable.png" />
+</pre>
+
+<div class="figure" style="width:200px">
+<img src="{@docRoot}images/bt_enable_discoverable.png" />
<strong>Figure 2:</strong> The enabling discoverability dialog.
-</div>
-
+</div>
+
<p>A dialog will be displayed, requesting user permission to make the device
discoverable, as shown in Figure 2. If the user responds "Yes," then the device
will become discoverable for the specified amount of time. Your Activity will
then receive a call to the {@link android.app.Activity#onActivityResult(int,int,Intent)
onActivityResult())} callback, with the result code equal to the duration that the device
is discoverable. If the user responded "No" or if an error occurred, the result code will
-be Activity.RESULT_CANCELLED.</p>
-
+be Activity.RESULT_CANCELLED.</p>
+
<p class="note"><strong>Note:</strong> If Bluetooth has not been enabled on the device,
-then enabling device discoverability will automatically enable Bluetooth.</p>
-
+then enabling device discoverability will automatically enable Bluetooth.</p>
+
<p>The device will silently remain in discoverable mode for the allotted time.
If you would like to be notified when the discoverable mode has changed, you can
register a BroadcastReceiver for the {@link
@@ -407,18 +435,18 @@
android.bluetooth.BluetoothAdapter#SCAN_MODE_NONE},
which indicate that the device is either in discoverable mode, not in
discoverable mode but still able to receive connections, or not in discoverable
-mode and unable to receive connections, respectively.</p>
-
+mode and unable to receive connections, respectively.</p>
+
<p>You do not need to enable device discoverability if you will be initiating
the connection to a remote device. Enabling discoverability is only necessary when
you want your application to host a server socket that will accept incoming
connections, because the remote devices must be able to discover the device
-before it can initiate the connection.</p>
-
-
-
-<h2 id="ConnectingDevices">Connecting Devices</h2>
-
+before it can initiate the connection.</p>
+
+
+
+<h2 id="ConnectingDevices">Connecting Devices</h2>
+
<p>In order to create a connection between your application on two devices, you
must implement both the server-side and client-side mechanisms, because one
device must open a server socket and the other one must initiate the connection
@@ -428,36 +456,36 @@
point, each device can obtain input and output streams and data transfer can
begin, which is discussed in the section about <a
href="#ManagingAConnection">Managing a Connection</a>. This section describes how
-to initiate the connection between two devices.</p>
-
+to initiate the connection between two devices.</p>
+
<p>The server device and the client device each obtain the required {@link
android.bluetooth.BluetoothSocket} in different ways. The server will receive it
when an incoming connection is accepted. The client will receive it when it
-opens an RFCOMM channel to the server.</p>
-
-<div class="figure" style="width:200px">
-<img src="{@docRoot}images/bt_pairing_request.png" />
+opens an RFCOMM channel to the server.</p>
+
+<div class="figure" style="width:200px">
+<img src="{@docRoot}images/bt_pairing_request.png" />
<strong>Figure 3:</strong> The Bluetooth pairing dialog.
-</div>
-
+</div>
+
<p>One implementation technique is to automatically prepare each device as a
server, so that each one has a server socket open and listening for connections.
Then either device can initiate a connection with the other and become the
client. Alternatively, one device can explicitly "host" the connection and open
a server socket on demand and the other device can simply initiate the
-connection.</p>
-
+connection.</p>
+
<p class="note"><strong>Note:</strong> If the two devices have not been previously paired,
then the Android framework will automatically show a pairing request notification or
dialog to the user during the connection procedure, as shown in Figure 3. So
when attempting to connect devices,
your application does not need to be concerned about whether or not the devices are
paired. Your RFCOMM connection attempt will block until the user has successfully paired,
-or will fail if the user rejects pairing, or if pairing fails or times out. </p>
-
-
-<h3 id="ConnectingAsAServer">Connecting as a server</h3>
-
+or will fail if the user rejects pairing, or if pairing fails or times out. </p>
+
+
+<h3 id="ConnectingAsAServer">Connecting as a server</h3>
+
<p>When you want to connect two devices, one must act as a server by holding an
open {@link android.bluetooth.BluetoothServerSocket}. The purpose of the server
socket is to listen for incoming connection requests and when one is accepted,
@@ -465,26 +493,26 @@
android.bluetooth.BluetoothSocket} is acquired from the {@link
android.bluetooth.BluetoothServerSocket},
the {@link android.bluetooth.BluetoothServerSocket} can (and should) be
-discarded, unless you want to accept more connections.</p>
-
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h2>About UUID</h2>
-
+discarded, unless you want to accept more connections.</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>About UUID</h2>
+
<p>A Universally Unique Identifier (UUID) is a standardized 128-bit format for a string
ID used to uniquely identify information. The point of a UUID is that it's big
enough that you can select any random and it won't clash. In this case, it's
used to uniquely identify your application's Bluetooth service. To get a UUID to
use with your application, you can use one of the many random UUID generators on
the web, then initialize a {@link java.util.UUID} with {@link
-java.util.UUID#fromString(String)}.</p>
-</div>
-</div>
-
+java.util.UUID#fromString(String)}.</p>
+</div>
+</div>
+
<p>Here's the basic procedure to set up a server socket and accept a
-connection:</p>
-
-<ol>
+connection:</p>
+
+<ol>
<li>Get a {@link android.bluetooth.BluetoothServerSocket} by calling the
{@link
android.bluetooth.BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,
@@ -496,9 +524,9 @@
agreement with the client device. That is, when the client attempts to connect
with this device, it will carry a UUID that uniquely identifies the service with
which it wants to connect. These UUIDs must match in order for the connection to
-be accepted (in the next step).</p>
-</li>
-
+be accepted (in the next step).</p>
+</li>
+
<li>Start listening for connection requests by calling
{@link android.bluetooth.BluetoothServerSocket#accept()}.
<p>This is a blocking call. It will return when either a connection has been
@@ -506,9 +534,9 @@
remote device has sent a connection request with a UUID matching the one
registered with this listening server socket. When successful, {@link
android.bluetooth.BluetoothServerSocket#accept()} will
-return a connected {@link android.bluetooth.BluetoothSocket}.</p>
-</li>
-
+return a connected {@link android.bluetooth.BluetoothSocket}.</p>
+</li>
+
<li>Unless you want to accept additional connections, call
{@link android.bluetooth.BluetoothServerSocket#close()}.
<p>This releases the server socket and all its resources, but does <em>not</em> close the
@@ -517,10 +545,10 @@
connected client per channel at a time, so in most cases it makes sense to call {@link
android.bluetooth.BluetoothServerSocket#close()} on the {@link
android.bluetooth.BluetoothServerSocket} immediately after accepting a connected
-socket.</p>
-</li>
-</ol>
-
+socket.</p>
+</li>
+</ol>
+
<p>The {@link android.bluetooth.BluetoothServerSocket#accept()} call should not
be executed in the main Activity UI thread because it is a blocking call and
will prevent any other interaction with the application. It usually makes
@@ -533,16 +561,16 @@
android.bluetooth.BluetoothSocket}) from another thread and the blocked call will
immediately return. Note that all methods on a {@link
android.bluetooth.BluetoothServerSocket} or {@link android.bluetooth.BluetoothSocket}
-are thread-safe.</p>
-
-<h4>Example</h4>
-
+are thread-safe.</p>
+
+<h4>Example</h4>
+
<p>Here's a simplified thread for the server component that accepts incoming
-connections:</p>
-<pre>
+connections:</p>
+<pre>
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
-
+
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
@@ -553,7 +581,7 @@
} catch (IOException e) { }
mmServerSocket = tmp;
}
-
+
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
@@ -572,7 +600,7 @@
}
}
}
-
+
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
@@ -580,37 +608,37 @@
} catch (IOException e) { }
}
}
-</pre>
-
+</pre>
+
<p>In this example, only one incoming connection is desired, so as soon as a
connection is accepted and the {@link android.bluetooth.BluetoothSocket} is
acquired, the application
sends the acquired {@link android.bluetooth.BluetoothSocket} to a separate
thread, closes the
-{@link android.bluetooth.BluetoothServerSocket} and breaks the loop.</p>
-
+{@link android.bluetooth.BluetoothServerSocket} and breaks the loop.</p>
+
<p>Note that when {@link android.bluetooth.BluetoothServerSocket#accept()}
returns the {@link android.bluetooth.BluetoothSocket}, the socket is already
connected, so you should <em>not</em> call {@link
android.bluetooth.BluetoothSocket#connect()} (as you do from the
-client-side).</p>
-
+client-side).</p>
+
<p><code>manageConnectedSocket()</code> is a fictional method in the application
that will
initiate the thread for transferring data, which is discussed in the section
-about <a href="#ManagingAConnection">Managing a Connection</a>.</p>
-
+about <a href="#ManagingAConnection">Managing a Connection</a>.</p>
+
<p>You should usually close your {@link android.bluetooth.BluetoothServerSocket}
as soon as you are done listening for incoming connections. In this example, {@link
android.bluetooth.BluetoothServerSocket#close()} is called as soon
as the {@link android.bluetooth.BluetoothSocket} is acquired. You may also want
to provide a public method in your thread that can close the private {@link
android.bluetooth.BluetoothSocket} in the event that you need to stop listening on the
-server socket.</p>
-
-
-<h3 id="ConnectingAsAClient">Connecting as a client</h3>
-
+server socket.</p>
+
+
+<h3 id="ConnectingAsAClient">Connecting as a client</h3>
+
<p>In order to initiate a connection with a remote device (a device holding an
open
server socket), you must first obtain a {@link
@@ -619,11 +647,11 @@
section about <a
href="#FindingDevices">Finding Devices</a>.) You must then use the
{@link android.bluetooth.BluetoothDevice} to acquire a {@link
-android.bluetooth.BluetoothSocket} and initiate the connection.</p>
-
-<p>Here's the basic procedure:</p>
-
-<ol>
+android.bluetooth.BluetoothSocket} and initiate the connection.</p>
+
+<p>Here's the basic procedure:</p>
+
+<ol>
<li>Using the {@link android.bluetooth.BluetoothDevice}, get a {@link
android.bluetooth.BluetoothSocket} by calling {@link
android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(UUID)}.
@@ -634,9 +662,9 @@
android.bluetooth.BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,
UUID)}). Using the same UUID is simply a matter of hard-coding the UUID string
into your application and then referencing it from both the server and client
-code.</p>
-</li>
-
+code.</p>
+</li>
+
<li>Initiate the connection by calling {@link
android.bluetooth.BluetoothSocket#connect()}.
<p>Upon this call, the system will perform an SDP lookup on the remote device in
@@ -647,34 +675,34 @@
blocking call. If, for
any reason, the connection fails or the {@link
android.bluetooth.BluetoothSocket#connect()} method times out (after about
-12 seconds), then it will throw an exception.</p>
+12 seconds), then it will throw an exception.</p>
<p>Because {@link
android.bluetooth.BluetoothSocket#connect()} is a blocking call, this connection
procedure should always be performed in a thread separate from the main Activity
-thread.</p>
+thread.</p>
<p class="note">Note: You should always ensure that the device is not performing
device discovery when you call {@link
android.bluetooth.BluetoothSocket#connect()}. If discovery is in progress, then
the
-connection attempt will be significantly slowed and is more likely to fail.</p>
-</li>
-</ol>
-
-<h4>Example</h4>
-
+connection attempt will be significantly slowed and is more likely to fail.</p>
+</li>
+</ol>
+
+<h4>Example</h4>
+
<p>Here is a basic example of a thread that initiates a Bluetooth
-connection:</p>
-<pre>
+connection:</p>
+<pre>
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
-
+
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
-
+
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
@@ -682,11 +710,11 @@
} catch (IOException e) { }
mmSocket = tmp;
}
-
+
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
-
+
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
@@ -698,11 +726,11 @@
} catch (IOException closeException) { }
return;
}
-
+
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
-
+
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
@@ -710,42 +738,42 @@
} catch (IOException e) { }
}
}
-</pre>
-
+</pre>
+
<p>Notice that {@link android.bluetooth.BluetoothAdapter#cancelDiscovery()} is called
before the connection is made. You should always do this before connecting and it is safe
to call without actually checking whether it is running or not (but if you do want to
-check, call {@link android.bluetooth.BluetoothAdapter#isDiscovering()}).</p>
-
+check, call {@link android.bluetooth.BluetoothAdapter#isDiscovering()}).</p>
+
<p><code>manageConnectedSocket()</code> is a fictional method in the application
that will initiate the thread for transferring data, which is discussed in the section
-about <a href="#ManagingAConnection">Managing a Connection</a>.</p>
-
+about <a href="#ManagingAConnection">Managing a Connection</a>.</p>
+
<p>When you're done with your {@link android.bluetooth.BluetoothSocket}, always
call {@link android.bluetooth.BluetoothSocket#close()} to clean up.
Doing so will immediately close the connected socket and clean up all internal
-resources.</p>
-
-
-<h2 id="ManagingAConnection">Managing a Connection</h2>
-
+resources.</p>
+
+
+<h2 id="ManagingAConnection">Managing a Connection</h2>
+
<p>When you have successfully connected two (or more) devices, each one will
have a connected {@link android.bluetooth.BluetoothSocket}. This is where the fun
begins because you can share data between devices. Using the {@link
android.bluetooth.BluetoothSocket}, the general procedure to transfer arbitrary data is
-simple:</p>
-<ol>
+simple:</p>
+<ol>
<li>Get the {@link java.io.InputStream} and {@link java.io.OutputStream} that
handle transmissions through the socket, via {@link
android.bluetooth.BluetoothSocket#getInputStream()} and
-{@link android.bluetooth.BluetoothSocket#getOutputStream}, respectively.</li>
-
+{@link android.bluetooth.BluetoothSocket#getOutputStream}, respectively.</li>
+
<li>Read and write data to the streams with {@link
-java.io.InputStream#read(byte[])} and {@link java.io.OutputStream#write(byte[])}.</li>
-</ol>
-
-<p>That's it.</p>
-
+java.io.InputStream#read(byte[])} and {@link java.io.OutputStream#write(byte[])}.</li>
+</ol>
+
+<p>That's it.</p>
+
<p>There are, of course, implementation details to consider. First and foremost,
you should use a dedicated thread for all stream reading and writing. This is
important because both {@link java.io.InputStream#read(byte[])} and {@link
@@ -756,37 +784,37 @@
java.io.InputStream#read(byte[])} quickly enough and the intermediate buffers are full.
So, your main loop in the thread should be dedicated to reading from the {@link
java.io.InputStream}. A separate public method in the thread can be used to initiate
-writes to the {@link java.io.OutputStream}.</p>
-
-<h4>Example</h4>
-
-<p>Here's an example of how this might look:</p>
-<pre>
+writes to the {@link java.io.OutputStream}.</p>
+
+<h4>Example</h4>
+
+<p>Here's an example of how this might look:</p>
+<pre>
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
-
+
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
-
+
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
-
+
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
-
+
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
-
+
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
@@ -800,14 +828,14 @@
}
}
}
-
+
/* Call this from the main Activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
-
+
/* Call this from the main Activity to shutdown the connection */
public void cancel() {
try {
@@ -815,27 +843,124 @@
} catch (IOException e) { }
}
}
-</pre>
-
+</pre>
+
<p>The constructor acquires the necessary streams and once executed, the thread
will wait for data to come through the InputStream. When {@link
java.io.InputStream#read(byte[])} returns with
bytes from the stream, the data is sent to the main Activity using a member
Handler from the parent class. Then it goes back and waits for more bytes from
-the stream.</p>
-
+the stream.</p>
+
<p>Sending outgoing data is as simple as calling the thread's
<code>write()</code> method from the main Activity and passing in the bytes to
be sent. This method then simply calls {@link
-java.io.OutputStream#write(byte[])} to send the data to the remote device.</p>
-
+java.io.OutputStream#write(byte[])} to send the data to the remote device.</p>
+
<p>The thread's <code>cancel()</code> method is important so that the connection
can be
terminated at any time by closing the {@link android.bluetooth.BluetoothSocket}.
This should always be called when you're done using the Bluetooth
-connection.</p>
+connection.</p>
+
+<div class="special">
+<p>For a demonstration of using the Bluetooth APIs, see the <a
+href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat sample app</a>.</p>
+</div>
-<div class="special">
-<p>For a complete demonstration using the Bluetooth APIs, see the <a
-href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat sample app</a>.</p>
-</div>
+<h2 id="Profiles">Working with Profiles</h2>
+
+<p>Starting in Android 3.0, the Bluetooth API includes support for working with
+Bluetooth profiles. A <em>Bluetooth profile</em> is a wireless interface
+specification for Bluetooth-based communication between devices. An example
+is the Hands-Free profile. For a mobile phone to connect to a wireless headset,
+both devices must support the Hands-Free profile. </p>
+
+<p>You can implement the interface {@link android.bluetooth.BluetoothProfile} to write
+your own classes to support a particular Bluetooth profile. The Android
+Bluetooth API provides implementations for the following Bluetooth
+profiles:</p>
+<ul>
+
+ <li><strong>Headset</strong>. The Headset profile provides support for
+Bluetooth headsets to be used with mobile phones. Android provides the {@link
+android.bluetooth.BluetoothHeadset} class, which is a proxy for controlling the
+Bluetooth Headset Service via interprocess communication (<a
+href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#IPC">IPC</a
+>). This includes both Bluetooth Headset and Hands-Free (v1.5) profiles. The
+{@link android.bluetooth.BluetoothHeadset} class includes support for AT commands.
+For more discussion of this topic, see <a href="#AT-Commands">Vendor-specific AT commands</a></li>
+
+ <li><strong>A2DP</strong>. The Advanced Audio Distribution Profile (A2DP)
+profile defines how high quality audio can be streamed from one device to
+another over a Bluetooth connection. Android provides the {@link
+android.bluetooth.BluetoothA2dp} class, which is a proxy for controlling
+the Bluetooth A2DP Service via IPC.</li>
+
+</ul>
+
+<p>Here are the basic steps for working with a profile:</p>
+<ol>
+
+ <li>Get the default adapter, as described in <a href="{@docRoot}guide/topics/wireless/bluetooth.
+html#SettingUp">Setting Up Bluetooth</a>.</li>
+
+ <li>Use {@link
+android.bluetooth.BluetoothAdapter#getProfileProxy(android.content.Context,
+android.bluetooth.BluetoothProfile.ServiceListener, int) getProfileProxy()} to
+establish a connection to the profile proxy object associated with the profile.
+In the example below, the profile proxy object is an instance of {@link
+android.bluetooth.BluetoothHeadset}. </li>
+
+ <li>Set up a {@link android.bluetooth.BluetoothProfile.ServiceListener}. This
+listener notifies {@link android.bluetooth.BluetoothProfile} IPC clients when
+they have been connected to or disconnected from the service.</li>
+
+ <li>In {@link
+android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected(int,
+android.bluetooth.BluetoothProfile) onServiceConnected()}, get a handle
+to the profile proxy object.</li>
+
+ <li>Once you have the profile proxy object, you can use it to monitor the
+state of the connection and perform other operations that are relevant to that
+profile.</li>
+</ol>
+<p> For example, this code snippet shows how to connect to a {@link android.bluetooth.BluetoothHeadset} proxy object so that you can control the
+Headset profile:</p>
+
+<pre>BluetoothHeadset mBluetoothHeadset;
+
+// Get the default adapter
+BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+// Establish connection to the proxy.
+mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
+
+private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET) {
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ }
+ }
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET) {
+ mBluetoothHeadset = null;
+ }
+ }
+};
+
+// ... call functions on mBluetoothHeadset
+
+// Close proxy connection after use.
+mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);
+</pre>
+
+<h3 id="AT-Commands">Vendor-specific AT commands</h3>
+
+<p>Starting in Android 3.0, applications can register to receive system
+broadcasts of pre-defined vendor-specific AT commands sent by headsets (such as
+a Plantronics +XEVENT command). For example, an application could receive
+broadcasts that indicate a connected device's battery level and could notify the
+user or take other action as needed. Create a broadcast receiver for the {@link
+android.bluetooth.BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
+to handle vendor-specific AT commands for the headset.</p>
diff --git a/graphics/java/android/graphics/ParcelSurfaceTexture.java b/graphics/java/android/graphics/ParcelSurfaceTexture.java
index 5272cc6..cc8bd02 100644
--- a/graphics/java/android/graphics/ParcelSurfaceTexture.java
+++ b/graphics/java/android/graphics/ParcelSurfaceTexture.java
@@ -19,6 +19,7 @@
import android.graphics.SurfaceTexture;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
/**
*
@@ -34,6 +35,17 @@
private int mISurfaceTexture;
/**
+ * Create a new ParcelSurfaceTexture from a Surface
+ *
+ * @param surface The Surface to create a ParcelSurfaceTexture from.
+ *
+ * @return Returns a new ParcelSurfaceTexture for the given Surface.
+ */
+ public static ParcelSurfaceTexture fromSurface(Surface surface) {
+ return new ParcelSurfaceTexture(surface);
+ }
+
+ /**
* Create a new ParcelSurfaceTexture from a SurfaceTexture
*
* @param surfaceTexture The SurfaceTexture to transport.
@@ -75,8 +87,11 @@
private ParcelSurfaceTexture(Parcel in) {
nativeReadFromParcel(in);
}
+ private ParcelSurfaceTexture(Surface surface) {
+ nativeInitFromSurface(surface);
+ }
private ParcelSurfaceTexture(SurfaceTexture surfaceTexture) {
- nativeInit(surfaceTexture);
+ nativeInitFromSurfaceTexture(surfaceTexture);
}
@Override
@@ -88,7 +103,8 @@
}
}
- private native void nativeInit(SurfaceTexture surfaceTexture);
+ private native void nativeInitFromSurface(Surface surface);
+ private native void nativeInitFromSurfaceTexture(SurfaceTexture surfaceTexture);
private native void nativeFinalize();
private native void nativeWriteToParcel(Parcel dest, int flags);
private native void nativeReadFromParcel(Parcel in);
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 6c7341f..9e498d0 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -131,6 +131,10 @@
*/
public void updateTexImage() {
nativeUpdateTexImage();
+ if (nativeGetQueuedCount() > 0) {
+ Message m = mEventHandler.obtainMessage();
+ mEventHandler.sendMessage(m);
+ }
}
/**
@@ -215,6 +219,7 @@
private native long nativeGetTimestamp();
private native void nativeSetDefaultBufferSize(int width, int height);
private native void nativeUpdateTexImage();
+ private native int nativeGetQueuedCount();
/*
* We use a class initializer to allow the native code to cache some
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index 317f1e7..fb0b057 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -23,10 +23,15 @@
namespace android {
+class Surface;
+
extern sp<ANativeWindow> android_Surface_getNativeWindow(
JNIEnv* env, jobject clazz);
extern bool android_Surface_isInstanceOf(JNIEnv* env, jobject obj);
+/* Gets the underlying Surface from a Surface Java object. */
+extern sp<Surface> Surface_getSurface(JNIEnv* env, jobject thiz);
+
} // namespace android
#endif // _ANDROID_VIEW_SURFACE_H
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 8845dc9..dc2a845 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -40,6 +40,7 @@
class GraphicBuffer;
class GraphicBufferMapper;
class IOMX;
+class ISurfaceTexture;
class Rect;
class Surface;
class SurfaceComposerClient;
@@ -154,6 +155,7 @@
bool isValid();
uint32_t getFlags() const { return mFlags; }
uint32_t getIdentity() const { return mIdentity; }
+ sp<ISurfaceTexture> getSurfaceTexture();
// the lock/unlock APIs must be used from the same thread
status_t lock(SurfaceInfo* info, bool blocking = true);
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4d1d923..9185e1e 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -421,6 +421,10 @@
return NO_ERROR;
}
+sp<ISurfaceTexture> Surface::getSurfaceTexture() {
+ return mSurface != NULL ? mSurface->getSurfaceTexture() : NULL;
+}
+
sp<IBinder> Surface::asBinder() const {
return mSurface!=0 ? mSurface->asBinder() : 0;
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d62c031..3bf309b 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5564,10 +5564,10 @@
private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
int dw, int dh) {
- dm.unscaledWidthPixels = mPolicy.getNonDecorDisplayWidth(rotation, dw);
- dm.unscaledHeightPixels = mPolicy.getNonDecorDisplayHeight(rotation, dh);
+ dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(rotation, dw);
+ dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(rotation, dh);
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
- int size = (int)(((dm.unscaledWidthPixels / scale) / dm.density) + .5f);
+ int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
if (curSize == 0 || size < curSize) {
curSize = size;
}
@@ -5642,9 +5642,9 @@
// Override display width and height with what we are computing,
// to be sure they remain consistent.
- dm.widthPixels = dm.unscaledWidthPixels = mAppDisplayWidth
+ dm.widthPixels = dm.noncompatWidthPixels = mAppDisplayWidth
= mPolicy.getNonDecorDisplayWidth(mRotation, dw);
- dm.heightPixels = dm.unscaledHeightPixels = mAppDisplayHeight
+ dm.heightPixels = dm.noncompatHeightPixels = mAppDisplayHeight
= mPolicy.getNonDecorDisplayHeight(mRotation, dh);
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 457fa7a..76b8b65 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -30,8 +30,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.PhoneNumberOfflineGeocoder;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 65a64cd..894611b 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -30,9 +30,9 @@
Consequently this tool:
- parses the input JAR,
- modifies some of the classes directly using some bytecode manipulation,
-- filters some packages and removes some that we don't want to end in the output JAR,
+- filters some packages and removes those we don't want in the output JAR,
- injects some new classes,
-- and generates a modified JAR file that is suitable for the Android plugin
+- generates a modified JAR file that is suitable for the Android plugin
for Eclipse to perform rendering.
The ASM library is used to do the bytecode modification using its visitor pattern API.
@@ -63,7 +63,7 @@
To do that, the analyzer is created with a list of base classes to keep -- everything
that derives from these is kept. Currently the one such class is android.view.View:
-since we want to render layouts, anything that is sort of the view needs to be kept.
+since we want to render layouts, anything that is sort of a view needs to be kept.
The analyzer is also given a list of class names to keep in the output.
This is done using shell-like glob patterns that filter on the fully-qualified
@@ -90,6 +90,7 @@
- the classes to inject in the output JAR -- these classes are directly implemented
in layoutlib_create and will be used to interface with the renderer in Eclipse.
- specific methods to override (see method stubs details below).
+- specific methods for which to delegate calls.
- specific methods to remove based on their return type.
- specific classes to rename.
@@ -114,6 +115,9 @@
The code of the methods is then kept as-is, except for native methods which are
replaced by a stub. Methods that are to be overridden are also replaced by a stub.
+The transformed class is then fed through the DelegateClassAdapter to implement
+method delegates.
+
Finally fields are also visited and changed from protected/private to public.
@@ -131,8 +135,7 @@
method was native. We do not currently provide the parameters. The listener
can however specify the return value of the overridden method.
-An extension being worked on is to actually replace these listeners by
-direct calls to a delegate class, complete with parameters.
+This strategy is now obsolete and replaced by the method delegates.
- Strategies
@@ -160,6 +163,9 @@
by a call to a specific OveriddeMethod.invokeX(). The bridge then registers
a listener on the method signature and can provide an implementation.
+This strategy is now obsolete and replaced by the method delegates.
+See strategy 5 below.
+
3- Renaming classes
@@ -195,6 +201,24 @@
bridge will provide its own implementation.
+5- Method Delegates
+
+This strategy is used to override method implementations.
+Given a method SomeClass.MethodName(), 1 or 2 methods are generated:
+a- A copy of the original method named SomeClass.MethodName_Original().
+ The content is the original method as-is from the reader.
+ This step is omitted if the method is native, since it has no Java implementation.
+b- A brand new implementation of SomeClass.MethodName() which calls to a
+ non-existing static method named SomeClass_Delegate.MethodName().
+ The implementation of this 'delegate' method is done in layoutlib_brigde.
+
+The delegate method is a static method.
+If the original method is non-static, the delegate method receives the original 'this'
+as its first argument. If the original method is an inner non-static method, it also
+receives the inner 'this' as the second argument.
+
+
+
- References -
--------------
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5c60318..233f72ec 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -51,6 +51,8 @@
* Returns The list of methods to stub out. Each entry must be in the form
* "package.package.OuterClass$InnerClass#MethodName".
* The list can be empty but must not be null.
+ * <p/>
+ * This usage is deprecated. Please use method 'delegates' instead.
*/
public String[] getOverriddenMethods() {
return OVERRIDDEN_METHODS;
@@ -158,6 +160,7 @@
/**
* The list of methods to stub out. Each entry must be in the form
* "package.package.OuterClass$InnerClass#MethodName".
+ * This usage is deprecated. Please use method 'delegates' instead.
*/
private final static String[] OVERRIDDEN_METHODS = new String[] {
};
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index 9cba8a0..49ddf1d 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -31,6 +31,11 @@
*/
public class DelegateClassAdapter extends ClassAdapter {
+ /** Suffix added to original methods. */
+ private static final String ORIGINAL_SUFFIX = "_Original";
+ private static String CONSTRUCTOR = "<init>";
+ private static String CLASS_INIT = "<clinit>";
+
public final static String ALL_NATIVES = "<<all_natives>>";
private final String mClassName;
@@ -73,22 +78,55 @@
boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
mDelegateMethods.contains(name);
- if (useDelegate) {
- // remove native
- access = access & ~Opcodes.ACC_NATIVE;
+ if (!useDelegate) {
+ // Not creating a delegate for this method, pass it as-is from the reader
+ // to the writer.
+ return super.visitMethod(access, name, desc, signature, exceptions);
}
- MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
if (useDelegate) {
- DelegateMethodAdapter a = new DelegateMethodAdapter(mLog, mw, mClassName,
- name, desc, isStatic);
- if (isNative) {
- // A native has no code to visit, so we need to generate it directly.
- a.generateCode();
- } else {
- return a;
+ if (CONSTRUCTOR.equals(name) || CLASS_INIT.equals(name)) {
+ // We don't currently support generating delegates for constructors.
+ throw new UnsupportedOperationException(
+ String.format(
+ "Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", //$NON-NLS-1$
+ mClassName, name, desc));
}
}
- return mw;
+
+ if (isNative) {
+ // Remove native flag
+ access = access & ~Opcodes.ACC_NATIVE;
+ MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
+
+ DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
+ mLog, null /*mwOriginal*/, mwDelegate, mClassName, name, desc, isStatic);
+
+ // A native has no code to visit, so we need to generate it directly.
+ a.generateDelegateCode();
+
+ return mwDelegate;
+ }
+
+ // Given a non-native SomeClass.MethodName(), we want to generate 2 methods:
+ // - A copy of the original method named SomeClass.MethodName_Original().
+ // The content is the original method as-is from the reader.
+ // - A brand new implementation of SomeClass.MethodName() which calls to a
+ // non-existing method named SomeClass_Delegate.MethodName().
+ // The implementation of this 'delegate' method is done in layoutlib_brigde.
+
+ int accessDelegate = access;
+ // change access to public for the original one
+ access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+ access |= Opcodes.ACC_PUBLIC;
+
+ MethodVisitor mwOriginal = super.visitMethod(access, name + ORIGINAL_SUFFIX,
+ desc, signature, exceptions);
+ MethodVisitor mwDelegate = super.visitMethod(accessDelegate, name,
+ desc, signature, exceptions);
+
+ DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
+ mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
+ return a;
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
deleted file mode 100644
index 8d7f016..0000000
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.layoutlib.create;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-
-import java.util.ArrayList;
-
-/**
- * This method adapter rewrites a method by discarding the original code and generating
- * a call to a delegate. Original annotations are passed along unchanged.
- * <p/>
- * Calls are delegated to a class named <code><className>_Delegate</code> with
- * static methods matching the methods to be overridden here. The methods have the
- * same return type. The argument type list is the same except the "this" reference is
- * passed first for non-static methods.
- * <p/>
- * A new annotation is added.
- * <p/>
- * Note that native methods have, by definition, no code so there's nothing a visitor
- * can visit. That means the caller must call {@link #generateCode()} directly for
- * a native and use the visitor pattern for non-natives.
- * <p/>
- * Instances of this class are not re-usable. You need a new instance for each method.
- */
-class DelegateMethodAdapter implements MethodVisitor {
-
- /**
- * Suffix added to delegate classes.
- */
- public static final String DELEGATE_SUFFIX = "_Delegate";
-
- private static String CONSTRUCTOR = "<init>";
- private static String CLASS_INIT = "<clinit>";
-
- /** The parent method writer */
- private MethodVisitor mParentVisitor;
- /** Flag to output the first line number. */
- private boolean mOutputFirstLineNumber = true;
- /** The original method descriptor (return type + argument types.) */
- private String mDesc;
- /** True if the original method is static. */
- private final boolean mIsStatic;
- /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
- private final String mClassName;
- /** The method name. */
- private final String mMethodName;
- /** Logger object. */
- private final Log mLog;
- /** True if {@link #visitCode()} has been invoked. */
- private boolean mVisitCodeCalled;
-
- /**
- * Creates a new {@link DelegateMethodAdapter} that will transform this method
- * into a delegate call.
- * <p/>
- * See {@link DelegateMethodAdapter} for more details.
- *
- * @param log The logger object. Must not be null.
- * @param mv the method visitor to which this adapter must delegate calls.
- * @param className The internal class name of the class to visit,
- * e.g. <code>com/android/SomeClass$InnerClass</code>.
- * @param methodName The simple name of the method.
- * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
- * {@link Type#getArgumentTypes(String)})
- * @param isStatic True if the method is declared static.
- */
- public DelegateMethodAdapter(Log log,
- MethodVisitor mv,
- String className,
- String methodName,
- String desc,
- boolean isStatic) {
- mLog = log;
- mParentVisitor = mv;
- mClassName = className;
- mMethodName = methodName;
- mDesc = desc;
- mIsStatic = isStatic;
-
- if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
- // We're going to simplify by not supporting constructors.
- // The only trick with a constructor is to find the proper super constructor
- // and call it (and deciding if we should mirror the original method call to
- // a custom constructor or call a default one.)
- throw new UnsupportedOperationException(
- String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)",
- className, methodName, desc));
- }
- }
-
- /**
- * Generates the new code for the method.
- * <p/>
- * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
- * (since they have no code to visit).
- * <p/>
- * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
- * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
- * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
- * this method will be invoked from {@link MethodVisitor#visitEnd()}.
- */
- public void generateCode() {
- /*
- * The goal is to generate a call to a static delegate method.
- * If this method is non-static, the first parameter will be 'this'.
- * All the parameters must be passed and then the eventual return type returned.
- *
- * Example, let's say we have a method such as
- * public void method_1(int a, Object b, ArrayList<String> c) { ... }
- *
- * We'll want to create a body that calls a delegate method like this:
- * TheClass_Delegate.method_1(this, a, b, c);
- *
- * If the method is non-static and the class name is an inner class (e.g. has $ in its
- * last segment), we want to push the 'this' of the outer class first:
- * OuterClass_InnerClass_Delegate.method_1(
- * OuterClass.this,
- * OuterClass$InnerClass.this,
- * a, b, c);
- *
- * Only one level of inner class is supported right now, for simplicity and because
- * we don't need more.
- *
- * The generated class name is the current class name with "_Delegate" appended to it.
- * One thing to realize is that we don't care about generics -- since generic types
- * are erased at runtime, they have no influence on the method name being called.
- */
-
- // Add our annotation
- AnnotationVisitor aw = mParentVisitor.visitAnnotation(
- Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
- true); // visible at runtime
- aw.visitEnd();
-
- if (!mVisitCodeCalled) {
- // If this is a direct call to generateCode() as done by DelegateClassAdapter
- // for natives, visitCode() hasn't been called yet.
- mParentVisitor.visitCode();
- mVisitCodeCalled = true;
- }
-
- ArrayList<Type> paramTypes = new ArrayList<Type>();
- String delegateClassName = mClassName + DELEGATE_SUFFIX;
- boolean pushedArg0 = false;
- int maxStack = 0;
-
- // For an instance method (e.g. non-static), push the 'this' preceded
- // by the 'this' of any outer class, if any.
- if (!mIsStatic) {
- // Check if the last segment of the class name has inner an class.
- // Right now we only support one level of inner classes.
- int slash = mClassName.lastIndexOf('/');
- int dol = mClassName.lastIndexOf('$');
- if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
- String outerClass = mClassName.substring(0, dol);
- Type outerType = Type.getObjectType(outerClass);
-
- // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
- delegateClassName = delegateClassName.replace('$', '_');
-
- // The first-level inner class has a package-protected member called 'this$0'
- // that points to the outer class.
-
- // Push this.getField("this$0") on the call stack.
- mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
- mParentVisitor.visitFieldInsn(Opcodes.GETFIELD,
- mClassName, // class where the field is defined
- "this$0", // field name
- outerType.getDescriptor()); // type of the field
- maxStack++;
- paramTypes.add(outerType);
- }
-
- // Push "this" for the instance method, which is always ALOAD 0
- mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- maxStack++;
- pushedArg0 = true;
- paramTypes.add(Type.getObjectType(mClassName));
- }
-
- // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
- Type[] argTypes = Type.getArgumentTypes(mDesc);
- int maxLocals = pushedArg0 ? 1 : 0;
- for (Type t : argTypes) {
- int size = t.getSize();
- mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
- maxLocals += size;
- maxStack += size;
- paramTypes.add(t);
- }
-
- // Construct the descriptor of the delegate based on the parameters
- // we pushed on the call stack. The return type remains unchanged.
- String desc = Type.getMethodDescriptor(
- Type.getReturnType(mDesc),
- paramTypes.toArray(new Type[paramTypes.size()]));
-
- // Invoke the static delegate
- mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
- delegateClassName,
- mMethodName,
- desc);
-
- Type returnType = Type.getReturnType(mDesc);
- mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
-
- mParentVisitor.visitMaxs(maxStack, maxLocals);
- mParentVisitor.visitEnd();
-
- // For debugging now. Maybe we should collect these and store them in
- // a text file for helping create the delegates. We could also compare
- // the text file to a golden and break the build on unsupported changes
- // or regressions. Even better we could fancy-print something that looks
- // like the expected Java method declaration.
- mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
- }
-
- /* Pass down to visitor writer. In this implementation, either do nothing. */
- public void visitCode() {
- mVisitCodeCalled = true;
- mParentVisitor.visitCode();
- }
-
- /*
- * visitMaxs is called just before visitEnd if there was any code to rewrite.
- * Skip the original.
- */
- public void visitMaxs(int maxStack, int maxLocals) {
- }
-
- /**
- * End of visiting. Generate the messaging code.
- */
- public void visitEnd() {
- generateCode();
- }
-
- /* Writes all annotation from the original method. */
- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- return mParentVisitor.visitAnnotation(desc, visible);
- }
-
- /* Writes all annotation default values from the original method. */
- public AnnotationVisitor visitAnnotationDefault() {
- return mParentVisitor.visitAnnotationDefault();
- }
-
- public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
- boolean visible) {
- return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
- }
-
- /* Writes all attributes from the original method. */
- public void visitAttribute(Attribute attr) {
- mParentVisitor.visitAttribute(attr);
- }
-
- /*
- * Only writes the first line number present in the original code so that source
- * viewers can direct to the correct method, even if the content doesn't match.
- */
- public void visitLineNumber(int line, Label start) {
- if (mOutputFirstLineNumber) {
- mParentVisitor.visitLineNumber(line, start);
- mOutputFirstLineNumber = false;
- }
- }
-
- public void visitInsn(int opcode) {
- // Skip original code.
- }
-
- public void visitLabel(Label label) {
- // Skip original code.
- }
-
- public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
- // Skip original code.
- }
-
- public void visitMethodInsn(int opcode, String owner, String name, String desc) {
- // Skip original code.
- }
-
- public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- // Skip original code.
- }
-
- public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
- // Skip original code.
- }
-
- public void visitIincInsn(int var, int increment) {
- // Skip original code.
- }
-
- public void visitIntInsn(int opcode, int operand) {
- // Skip original code.
- }
-
- public void visitJumpInsn(int opcode, Label label) {
- // Skip original code.
- }
-
- public void visitLdcInsn(Object cst) {
- // Skip original code.
- }
-
- public void visitLocalVariable(String name, String desc, String signature,
- Label start, Label end, int index) {
- // Skip original code.
- }
-
- public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
- // Skip original code.
- }
-
- public void visitMultiANewArrayInsn(String desc, int dims) {
- // Skip original code.
- }
-
- public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
- // Skip original code.
- }
-
- public void visitTypeInsn(int opcode, String type) {
- // Skip original code.
- }
-
- public void visitVarInsn(int opcode, int var) {
- // Skip original code.
- }
-
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java
new file mode 100644
index 0000000..ac4ae6d
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java
@@ -0,0 +1,431 @@
+/*
+ * 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.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.ArrayList;
+
+/**
+ * This method adapter generates delegate methods.
+ * <p/>
+ * Given a method {@code SomeClass.MethodName()}, this generates 1 or 2 methods:
+ * <ul>
+ * <li> A copy of the original method named {@code SomeClass.MethodName_Original()}.
+ * The content is the original method as-is from the reader.
+ * This step is omitted if the method is native, since it has no Java implementation.
+ * <li> A brand new implementation of {@code SomeClass.MethodName()} which calls to a
+ * non-existing method named {@code SomeClass_Delegate.MethodName()}.
+ * The implementation of this 'delegate' method is done in layoutlib_brigde.
+ * </ul>
+ * A method visitor is generally constructed to generate a single method; however
+ * here we might want to generate one or two depending on the context. To achieve
+ * that, the visitor here generates the 'original' method and acts as a no-op if
+ * no such method exists (e.g. when the original is a native method).
+ * The delegate method is generated after the {@code visitEnd} of the original method
+ * or by having the class adapter <em>directly</em> call {@link #generateDelegateCode()}
+ * for native methods.
+ * <p/>
+ * When generating the 'delegate', the implementation generates a call to a class
+ * class named <code><className>_Delegate</code> with static methods matching
+ * the methods to be overridden here. The methods have the same return type.
+ * The argument type list is the same except the "this" reference is passed first
+ * for non-static methods.
+ * <p/>
+ * A new annotation is added to these 'delegate' methods so that we can easily find them
+ * for automated testing.
+ * <p/>
+ * This class isn't intended to be generic or reusable.
+ * It is called by {@link DelegateClassAdapter}, which takes care of properly initializing
+ * the two method writers for the original and the delegate class, as needed, with their
+ * expected names.
+ * <p/>
+ * The class adapter also takes care of calling {@link #generateDelegateCode()} directly for
+ * a native and use the visitor pattern for non-natives.
+ * Note that native methods have, by definition, no code so there's nothing a visitor
+ * can visit.
+ * <p/>
+ * Instances of this class are not re-usable.
+ * The class adapter creates a new instance for each method.
+ */
+class DelegateMethodAdapter2 implements MethodVisitor {
+
+ /** Suffix added to delegate classes. */
+ public static final String DELEGATE_SUFFIX = "_Delegate";
+
+ /** The parent method writer to copy of the original method.
+ * Null when dealing with a native original method. */
+ private MethodVisitor mOrgWriter;
+ /** The parent method writer to generate the delegating method. Never null. */
+ private MethodVisitor mDelWriter;
+ /** The original method descriptor (return type + argument types.) */
+ private String mDesc;
+ /** True if the original method is static. */
+ private final boolean mIsStatic;
+ /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+ private final String mClassName;
+ /** The method name. */
+ private final String mMethodName;
+ /** Logger object. */
+ private final Log mLog;
+
+ /** Array used to capture the first line number information from the original method
+ * and duplicate it in the delegate. */
+ private Object[] mDelegateLineNumber;
+
+ /**
+ * Creates a new {@link DelegateMethodAdapter2} that will transform this method
+ * into a delegate call.
+ * <p/>
+ * See {@link DelegateMethodAdapter2} for more details.
+ *
+ * @param log The logger object. Must not be null.
+ * @param mvOriginal The parent method writer to copy of the original method.
+ * Must be {@code null} when dealing with a native original method.
+ * @param mvDelegate The parent method writer to generate the delegating method.
+ * Must never be null.
+ * @param className The internal class name of the class to visit,
+ * e.g. <code>com/android/SomeClass$InnerClass</code>.
+ * @param methodName The simple name of the method.
+ * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
+ * {@link Type#getArgumentTypes(String)})
+ * @param isStatic True if the method is declared static.
+ */
+ public DelegateMethodAdapter2(Log log,
+ MethodVisitor mvOriginal,
+ MethodVisitor mvDelegate,
+ String className,
+ String methodName,
+ String desc,
+ boolean isStatic) {
+ mLog = log;
+ mOrgWriter = mvOriginal;
+ mDelWriter = mvDelegate;
+ mClassName = className;
+ mMethodName = methodName;
+ mDesc = desc;
+ mIsStatic = isStatic;
+ }
+
+ /**
+ * Generates the new code for the method.
+ * <p/>
+ * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
+ * (since they have no code to visit).
+ * <p/>
+ * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
+ * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern
+ * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
+ * this method will be invoked from {@link MethodVisitor#visitEnd()}.
+ */
+ public void generateDelegateCode() {
+ /*
+ * The goal is to generate a call to a static delegate method.
+ * If this method is non-static, the first parameter will be 'this'.
+ * All the parameters must be passed and then the eventual return type returned.
+ *
+ * Example, let's say we have a method such as
+ * public void myMethod(int a, Object b, ArrayList<String> c) { ... }
+ *
+ * We'll want to create a body that calls a delegate method like this:
+ * TheClass_Delegate.myMethod(this, a, b, c);
+ *
+ * If the method is non-static and the class name is an inner class (e.g. has $ in its
+ * last segment), we want to push the 'this' of the outer class first:
+ * OuterClass_InnerClass_Delegate.myMethod(
+ * OuterClass.this,
+ * OuterClass$InnerClass.this,
+ * a, b, c);
+ *
+ * Only one level of inner class is supported right now, for simplicity and because
+ * we don't need more.
+ *
+ * The generated class name is the current class name with "_Delegate" appended to it.
+ * One thing to realize is that we don't care about generics -- since generic types
+ * are erased at build time, they have no influence on the method name being called.
+ */
+
+ // Add our annotation
+ AnnotationVisitor aw = mDelWriter.visitAnnotation(
+ Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+ true); // visible at runtime
+ if (aw != null) {
+ aw.visitEnd();
+ }
+
+ mDelWriter.visitCode();
+
+ if (mDelegateLineNumber != null) {
+ Object[] p = mDelegateLineNumber;
+ mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
+ }
+
+ ArrayList<Type> paramTypes = new ArrayList<Type>();
+ String delegateClassName = mClassName + DELEGATE_SUFFIX;
+ boolean pushedArg0 = false;
+ int maxStack = 0;
+
+ // For an instance method (e.g. non-static), push the 'this' preceded
+ // by the 'this' of any outer class, if any.
+ if (!mIsStatic) {
+ // Check if the last segment of the class name has inner an class.
+ // Right now we only support one level of inner classes.
+ int slash = mClassName.lastIndexOf('/');
+ int dol = mClassName.lastIndexOf('$');
+ if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
+ String outerClass = mClassName.substring(0, dol);
+ Type outerType = Type.getObjectType(outerClass);
+
+ // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
+ delegateClassName = delegateClassName.replace('$', '_');
+
+ // The first-level inner class has a package-protected member called 'this$0'
+ // that points to the outer class.
+
+ // Push this.getField("this$0") on the call stack.
+ mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
+ mDelWriter.visitFieldInsn(Opcodes.GETFIELD,
+ mClassName, // class where the field is defined
+ "this$0", // field name
+ outerType.getDescriptor()); // type of the field
+ maxStack++;
+ paramTypes.add(outerType);
+ }
+
+ // Push "this" for the instance method, which is always ALOAD 0
+ mDelWriter.visitVarInsn(Opcodes.ALOAD, 0);
+ maxStack++;
+ pushedArg0 = true;
+ paramTypes.add(Type.getObjectType(mClassName));
+ }
+
+ // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
+ Type[] argTypes = Type.getArgumentTypes(mDesc);
+ int maxLocals = pushedArg0 ? 1 : 0;
+ for (Type t : argTypes) {
+ int size = t.getSize();
+ mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
+ maxLocals += size;
+ maxStack += size;
+ paramTypes.add(t);
+ }
+
+ // Construct the descriptor of the delegate based on the parameters
+ // we pushed on the call stack. The return type remains unchanged.
+ String desc = Type.getMethodDescriptor(
+ Type.getReturnType(mDesc),
+ paramTypes.toArray(new Type[paramTypes.size()]));
+
+ // Invoke the static delegate
+ mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC,
+ delegateClassName,
+ mMethodName,
+ desc);
+
+ Type returnType = Type.getReturnType(mDesc);
+ mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+ mDelWriter.visitMaxs(maxStack, maxLocals);
+ mDelWriter.visitEnd();
+
+ // For debugging now. Maybe we should collect these and store them in
+ // a text file for helping create the delegates. We could also compare
+ // the text file to a golden and break the build on unsupported changes
+ // or regressions. Even better we could fancy-print something that looks
+ // like the expected Java method declaration.
+ mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
+ }
+
+ /* Pass down to visitor writer. In this implementation, either do nothing. */
+ public void visitCode() {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitCode();
+ }
+ }
+
+ /*
+ * visitMaxs is called just before visitEnd if there was any code to rewrite.
+ */
+ public void visitMaxs(int maxStack, int maxLocals) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitMaxs(maxStack, maxLocals);
+ }
+ }
+
+ /** End of visiting. Generate the delegating code. */
+ public void visitEnd() {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitEnd();
+ }
+ generateDelegateCode();
+ }
+
+ /* Writes all annotation from the original method. */
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (mOrgWriter != null) {
+ return mOrgWriter.visitAnnotation(desc, visible);
+ } else {
+ return null;
+ }
+ }
+
+ /* Writes all annotation default values from the original method. */
+ public AnnotationVisitor visitAnnotationDefault() {
+ if (mOrgWriter != null) {
+ return mOrgWriter.visitAnnotationDefault();
+ } else {
+ return null;
+ }
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+ boolean visible) {
+ if (mOrgWriter != null) {
+ return mOrgWriter.visitParameterAnnotation(parameter, desc, visible);
+ } else {
+ return null;
+ }
+ }
+
+ /* Writes all attributes from the original method. */
+ public void visitAttribute(Attribute attr) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitAttribute(attr);
+ }
+ }
+
+ /*
+ * Only writes the first line number present in the original code so that source
+ * viewers can direct to the correct method, even if the content doesn't match.
+ */
+ public void visitLineNumber(int line, Label start) {
+ // Capture the first line values for the new delegate method
+ if (mDelegateLineNumber == null) {
+ mDelegateLineNumber = new Object[] { line, start };
+ }
+ if (mOrgWriter != null) {
+ mOrgWriter.visitLineNumber(line, start);
+ }
+ }
+
+ public void visitInsn(int opcode) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitInsn(opcode);
+ }
+ }
+
+ public void visitLabel(Label label) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitLabel(label);
+ }
+ }
+
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitTryCatchBlock(start, end, handler, type);
+ }
+ }
+
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitMethodInsn(opcode, owner, name, desc);
+ }
+ }
+
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitFieldInsn(opcode, owner, name, desc);
+ }
+ }
+
+ public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitFrame(type, nLocal, local, nStack, stack);
+ }
+ }
+
+ public void visitIincInsn(int var, int increment) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitIincInsn(var, increment);
+ }
+ }
+
+ public void visitIntInsn(int opcode, int operand) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitIntInsn(opcode, operand);
+ }
+ }
+
+ public void visitJumpInsn(int opcode, Label label) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitJumpInsn(opcode, label);
+ }
+ }
+
+ public void visitLdcInsn(Object cst) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitLdcInsn(cst);
+ }
+ }
+
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+ }
+
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+ }
+
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitMultiANewArrayInsn(desc, dims);
+ }
+ }
+
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+ }
+
+ public void visitTypeInsn(int opcode, String type) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitTypeInsn(opcode, type);
+ }
+ }
+
+ public void visitVarInsn(int opcode, int var) {
+ if (mOrgWriter != null) {
+ mOrgWriter.visitVarInsn(opcode, var);
+ }
+ }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
index 9a57a4a..d70d028 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -31,7 +31,7 @@
private static String CONSTRUCTOR = "<init>";
private static String CLASS_INIT = "<clinit>";
-
+
/** The parent method writer */
private MethodVisitor mParentVisitor;
/** The method return type. Can be null. */
@@ -40,7 +40,7 @@
private String mInvokeSignature;
/** Flag to output the first line number. */
private boolean mOutputFirstLineNumber = true;
- /** Flag that is true when implementing a constructor, to accept all original
+ /** Flag that is true when implementing a constructor, to accept all original
* code calling the original super constructor. */
private boolean mIsInitMethod = false;
@@ -55,12 +55,12 @@
mInvokeSignature = invokeSignature;
mIsStatic = isStatic;
mIsNative = isNative;
-
+
if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
mIsInitMethod = true;
}
}
-
+
private void generateInvoke() {
/* Generates the code:
* OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
@@ -188,7 +188,7 @@
}
mParentVisitor.visitMaxs(maxStack, maxLocals);
}
-
+
/**
* End of visiting.
* For non-constructor, generate the messaging code and the return statement
@@ -250,6 +250,7 @@
generatePop();
generateInvoke();
mMessageGenerated = true;
+ //$FALL-THROUGH$
default:
mParentVisitor.visitInsn(opcode);
}
@@ -346,5 +347,5 @@
mParentVisitor.visitVarInsn(opcode, var);
}
}
-
+
}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index e8b3ea8..6e120ce 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -130,7 +130,7 @@
}
/**
- * {@link DelegateMethodAdapter} does not support overriding constructors yet,
+ * {@link DelegateMethodAdapter2} does not support overriding constructors yet,
* so this should fail with an {@link UnsupportedOperationException}.
*
* Although not tested here, the message of the exception should contain the
@@ -202,6 +202,7 @@
// We'll delegate the "get" method of both the inner and outer class.
HashSet<String> delegateMethods = new HashSet<String>();
delegateMethods.add("get");
+ delegateMethods.add("privateMethod");
// Generate the delegate for the outer class.
ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
@@ -234,6 +235,25 @@
// The original Outer.get returns 1+10+20,
// but the delegate makes it return 4+10+20
assertEquals(4+10+20, callGet(o2, 10, 20));
+ assertEquals(1+10+20, callGet_Original(o2, 10, 20));
+
+ // The original Outer has a private method that is
+ // delegated. We should be able to call both the delegate
+ // and the original (which is now public).
+ assertEquals("outerPrivateMethod",
+ callMethod(o2, "privateMethod_Original", false /*makePublic*/));
+
+ // The original method is private, so by default we can't access it
+ boolean gotIllegalAccessException = false;
+ try {
+ callMethod(o2, "privateMethod", false /*makePublic*/);
+ } catch(IllegalAccessException e) {
+ gotIllegalAccessException = true;
+ }
+ assertTrue(gotIllegalAccessException);
+ // Try again, but now making it accessible
+ assertEquals("outerPrivate_Delegate",
+ callMethod(o2, "privateMethod", true /*makePublic*/));
// Check the inner class. Since it's not a static inner class, we need
// to use the hidden constructor that takes the outer class as first parameter.
@@ -246,6 +266,7 @@
// The original Inner.get returns 3+10+20,
// but the delegate makes it return 6+10+20
assertEquals(6+10+20, callGet(i2, 10, 20));
+ assertEquals(3+10+20, callGet_Original(i2, 10, 20));
}
};
cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
@@ -319,7 +340,7 @@
}
/**
- * Accesses {@link OuterClass#get()} or {@link InnerClass#get() }via reflection.
+ * Accesses {@link OuterClass#get} or {@link InnerClass#get}via reflection.
*/
public int callGet(Object instance, int a, long b) throws Exception {
Method m = instance.getClass().getMethod("get",
@@ -330,6 +351,39 @@
}
/**
+ * Accesses the "_Original" methods for {@link OuterClass#get}
+ * or {@link InnerClass#get}via reflection.
+ */
+ public int callGet_Original(Object instance, int a, long b) throws Exception {
+ Method m = instance.getClass().getMethod("get_Original",
+ new Class<?>[] { int.class, long.class } );
+
+ Object result = m.invoke(instance, new Object[] { a, b });
+ return ((Integer) result).intValue();
+ }
+
+ /**
+ * Accesses the any declared method that takes no parameter via reflection.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T callMethod(Object instance, String methodName, boolean makePublic) throws Exception {
+ Method m = instance.getClass().getDeclaredMethod(methodName, (Class<?>[])null);
+
+ boolean wasAccessible = m.isAccessible();
+ if (makePublic && !wasAccessible) {
+ m.setAccessible(true);
+ }
+
+ Object result = m.invoke(instance, (Object[])null);
+
+ if (makePublic && !wasAccessible) {
+ m.setAccessible(false);
+ }
+
+ return (T) result;
+ }
+
+ /**
* Accesses {@link ClassWithNative#add(int, int)} via reflection.
*/
public int callAdd(Object instance, int a, int b) throws Exception {
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
index 9dc2f69..f083e76 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -39,10 +39,15 @@
public InnerClass() {
}
- // Inner.get returns 1+2=3 + a + b
+ // Inner.get returns 2 + 1 + a + b
public int get(int a, long b) {
return 2 + mOuterValue + a + (int) b;
}
}
+
+ @SuppressWarnings("unused")
+ private String privateMethod() {
+ return "outerPrivateMethod";
+ }
}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
index 3252d87..774be8e 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
@@ -26,5 +26,9 @@
public static int get(OuterClass instance, int a, long b) {
return 4 + a + (int) b;
}
+
+ public static String privateMethod(OuterClass instance) {
+ return "outerPrivate_Delegate";
+ }
}