Merge "PBAP Client SDP record"
diff --git a/jni/com_android_bluetooth_sdp.cpp b/jni/com_android_bluetooth_sdp.cpp
index c2eb5ce..827db71 100644
--- a/jni/com_android_bluetooth_sdp.cpp
+++ b/jni/com_android_bluetooth_sdp.cpp
@@ -307,6 +307,37 @@
   return handle;
 }
 
+static jint sdpCreatePbapPceRecordNative(JNIEnv* env, jobject obj,
+                                         jstring name_str, jint version) {
+  ALOGD("%s", __func__);
+  if (!sBluetoothSdpInterface) return -1;
+
+  bluetooth_sdp_record record = {};  // Must be zero initialized
+  record.pce.hdr.type = SDP_TYPE_PBAP_PCE;
+
+  const char* service_name = NULL;
+  if (name_str != NULL) {
+    service_name = env->GetStringUTFChars(name_str, NULL);
+    record.pce.hdr.service_name = (char*)service_name;
+    record.pce.hdr.service_name_length = strlen(service_name);
+  } else {
+    record.pce.hdr.service_name = NULL;
+    record.pce.hdr.service_name_length = 0;
+  }
+  record.pce.hdr.profile_version = version;
+
+  int handle = -1;
+  int ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle);
+  if (ret != BT_STATUS_SUCCESS) {
+    ALOGE("SDP Create record failed: %d", ret);
+  } else {
+    ALOGD("SDP Create record success - handle: %d", handle);
+  }
+
+  if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
+  return handle;
+}
+
 static jint sdpCreatePbapPseRecordNative(JNIEnv* env, jobject obj,
                                          jstring name_str, jint scn,
                                          jint l2cap_psm, jint version,
@@ -474,6 +505,8 @@
      (void*)sdpCreateMapMasRecordNative},
     {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I",
      (void*)sdpCreateMapMnsRecordNative},
+    {"sdpCreatePbapPceRecordNative", "(Ljava/lang/String;I)I",
+     (void*)sdpCreatePbapPceRecordNative},
     {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I",
      (void*)sdpCreatePbapPseRecordNative},
     {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I",
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
index c060ab2..5c3fa4f 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
@@ -92,7 +92,6 @@
     private static final long PBAP_REQUESTED_FIELDS =
             PBAP_FILTER_VERSION | PBAP_FILTER_FN | PBAP_FILTER_N | PBAP_FILTER_PHOTO
                     | PBAP_FILTER_ADR | PBAP_FILTER_EMAIL | PBAP_FILTER_TEL | PBAP_FILTER_NICKNAME;
-    private static final int PBAP_V1_2 = 0x0102;
     private static final int L2CAP_INVALID_PSM = -1;
 
     public static final String PB_PATH = "telecom/pb.vcf";
@@ -100,6 +99,7 @@
     public static final String ICH_PATH = "telecom/ich.vcf";
     public static final String OCH_PATH = "telecom/och.vcf";
 
+    public static final int PBAP_V1_2 = 0x0102;
     public static final byte VCARD_TYPE_21 = 0;
     public static final byte VCARD_TYPE_30 = 1;
 
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientService.java b/src/com/android/bluetooth/pbapclient/PbapClientService.java
index 4775e4b..f150cdd 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -31,6 +31,7 @@
 import com.android.bluetooth.R;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.sdp.SdpManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,12 +48,14 @@
     private static final boolean VDBG = Utils.VDBG;
 
     private static final String TAG = "PbapClientService";
+    private static final String SERVICE_NAME = "Phonebook Access PCE";
     // MAXIMUM_DEVICES set to 10 to prevent an excessive number of simultaneous devices.
     private static final int MAXIMUM_DEVICES = 10;
     private Map<BluetoothDevice, PbapClientStateMachine> mPbapClientStateMachineMap =
             new ConcurrentHashMap<>();
     private static PbapClientService sPbapClientService;
     private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
+    private int mSdpHandle = -1;
 
     @Override
     public IProfileServiceBinder initBinder() {
@@ -73,13 +76,17 @@
         } catch (Exception e) {
             Log.w(TAG, "Unable to register pbapclient receiver", e);
         }
+
         removeUncleanAccounts();
+        registerSdpRecord();
         setPbapClientService(this);
         return true;
     }
 
     @Override
     protected boolean stop() {
+        setPbapClientService(null);
+        cleanUpSdpRecord();
         try {
             unregisterReceiver(mPbapBroadcastReceiver);
         } catch (Exception e) {
@@ -88,14 +95,8 @@
         for (PbapClientStateMachine pbapClientStateMachine : mPbapClientStateMachineMap.values()) {
             pbapClientStateMachine.doQuit();
         }
-        return true;
-    }
-
-    @Override
-    protected void cleanup() {
         removeUncleanAccounts();
-        // TODO: Should move to stop()
-        setPbapClientService(null);
+        return true;
     }
 
     void cleanupDevice(BluetoothDevice device) {
@@ -127,6 +128,35 @@
         }
     }
 
+    private void registerSdpRecord() {
+        SdpManager sdpManager = SdpManager.getDefaultManager();
+        if (sdpManager == null) {
+            Log.e(TAG, "SdpManager is null");
+            return;
+        }
+        mSdpHandle = sdpManager.createPbapPceRecord(SERVICE_NAME,
+                PbapClientConnectionHandler.PBAP_V1_2);
+    }
+
+    private void cleanUpSdpRecord() {
+        if (mSdpHandle < 0) {
+            Log.e(TAG, "cleanUpSdpRecord, SDP record never created");
+            return;
+        }
+        int sdpHandle = mSdpHandle;
+        mSdpHandle = -1;
+        SdpManager sdpManager = SdpManager.getDefaultManager();
+        if (sdpManager == null) {
+            Log.e(TAG, "cleanUpSdpRecord failed, sdpManager is null, sdpHandle=" + sdpHandle);
+            return;
+        }
+        Log.i(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
+        if (!sdpManager.removeSdpRecord(sdpHandle)) {
+            Log.e(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
+        }
+    }
+
+
     private class PbapBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/src/com/android/bluetooth/sdp/SdpManager.java b/src/com/android/bluetooth/sdp/SdpManager.java
index d0cca25..d5e3770 100644
--- a/src/com/android/bluetooth/sdp/SdpManager.java
+++ b/src/com/android/bluetooth/sdp/SdpManager.java
@@ -85,6 +85,9 @@
     private native int sdpCreateMapMnsRecordNative(String serviceName, int rfcommChannel,
             int l2capPsm, int version, int features);
 
+    private native int sdpCreatePbapPceRecordNative(String serviceName,
+            int version);
+
     private native int sdpCreatePbapPseRecordNative(String serviceName, int rfcommChannel,
             int l2capPsm, int version, int repositories, int features);
 
@@ -537,6 +540,27 @@
         return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, l2capPsm, version, features);
     }
 
+     /**
+     * Create a Client side Phone Book Access Profile Service Record.
+     * Create the record once, and reuse it for all connections.
+     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
+     * and then create a new one.
+     * @param serviceName   The textual name of the service
+     * @param version       The Profile version number (As specified in the Bluetooth
+     *                      PBAP specification)
+     * @return a handle to the record created. The record can be removed again
+     *          using {@link removeSdpRecord}(). The record is not linked to the
+     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
+     *          is a separate process.
+     */
+    public int createPbapPceRecord(String serviceName, int version) {
+        if (!sNativeAvailable) {
+            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
+        }
+        return sdpCreatePbapPceRecordNative(serviceName, version);
+    }
+
+
     /**
      * Create a Server side Phone Book Access Profile Service Record.
      * Create the record once, and reuse it for all connections.