From 61aac7b377ac4878a5778457cad239fe3a50604a Mon Sep 17 00:00:00 2001 From: Tammo Spalink Date: Mon, 5 Oct 2009 14:20:06 +0800 Subject: [PATCH 01/10] Add PERIOD char to CDMA address sugar list. Addresses issue: http://buganizer/issue?id=2164455 --- .../internal/telephony/cdma/sms/CdmaSmsAddress.java | 2 +- .../src/com/android/unit_tests/CdmaSmsTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java index d9cc2c62d1505..f49b5029189bb 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -135,7 +135,7 @@ public class CdmaSmsAddress extends SmsAddress { }; private static final char[] numericCharsSugar = { - '(', ')', ' ', '-', '+' + '(', ')', ' ', '-', '+', '.' }; private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray ( diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index 4a77e1968ca08..85840a89358d8 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -61,6 +61,16 @@ public class CdmaSmsTest extends AndroidTestCase { for (int i = 0; i < data2.length; i++) { assertEquals(addr.origBytes[i], data2[i]); } + addr = CdmaSmsAddress.parse("650.253.1000"); + assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 10); + assertEquals(addr.origBytes.length, 10); + byte[] data5 = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10}; + for (int i = 0; i < data2.length; i++) { + assertEquals(addr.origBytes[i], data5[i]); + } addr = CdmaSmsAddress.parse("(+886) 917 222 555"); assertEquals(addr.ton, CdmaSmsAddress.TON_INTERNATIONAL_OR_IP); assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); From ac7391de5f32cf051678886f0d393fe0431dc6d1 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Mon, 5 Oct 2009 14:12:04 -0700 Subject: [PATCH 02/10] Refactor class/method names used by layoutlib_create. Information about classes and methods that are replaced, renamed, removed into CreateInfo, so that it can be used by the tests. Change-Id: I8874a40503249416621aa90b2ca02dcaf30ecd57 --- .../tools/layoutlib/create/AsmGenerator.java | 1 + .../tools/layoutlib/create/CreateInfo.java | 81 +++++++++++++++++++ .../android/tools/layoutlib/create/Main.java | 42 +--------- 3 files changed, 86 insertions(+), 38 deletions(-) create mode 100644 tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index 1adcc17270218..7b55ed3e8aff3 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -68,6 +68,7 @@ public class AsmGenerator { * * @param log Output logger. * @param osDestJar The path of the destination JAR to create. + * @param injectClasses The list of class from layoutlib_create to inject in layoutlib. * @param stubMethods The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN 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 new file mode 100644 index 0000000000000..5a13b0be2bb3a --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -0,0 +1,81 @@ +/* + * 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; + +public class CreateInfo { + /** + * The list of class from layoutlib_create to inject in layoutlib. + */ + public final static Class[] INJECTED_CLASSES = new Class[] { + OverrideMethod.class, + MethodListener.class, + MethodAdapter.class, + CreateInfo.class + }; + + /** + * The list of methods to stub out. Each entry must be in the form + * "package.package.OuterClass$InnerClass#MethodName". + */ + public final static String[] OVERRIDDEN_METHODS = new String[] { + "android.view.View#isInEditMode", + "android.content.res.Resources$Theme#obtainStyledAttributes", + }; + + /** + * The list of classes to rename, must be an even list: the binary FQCN + * of class to replace followed by the new FQCN. + */ + public final static String[] RENAMED_CLASSES = + new String[] { + "android.graphics.Bitmap", "android.graphics._Original_Bitmap", + "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", + "android.graphics.Canvas", "android.graphics._Original_Canvas", + "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", + "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", + "android.graphics.Matrix", "android.graphics._Original_Matrix", + "android.graphics.Paint", "android.graphics._Original_Paint", + "android.graphics.Path", "android.graphics._Original_Path", + "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", + "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", + "android.graphics.Shader", "android.graphics._Original_Shader", + "android.graphics.SweepGradient", "android.graphics._Original_SweepGradient", + "android.graphics.Typeface", "android.graphics._Original_Typeface", + "android.os.ServiceManager", "android.os._Original_ServiceManager", + "android.util.FloatMath", "android.util._Original_FloatMath", + "android.view.SurfaceView", "android.view._Original_SurfaceView", + "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", + }; + + /** + * List of classes for which the methods returning them should be deleted. + * The array contains a list of null terminated section starting with the name of the class + * to rename in which the methods are deleted, followed by a list of return types identifying + * the methods to delete. + */ + public final static String[] REMOVED_METHODS = + new String[] { + "android.graphics.Paint", // class to delete methods from + "android.graphics.Paint$Align", // list of type identifying methods to delete + "android.graphics.Paint$Style", + "android.graphics.Paint$Join", + "android.graphics.Paint$Cap", + "android.graphics.Paint$FontMetrics", + "android.graphics.Paint$FontMetricsInt", + null }; // separator, for next class/methods list. +} + diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 47184f10efdc6..303f0974343c2 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -43,44 +43,10 @@ public class Main { try { AsmGenerator agen = new AsmGenerator(log, osDestJar[0], - new Class[] { // classes to inject in the final JAR - OverrideMethod.class, - MethodListener.class, - MethodAdapter.class - }, - new String[] { // methods to force override - "android.view.View#isInEditMode", - "android.content.res.Resources$Theme#obtainStyledAttributes", - }, - new String[] { // classes to rename (so that we can replace them in layoutlib) - // original-platform-class-name ======> renamed-class-name - "android.graphics.Bitmap", "android.graphics._Original_Bitmap", - "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", - "android.graphics.Canvas", "android.graphics._Original_Canvas", - "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", - "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", - "android.graphics.Matrix", "android.graphics._Original_Matrix", - "android.graphics.Paint", "android.graphics._Original_Paint", - "android.graphics.Path", "android.graphics._Original_Path", - "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", - "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", - "android.graphics.Shader", "android.graphics._Original_Shader", - "android.graphics.SweepGradient", "android.graphics._Original_SweepGradient", - "android.graphics.Typeface", "android.graphics._Original_Typeface", - "android.os.ServiceManager", "android.os._Original_ServiceManager", - "android.util.FloatMath", "android.util._Original_FloatMath", - "android.view.SurfaceView", "android.view._Original_SurfaceView", - "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", - }, - new String[] { // methods deleted from their return type. - "android.graphics.Paint", // class to delete method from - "android.graphics.Paint$Align", // list of type identifying methods to delete - "android.graphics.Paint$Style", - "android.graphics.Paint$Join", - "android.graphics.Paint$Cap", - "android.graphics.Paint$FontMetrics", - "android.graphics.Paint$FontMetricsInt", - null } + CreateInfo.INJECTED_CLASSES, + CreateInfo.OVERRIDDEN_METHODS, + CreateInfo.RENAMED_CLASSES, + CreateInfo.REMOVED_METHODS ); AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen, From 89c40f50347f8ec3128611a7d1c5e4e0eeb01fc2 Mon Sep 17 00:00:00 2001 From: Fred Quintana Date: Mon, 5 Oct 2009 14:21:53 -0700 Subject: [PATCH 03/10] fix a javadoc build breakage --- core/java/android/accounts/AccountAuthenticatorResponse.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java index 3488c5e1dfba3..7c09fbffb86ad 100644 --- a/core/java/android/accounts/AccountAuthenticatorResponse.java +++ b/core/java/android/accounts/AccountAuthenticatorResponse.java @@ -22,8 +22,7 @@ import android.os.Parcel; import android.os.RemoteException; /** - * Object that wraps calls to an {@link IAccountAuthenticatorResponse} object. - * TODO: this interface is still in flux + * Object used to communicate responses back to the AccountManager */ public class AccountAuthenticatorResponse implements Parcelable { private IAccountAuthenticatorResponse mAccountAuthenticatorResponse; From 4b057801d5a5a859c2f9a68230744a6b0939ae01 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Mon, 5 Oct 2009 14:48:04 -0700 Subject: [PATCH 04/10] Make sure AsyncTask sens a null result to onPostExecute() when cancelled. This is in accordance to the documentation. This bug was approved by hackbod. The change is safe and is a single line of code. Change-Id: I9b771df3ae2aa4f496d15e6c43b677f3245539ac --- core/java/android/os/AsyncTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index abfb27412de0c..7d2c698895817 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -413,6 +413,7 @@ public abstract class AsyncTask { } private void finish(Result result) { + if (isCancelled()) result = null; onPostExecute(result); mStatus = Status.FINISHED; } From 8c739671d262c74c5743bf249180b1ba58bef4da Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Mon, 5 Oct 2009 15:28:33 -0700 Subject: [PATCH 05/10] Fix the layoutlib test to run from adt-tests. Change-Id: I34f513429593dce9f781ed8a08c4664a5d6f9aa8 --- .../android/layoutlib/bridge/BridgeTest.java | 56 ++++++++++++++-- .../bridge/BridgeXmlBlockParserTest.java | 2 +- .../layoutlib/bridge/NinePatchTest.java | 2 +- .../layoutlib/bridge/StyleResourceValue.java | 60 ------------------ .../android/layoutlib/bridge}/button.9.png | Bin .../android/layoutlib/bridge}/layout1.xml | 0 6 files changed, 53 insertions(+), 67 deletions(-) delete mode 100644 tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java rename tools/layoutlib/bridge/tests/{data => com/android/layoutlib/bridge}/button.9.png (100%) rename tools/layoutlib/bridge/tests/{data => com/android/layoutlib/bridge}/layout1.xml (100%) diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java index e424f1d801851..c66ae37e70bc2 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java @@ -73,6 +73,48 @@ public class BridgeTest extends TestCase { } } + /** + * Mock implementation of {@link IStyleResourceValue}. + */ + private static class StyleResourceValueMock extends ResourceValue + implements IStyleResourceValue { + + private String mParentStyle = null; + private HashMap mItems = new HashMap(); + + StyleResourceValueMock(String name) { + super(name); + } + + StyleResourceValueMock(String name, String parentStyle) { + super(name); + mParentStyle = parentStyle; + } + + public String getParentStyle() { + return mParentStyle; + } + + public IResourceValue findItem(String name) { + return mItems.get(name); + } + + public void addItem(IResourceValue value) { + mItems.put(value.getName(), value); + } + + @Override + public void replaceWith(ResourceValue value) { + super.replaceWith(value); + + if (value instanceof StyleResourceValueMock) { + mItems.clear(); + mItems.putAll(((StyleResourceValueMock)value).mItems); + } + } + } + + public void testComputeLayout() throws Exception { TestParser parser = new TestParser(); @@ -88,8 +130,10 @@ public class BridgeTest extends TestCase { // FIXME need a dummy font for the tests! ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); + screenWidth, screenHeight, false /* full render */, + 160, 160f, 160f, + "Theme", false /* is project theme */, + projectResources, frameworkResources, null, null); display(result.getRootView(), ""); } @@ -191,7 +235,7 @@ public class BridgeTest extends TestCase { * a style item value. If the number of string in the array is not even, an exception is thrown. */ private IStyleResourceValue createStyle(String styleName, String... items) { - StyleResourceValue value = new StyleResourceValue(styleName); + StyleResourceValueMock value = new StyleResourceValueMock(styleName); if (items.length % 3 == 0) { for (int i = 0 ; i < items.length;) { @@ -220,8 +264,10 @@ public class BridgeTest extends TestCase { // FIXME need a dummy font for the tests! ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); + screenWidth, screenHeight, false /* full render */, + 160, 160f, 160f, + "Theme", false /* is project theme */, + projectResources, frameworkResources, null, null); display(result.getRootView(), ""); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java index cac1f95df2c2b..ef7442c4a5ca6 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java @@ -41,7 +41,7 @@ public class BridgeXmlBlockParserTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - URL url = this.getClass().getClassLoader().getResource("data/layout1.xml"); + URL url = this.getClass().getClassLoader().getResource("layout1.xml"); mXmlPath = url.getFile(); mDoc = getXmlDocument(mXmlPath); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java index 67ec5e16241f2..e667472c75db8 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java @@ -12,7 +12,7 @@ public class NinePatchTest extends TestCase { @Override protected void setUp() throws Exception { - URL url = this.getClass().getClassLoader().getResource("data/button.9.png"); + URL url = this.getClass().getClassLoader().getResource("button.9.png"); mPatch = NinePatch.load(url, false /* convert */); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java deleted file mode 100644 index 84bdc2f03c92d..0000000000000 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java +++ /dev/null @@ -1,60 +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.layoutlib.bridge; - -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; - -import java.util.HashMap; - -class StyleResourceValue extends ResourceValue implements IStyleResourceValue { - - private String mParentStyle = null; - private HashMap mItems = new HashMap(); - - StyleResourceValue(String name) { - super(name); - } - - StyleResourceValue(String name, String parentStyle) { - super(name); - mParentStyle = parentStyle; - } - - public String getParentStyle() { - return mParentStyle; - } - - public IResourceValue findItem(String name) { - return mItems.get(name); - } - - public void addItem(IResourceValue value) { - mItems.put(value.getName(), value); - } - - @Override - public void replaceWith(ResourceValue value) { - super.replaceWith(value); - - if (value instanceof StyleResourceValue) { - mItems.clear(); - mItems.putAll(((StyleResourceValue)value).mItems); - } - } - -} diff --git a/tools/layoutlib/bridge/tests/data/button.9.png b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/button.9.png similarity index 100% rename from tools/layoutlib/bridge/tests/data/button.9.png rename to tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/button.9.png diff --git a/tools/layoutlib/bridge/tests/data/layout1.xml b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/layout1.xml similarity index 100% rename from tools/layoutlib/bridge/tests/data/layout1.xml rename to tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/layout1.xml From 68d881cf2d2b252f6f795cd64d43e316a1d736e5 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 5 Oct 2009 13:58:17 -0700 Subject: [PATCH 06/10] Fix issue #2166755: BroadcastReceiver trying to return result during a non-ordered broadcast Tell the broadcast receiver whether it is getting an initial sticky value, so it will be quiet about attempts to do ordered broadcast stuff. Note that the original bug being reported was not actually a crash, just an error log. So all we are doing here is making the log quieter. Change-Id: Iaf1b718d82093ec1197142410a64feff47eb3859 --- api/current.xml | 22 +++++++++++++ cmds/am/src/com/android/commands/am/Am.java | 3 +- core/java/android/app/ActivityThread.java | 16 ++++++---- .../android/app/ApplicationThreadNative.java | 6 ++-- core/java/android/app/IApplicationThread.java | 2 +- core/java/android/app/PendingIntent.java | 2 +- .../android/content/BroadcastReceiver.java | 32 ++++++++++++++++++- .../java/android/content/IIntentReceiver.aidl | 2 +- core/java/android/content/IntentSender.java | 2 +- .../server/am/ActivityManagerService.java | 21 ++++++------ .../android/server/am/BroadcastRecord.java | 11 ++++--- .../server/am/PendingIntentRecord.java | 2 +- 12 files changed, 92 insertions(+), 29 deletions(-) diff --git a/api/current.xml b/api/current.xml index e764fa5aa6076..9b0ea3aab168a 100644 --- a/api/current.xml +++ b/api/current.xml @@ -27787,6 +27787,28 @@ + + + + + #include "dumpstate.h" static char* const gzip_args[] = { "gzip", "-6", 0 }; @@ -182,6 +186,8 @@ int main(int argc, char *argv[]) { pid_t pid; gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; + LOGI("begin\n"); + /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); protect_from_oom_killer(); @@ -332,6 +338,8 @@ int main(int argc, char *argv[]) { /* so gzip will terminate */ close(STDOUT_FILENO); + LOGI("done\n"); + return 0; } From 24bb9b8af4ff691538fe9e517e8156016b0da6cd Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 2 Oct 2009 20:34:18 -0700 Subject: [PATCH 09/10] Provide an API for apps to use a dynamic RFCOMM channel and SDP record. Hide listenUsingRfcommOn(int channel) Add listenUsingRfcomm(String name, ParcelUuid uuid) The new API automatically finds a free RFCOMM channel and registers an SDP record with the given uuid and name. The SDP record is automatically removed when the socket is closed, or if the application dies. Apps are prevented from registering SDP records with the uuid of system Bluetooth profiles, such as A2DP, HFP and OPP. Apps are prevented from removing SDP records that they did not create. This is tracked by pid. TODO: Provide an API for the connecting app to look up an SDP record. Bug: 2158900 DrNo: eastham Joke: "What did the dog say to the tree? bark." Change-Id: Ia92f51c34615a7270a403255ad2b8faa98c4a3f5 --- api/current.xml | 6 +- .../android/bluetooth/BluetoothAdapter.java | 160 ++++++++++++++++-- .../bluetooth/BluetoothServerSocket.java | 14 ++ .../android/bluetooth/BluetoothSocket.java | 28 ++- .../java/android/bluetooth/BluetoothUuid.java | 4 + core/java/android/bluetooth/IBluetooth.aidl | 3 + .../java/android/server/BluetoothService.java | 128 +++++++++++--- .../jni/android_bluetooth_BluetoothSocket.cpp | 25 +-- core/jni/android_server_BluetoothService.cpp | 44 +++++ 9 files changed, 356 insertions(+), 56 deletions(-) diff --git a/api/current.xml b/api/current.xml index 9b0ea3aab168a..df69ede521ed0 100644 --- a/api/current.xml +++ b/api/current.xml @@ -25618,7 +25618,7 @@ visibility="public" > - - + + + diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e3ec2cc4cb24d..c6a0619cf8ec8 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -18,14 +18,19 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; import java.io.IOException; import java.util.Collections; -import java.util.Set; import java.util.HashSet; +import java.util.LinkedList; +import java.util.Random; +import java.util.Set; /** * Represents the local Bluetooth adapter. @@ -40,6 +45,7 @@ import java.util.HashSet; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + private static final boolean DBG = true; //STOPSHIP: Remove excess logging /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -557,33 +563,142 @@ public final class BluetoothAdapter { return null; } + /** + * Randomly picks RFCOMM channels until none are left. + * Avoids reserved channels. + */ + private static class RfcommChannelPicker { + private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { + 10, // HFAG + 11, // HSAG + 12, // OPUSH + 19, // PBAP + }; + private static LinkedList sChannels; // master list of non-reserved channels + private static Random sRandom; + + private final LinkedList mChannels; // local list of channels left to try + + public RfcommChannelPicker() { + synchronized (RfcommChannelPicker.class) { + if (sChannels == null) { + // lazy initialization of non-reserved rfcomm channels + sChannels = new LinkedList(); + for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) { + sChannels.addLast(new Integer(i)); + } + for (int reserved : RESERVED_RFCOMM_CHANNELS) { + sChannels.remove(new Integer(reserved)); + } + sRandom = new Random(); + } + mChannels = (LinkedList)sChannels.clone(); + } + } + /* Returns next random channel, or -1 if we're out */ + public int nextChannel() { + if (mChannels.size() == 0) { + return -1; + } + return mChannels.remove(sRandom.nextInt(mChannels.size())); + } + } + /** * Create a listening, secure RFCOMM Bluetooth socket. *

A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. *

Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections to listening {@link BluetoothServerSocket}. + * connections from a listening {@link BluetoothServerSocket}. *

Valid RFCOMM channels are in range 1 to 30. - *

Requires {@link android.Manifest.permission#BLUETOOTH} + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. + * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, true, true, channel); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } + /** + * Create a listening, secure RFCOMM Bluetooth socket with Service Record. + *

A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

The system will assign an unused RFCOMM channel to listen on. + *

The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid) + throws IOException { + RfcommChannelPicker picker = new RfcommChannelPicker(); + + BluetoothServerSocket socket; + int channel; + int errno; + while (true) { + channel = picker.nextChannel(); + + if (channel == -1) { + throw new IOException("No available channels"); + } + + socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, channel); + errno = socket.mSocket.bindListen(); + if (errno == 0) { + if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); + break; // success + } else if (errno == BluetoothSocket.EADDRINUSE) { + if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use"); + try { + socket.close(); + } catch (IOException e) {} + continue; // try another channel + } else { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() + } + } + + int handle = -1; + try { + handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + if (handle == -1) { + try { + socket.close(); + } catch (IOException e) {} + throw new IOException("Not able to register SDP record for " + name); + } + socket.setCloseHandler(mHandler, handle); + return socket; + } + /** * Construct an unencrypted, unauthenticated, RFCOMM server socket. * Call #accept to retrieve connections to this socket. @@ -595,13 +710,12 @@ public final class BluetoothAdapter { public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -617,13 +731,12 @@ public final class BluetoothAdapter { public static BluetoothServerSocket listenUsingScoOn() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_SCO, false, false, -1); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -636,6 +749,17 @@ public final class BluetoothAdapter { return Collections.unmodifiableSet(devices); } + private Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + /* handle socket closing */ + int handle = msg.what; + try { + if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle)); + mService.removeServiceRecord(handle); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + }; + /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" *

Alphabetic characters must be uppercase to be valid. diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 45dc432d332fe..c14e4c0761d19 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.os.Handler; + import java.io.Closeable; import java.io.IOException; @@ -52,6 +54,8 @@ import java.io.IOException; public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; + private Handler mHandler; + private int mMessage; /** * Construct a socket for incoming connections. @@ -101,6 +105,16 @@ public final class BluetoothServerSocket implements Closeable { * throw an IOException. */ public void close() throws IOException { + synchronized (this) { + if (mHandler != null) { + mHandler.obtainMessage(mMessage).sendToTarget(); + } + } mSocket.close(); } + + /*package*/ synchronized void setCloseHandler(Handler handler, int message) { + mHandler = handler; + mMessage = message; + } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index e462ea6383147..573cb3d82640a 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -54,11 +54,17 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothSocket implements Closeable { + /** @hide */ + public static final int MAX_RFCOMM_CHANNEL = 30; + /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ /*package*/ static final int TYPE_RFCOMM = 1; /*package*/ static final int TYPE_SCO = 2; /*package*/ static final int TYPE_L2CAP = 3; + /*package*/ static final int EBADFD = 77; + /*package*/ static final int EADDRINUSE = 98; + private final int mType; /* one of TYPE_RFCOMM etc */ private final int mPort; /* RFCOMM channel or L2CAP psm */ private final BluetoothDevice mDevice; /* remote device */ @@ -90,6 +96,11 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port) throws IOException { + if (type == BluetoothSocket.TYPE_RFCOMM) { + if (port < 1 || port > MAX_RFCOMM_CHANNEL) { + throw new IOException("Invalid RFCOMM channel: " + port); + } + } mType = type; mAuth = auth; mEncrypt = encrypt; @@ -211,11 +222,15 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } - /*package*/ void bindListen() throws IOException { + /** + * Currently returns unix errno instead of throwing IOException, + * so that BluetoothAdapter can check the error code for EADDRINUSE + */ + /*package*/ int bindListen() { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); - bindListenNative(); + if (mClosed) return EBADFD; + return bindListenNative(); } finally { mLock.readLock().unlock(); } @@ -264,11 +279,16 @@ public final class BluetoothSocket implements Closeable { private native void initSocketNative() throws IOException; private native void initSocketFromFdNative(int fd) throws IOException; private native void connectNative() throws IOException; - private native void bindListenNative() throws IOException; + private native int bindListenNative(); private native BluetoothSocket acceptNative(int timeout) throws IOException; private native int availableNative() throws IOException; private native int readNative(byte[] b, int offset, int length) throws IOException; private native int writeNative(byte[] b, int offset, int length) throws IOException; private native void abortNative() throws IOException; private native void destroyNative() throws IOException; + /** + * Throws an IOException for given posix errno. Done natively so we can + * use strerr to convert to string error. + */ + /*package*/ native void throwErrnoNative(int errno) throws IOException; } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index da0564a2a057a..4164a3d6e6658 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -50,6 +50,10 @@ public final class BluetoothUuid { public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid[] RESERVED_UUIDS = { + AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, + ObexObjectPush}; + public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 2f77ba48da162..e54abec2de28d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -63,4 +63,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + + int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); + void removeServiceRecord(int handle); } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index edf6d10b5dd20..93133d70255c8 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.os.ParcelUuid; @@ -37,6 +38,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; +import android.os.IBinder; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -90,6 +92,8 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap > mDeviceServiceChannelCache; private final ArrayList mUuidIntentTracker; + private final HashMap mServiceRecordToPid; + static { classInitNative(); } @@ -117,6 +121,7 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceServiceChannelCache = new HashMap>(); mUuidIntentTracker = new ArrayList(); + mServiceRecordToPid = new HashMap(); registerForAirplaneMode(); } @@ -206,6 +211,7 @@ public class BluetoothService extends IBluetooth.Stub { mIsDiscovering = false; mAdapterProperties.clear(); + mServiceRecordToPid.clear(); if (saveSetting) { persistBluetoothOnSetting(false); @@ -1211,6 +1217,71 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceServiceChannelCache.put(address, value); } + /** + * b is a handle to a Binder instance, so that this service can be notified + * for Applications that terminate unexpectedly, to clean there service + * records + */ + public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid, + int channel, IBinder b) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + if (serviceName == null || uuid == null || channel < 1 || + channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) { + return -1; + } + if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) { + Log.w(TAG, "Attempted to register a reserved UUID: " + uuid); + return -1; + } + int handle = addRfcommServiceRecordNative(serviceName, + uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(), + (short)channel); + if (DBG) log("new handle " + Integer.toHexString(handle)); + if (handle == -1) { + return -1; + } + + int pid = Binder.getCallingPid(); + mServiceRecordToPid.put(new Integer(handle), new Integer(pid)); + try { + b.linkToDeath(new Reaper(handle, pid), 0); + } catch (RemoteException e) {} + return handle; + } + + public void removeServiceRecord(int handle) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + checkAndRemoveRecord(handle, Binder.getCallingPid()); + } + + private synchronized void checkAndRemoveRecord(int handle, int pid) { + Integer handleInt = new Integer(handle); + Integer owner = mServiceRecordToPid.get(handleInt); + if (owner != null && pid == owner.intValue()) { + if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " + + pid); + mServiceRecordToPid.remove(handleInt); + removeServiceRecordNative(handle); + } + } + + private class Reaper implements IBinder.DeathRecipient { + int pid; + int handle; + Reaper(int handle, int pid) { + this.pid = pid; + this.handle = handle; + } + public void binderDied() { + synchronized (BluetoothService.this) { + if (DBG) log("Tracked app " + pid + " died"); + checkAndRemoveRecord(handle, pid); + } + } + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1263,25 +1334,25 @@ public class BluetoothService extends IBluetooth.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); - switch(mBluetoothState) { case BluetoothAdapter.STATE_OFF: - pw.println("\nBluetooth OFF\n"); + pw.println("Bluetooth OFF\n"); return; case BluetoothAdapter.STATE_TURNING_ON: - pw.println("\nBluetooth TURNING ON\n"); + pw.println("Bluetooth TURNING ON\n"); return; case BluetoothAdapter.STATE_TURNING_OFF: - pw.println("\nBluetooth TURNING OFF\n"); + pw.println("Bluetooth TURNING OFF\n"); return; case BluetoothAdapter.STATE_ON: - pw.println("\nBluetooth ON\n"); + pw.println("Bluetooth ON\n"); } - pw.println("\nLocal address = " + getAddress()); - pw.println("\nLocal name = " + getName()); - pw.println("\nisDiscovering() = " + isDiscovering()); + pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); + + pw.println("Local address = " + getAddress()); + pw.println("Local name = " + getName()); + pw.println("isDiscovering() = " + isDiscovering()); BluetoothHeadset headset = new BluetoothHeadset(mContext, null); @@ -1292,13 +1363,17 @@ public class BluetoothService extends IBluetooth.Stub { toBondStateString(bondState), mBondState.getAttempt(address), getRemoteName(address)); - if (bondState == BluetoothDevice.BOND_BONDED) { - ParcelUuid[] uuids = getRemoteUuids(address); - if (uuids == null) { - pw.printf("\tuuids = null\n"); - } else { - for (ParcelUuid uuid : uuids) { - pw.printf("\t" + uuid + "\n"); + + Map uuidChannels = mDeviceServiceChannelCache.get(address); + if (uuidChannels == null) { + pw.println("\tuuids = null"); + } else { + for (ParcelUuid uuid : uuidChannels.keySet()) { + Integer channel = uuidChannels.get(uuid); + if (channel == null) { + pw.println("\t" + uuid); + } else { + pw.println("\t" + uuid + " RFCOMM channel = " + channel); } } } @@ -1310,8 +1385,10 @@ public class BluetoothService extends IBluetooth.Stub { devicesObjectPath = value.split(","); } pw.println("\n--ACL connected devices--"); - for (String device : devicesObjectPath) { - pw.println(getAddressFromObjectPath(device)); + if (devicesObjectPath != null) { + for (String device : devicesObjectPath) { + pw.println(getAddressFromObjectPath(device)); + } } // Rather not do this from here, but no-where else and I need this @@ -1331,10 +1408,15 @@ public class BluetoothService extends IBluetooth.Stub { pw.println("getState() = STATE_ERROR"); break; } - pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset()); - pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); headset.close(); + pw.println("\n--Application Service Records--"); + for (Integer handle : mServiceRecordToPid.keySet()) { + Integer pid = mServiceRecordToPid.get(handle); + pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); + } } /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { @@ -1423,8 +1505,12 @@ public class BluetoothService extends IBluetooth.Stub { private native boolean setPasskeyNative(String address, int passkey, int nativeData); private native boolean setPairingConfirmationNative(String address, boolean confirm, int nativeData); - private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value); + private native boolean setDevicePropertyBooleanNative(String objectPath, String key, + int value); private native boolean createDeviceNative(String address); private native boolean discoverServicesNative(String objectPath, String pattern); + private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, + short channel); + private native boolean removeServiceRecordNative(int handle); } diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp index 2532effe98bdc..31ebf8ca58cce 100644 --- a/core/jni/android_bluetooth_BluetoothSocket.cpp +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -237,7 +237,8 @@ static void connectNative(JNIEnv *env, jobject obj) { jniThrowIOException(env, ENOSYS); } -static void bindListenNative(JNIEnv *env, jobject obj) { +/* Returns errno instead of throwing, so java can check errno */ +static int bindListenNative(JNIEnv *env, jobject obj) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -248,7 +249,7 @@ static void bindListenNative(JNIEnv *env, jobject obj) { struct asocket *s = get_socketData(env, obj); if (!s) - return; + return EINVAL; type = env->GetIntField(obj, field_mType); @@ -283,28 +284,25 @@ static void bindListenNative(JNIEnv *env, jobject obj) { memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t)); break; default: - jniThrowIOException(env, ENOSYS); - return; + return ENOSYS; } if (bind(s->fd, addr, addr_sz)) { LOGV("...bind(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } if (listen(s->fd, 1)) { LOGV("...listen(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } LOGV("...bindListenNative(%d) success", s->fd); - return; + return 0; #endif - jniThrowIOException(env, ENOSYS); + return ENOSYS; } static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) { @@ -521,17 +519,22 @@ static void destroyNative(JNIEnv *env, jobject obj) { jniThrowIOException(env, ENOSYS); } +static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) { + jniThrowIOException(env, err); +} + static JNINativeMethod sMethods[] = { {"initSocketNative", "()V", (void*) initSocketNative}, {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative}, {"connectNative", "()V", (void *) connectNative}, - {"bindListenNative", "()V", (void *) bindListenNative}, + {"bindListenNative", "()I", (void *) bindListenNative}, {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, {"availableNative", "()I", (void *) availableNative}, {"readNative", "([BII)I", (void *) readNative}, {"writeNative", "([BII)I", (void *) writeNative}, {"abortNative", "()V", (void *) abortNative}, {"destroyNative", "()V", (void *) destroyNative}, + {"throwErrnoNative", "(I)V", (void *) throwErrnoNative}, }; int register_android_bluetooth_BluetoothSocket(JNIEnv *env) { diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index c2f93eea868d3..ea64305d145f8 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -818,6 +818,48 @@ static jboolean discoverServicesNative(JNIEnv *env, jobject object, return JNI_FALSE; } +static jint addRfcommServiceRecordNative(JNIEnv *env, jobject object, + jstring name, jlong uuidMsb, jlong uuidLsb, jshort channel) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_name = env->GetStringUTFChars(name, NULL); + LOGV("... name = %s", c_name); + LOGV("... uuid1 = %llX", uuidMsb); + LOGV("... uuid2 = %llX", uuidLsb); + LOGV("... channel = %d", channel); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord", + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_UINT64, &uuidMsb, + DBUS_TYPE_UINT64, &uuidLsb, + DBUS_TYPE_UINT16, &channel, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(name, c_name); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint handle) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + LOGV("... handle = %X", handle); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "RemoveServiceRecord", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + return reply ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ @@ -861,6 +903,8 @@ static JNINativeMethod sMethods[] = { (void *)setDevicePropertyBooleanNative}, {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative}, {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative}, + {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative}, + {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative}, }; int register_android_server_BluetoothService(JNIEnv *env) { From 52cde7279bad58285704498eea57bdaf9e595b49 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 6 Oct 2009 16:10:02 +0200 Subject: [PATCH 10/10] Fix docs builds. --- core/java/android/bluetooth/BluetoothServerSocket.java | 2 +- core/java/android/bluetooth/BluetoothSocket.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index c14e4c0761d19..d126ea48dddd5 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -40,7 +40,7 @@ import java.io.IOException; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. * diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 573cb3d82640a..b9e33f3bda057 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. *