Merge "MAP: Add MNS SDP Search during setNotificationRegistration."
diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index cdd5317..335b728 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -327,6 +327,26 @@
         }
     }
 
+    public int getObserverRemoteFeatureMask() {
+        if (V) Log.v(TAG, "getObserverRemoteFeatureMask : " + mMapEventReportVersion
+            + " mMapSupportedFeatures: " + mMapSupportedFeatures);
+        return mMapSupportedFeatures;
+    }
+
+    public void setObserverRemoteFeatureMask(int remoteSupportedFeatures) {
+        mMapSupportedFeatures = remoteSupportedFeatures;
+        if ((BluetoothMapUtils.MAP_FEATURE_EXTENDED_EVENT_REPORT_11_BIT
+                & mMapSupportedFeatures) != 0) {
+            mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        }
+        // Make sure support for all formats result in latest version returned
+        if ((BluetoothMapUtils.MAP_FEATURE_EVENT_REPORT_V12_BIT
+                & mMapSupportedFeatures) != 0) {
+            mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+        }
+        if (V) Log.d(TAG, "setObserverRemoteFeatureMask : " + mMapEventReportVersion
+            + " mMapSupportedFeatures : " + mMapSupportedFeatures);
+    }
 
     private Map<Long, Msg> getMsgListSms() {
         return mMsgListSms;
@@ -865,28 +885,48 @@
     public int setNotificationRegistration(int notificationStatus) throws RemoteException {
         // Forward the request to the MNS thread as a message - including the MAS instance ID.
         if(D) Log.d(TAG,"setNotificationRegistration() enter");
+        if (mMnsClient == null) {
+            return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        }
         Handler mns = mMnsClient.getMessageHandler();
-        if(mns != null) {
+        if (mns != null) {
             Message msg = mns.obtainMessage();
-            msg.what = BluetoothMnsObexClient.MSG_MNS_NOTIFICATION_REGISTRATION;
+            if (mMnsClient.isValidMnsRecord()) {
+                msg.what = BluetoothMnsObexClient.MSG_MNS_NOTIFICATION_REGISTRATION;
+            } else {
+                //Trigger SDP Search and notificaiton registration , if SDP record not found.
+                msg.what = BluetoothMnsObexClient.MSG_MNS_SDP_SEARCH_REGISTRATION;
+                if (mMnsClient.mMnsLstRegRqst != null &&
+                        (mMnsClient.mMnsLstRegRqst.isSearchInProgress())) {
+                    /*  1. Disallow next Notification ON Request :
+                     *     - Respond "Service Unavailable" as SDP Search and last notification
+                     *       registration ON request is already InProgress.
+                     *     - Next notification ON Request will be allowed ONLY after search
+                     *       and connect for last saved request [Replied with OK ] is processed.
+                     */
+                    if (notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) {
+                        return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                    } else {
+                        /*  2. Allow next Notification OFF Request:
+                         *    - Keep the SDP search still in progress.
+                         *    - Disconnect and Deregister the contentObserver.
+                         */
+                        msg.what = BluetoothMnsObexClient.MSG_MNS_NOTIFICATION_REGISTRATION;
+                    }
+                }
+            }
             msg.arg1 = mMasId;
             msg.arg2 = notificationStatus;
             mns.sendMessageDelayed(msg, 10); // Send message without forcing a context switch
             /* Some devices - e.g. PTS needs to get the unregister confirm before we actually
              * disconnect the MNS. */
-            if(D) Log.d(TAG,"setNotificationRegistration() MSG_MNS_NOTIFICATION_REGISTRATION " +
-                    "send to MNS");
+            if(D) Log.d(TAG,"setNotificationRegistration() send : " + msg.what + " to MNS ");
+            return ResponseCodes.OBEX_HTTP_OK;
         } else {
             // This should not happen except at shutdown.
             if(D) Log.d(TAG,"setNotificationRegistration() Unable to send registration request");
             return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
         }
-        if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) {
-            registerObserver();
-        } else {
-            unregisterObserver();
-        }
-        return ResponseCodes.OBEX_HTTP_OK;
     }
 
     boolean eventMaskContainsContacts(long mask) {
diff --git a/src/com/android/bluetooth/map/BluetoothMapMasInstance.java b/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
index 66e6d3b..0da7292 100644
--- a/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
+++ b/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
@@ -434,8 +434,13 @@
         }
     }
 
-    public void setRemoteFeatureMask(int supported_features) {
-        mRemoteFeatureMask  = supported_features;
+    public void setRemoteFeatureMask(int supportedFeatures) {
+       if(V) Log.v(TAG, "setRemoteFeatureMask : Curr: "+ mRemoteFeatureMask);
+       mRemoteFeatureMask  = supportedFeatures;
+       if (mObserver != null) {
+           mObserver.setObserverRemoteFeatureMask(mRemoteFeatureMask);
+           if(V) Log.v(TAG, "setRemoteFeatureMask : set: " + mRemoteFeatureMask);
+       }
     }
 
     public int getRemoteFeatureMask(){
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java
old mode 100755
new mode 100644
index 04d7043..9cca2c7
--- a/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -90,6 +90,10 @@
 
     public static final int MSG_RELEASE_WAKE_LOCK = 5006;
 
+    public static final int MSG_MNS_SDP_SEARCH = 5007;
+
+    public static final int MSG_OBSERVER_REGISTRATION = 5008;
+
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
@@ -389,6 +393,30 @@
                         if (DEBUG) Log.d(TAG, "  Released Wake Lock by message");
                     }
                     break;
+                case MSG_MNS_SDP_SEARCH:
+                    if (mRemoteDevice != null) {
+                        if (DEBUG) Log.d(TAG,"MNS SDP Initiate Search ..");
+                        mRemoteDevice.sdpSearch(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS);
+                    } else {
+                        Log.w(TAG, "remoteDevice info not available");
+                    }
+                    break;
+                case MSG_OBSERVER_REGISTRATION:
+                    if (DEBUG) Log.d(TAG,"ContentObserver Registration MASID: " + msg.arg1
+                        + " Enable: " + msg.arg2);
+                    BluetoothMapMasInstance masInst = mMasInstances.get(msg.arg1);
+                    if (masInst != null) {
+                        try {
+                            if (msg.arg2 == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) {
+                                masInst.mObserver.registerObserver();
+                            } else {
+                                masInst.mObserver.unregisterObserver();
+                            }
+                        } catch (RemoteException e) {
+                            Log.e(TAG,"ContentObserverRegistarion Failed: "+ e);
+                        }
+                    }
+                    break;
                 default:
                     break;
             }
@@ -965,31 +993,34 @@
                     }
                     sendConnectCancelMessage();
                 }
-            } else if (action.equals(BluetoothDevice.ACTION_SDP_RECORD)){
-//                Log.v(TAG, "Received ACTION_SDP_RECORD.");
+            } else if (action.equals(BluetoothDevice.ACTION_SDP_RECORD)) {
+                if (DEBUG) Log.d(TAG, "Received ACTION_SDP_RECORD.");
                 ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID);
                 if (VERBOSE) {
                     Log.v(TAG, "Received UUID: " + uuid.toString());
                     Log.v(TAG, "expected UUID: " +
                           BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS.toString());
                 }
-                if(uuid.equals(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS)
-                        && mSdpSearchInitiated)
-                {
+                if (uuid.equals(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS)) {
                     mMnsRecord = intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD);
                     int status = intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, -1);
                     if (VERBOSE) {
                         Log.v(TAG, " -> MNS Record:" + mMnsRecord);
                         Log.v(TAG, " -> status: " + status);
                     }
-                    mSdpSearchInitiated = false; // done searching
-                    if(status != -1 && mMnsRecord != null){
-                        for(int i=0, c=mMasInstances.size(); i < c; i++) {
+                    if (mBluetoothMnsObexClient != null && !mSdpSearchInitiated) {
+                        mBluetoothMnsObexClient.setMnsRecord(mMnsRecord);
+                    }
+                    if (status != -1 && mMnsRecord != null) {
+                        for (int i = 0, c = mMasInstances.size(); i < c; i++) {
                                 mMasInstances.valueAt(i).setRemoteFeatureMask(
                                         mMnsRecord.getSupportedFeatures());
                         }
                     }
-                    sendConnectMessage(-1); // -1 indicates all MAS instances
+                    if (mSdpSearchInitiated) {
+                        mSdpSearchInitiated = false; // done searching
+                        sendConnectMessage(-1); // -1 indicates all MAS instances
+                    }
                 }
             } else if (action.equals(ACTION_SHOW_MAPS_SETTINGS)) {
                 if (VERBOSE) Log.v(TAG, "Received ACTION_SHOW_MAPS_SETTINGS.");
diff --git a/src/com/android/bluetooth/map/BluetoothMnsObexClient.java b/src/com/android/bluetooth/map/BluetoothMnsObexClient.java
index 39aa873..2558641 100644
--- a/src/com/android/bluetooth/map/BluetoothMnsObexClient.java
+++ b/src/com/android/bluetooth/map/BluetoothMnsObexClient.java
@@ -60,12 +60,16 @@
 
     private HeaderSet mHsConnect = null;
     private Handler mCallback = null;
-    private final SdpMnsRecord mMnsRecord;
+    private SdpMnsRecord mMnsRecord;
     // Used by the MAS to forward notification registrations
     public static final int MSG_MNS_NOTIFICATION_REGISTRATION = 1;
     public static final int MSG_MNS_SEND_EVENT = 2;
+    public static final int MSG_MNS_SDP_SEARCH_REGISTRATION = 3;
 
-
+    //Copy SdpManager.SDP_INTENT_DELAY - The timeout to wait for reply from native.
+    private final int MNS_SDP_SEARCH_DELAY = 6000;
+    public MnsSdpSearchInfo mMnsLstRegRqst = null;
+    private static final int MNS_NOTIFICATION_DELAY = 10;
     public static final ParcelUuid BLUETOOTH_UUID_OBEX_MNS =
             ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
 
@@ -90,6 +94,26 @@
         return mHandler;
     }
 
+    class MnsSdpSearchInfo {
+        private boolean isSearchInProgress;
+        int lastMasId;
+        int lastNotificationStatus;
+
+        MnsSdpSearchInfo (boolean isSearchON, int masId, int notification) {
+            isSearchInProgress = isSearchON;
+            lastMasId = masId;
+            lastNotificationStatus = notification;
+        }
+
+        public boolean isSearchInProgress() {
+            return isSearchInProgress;
+        }
+
+        public void setIsSearchInProgress(boolean isSearchON) {
+            isSearchInProgress = isSearchON;
+        }
+    }
+
     private final class MnsObexClientHandler extends Handler {
         private MnsObexClientHandler(Looper looper) {
             super(looper);
@@ -99,11 +123,29 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case MSG_MNS_NOTIFICATION_REGISTRATION:
-                handleRegistration(msg.arg1 /*masId*/, msg.arg2 /*status*/);
+                if (V) Log.v(TAG, "Reg  masId:  " + msg.arg1 + " notfStatus: " + msg.arg2);
+                if (isValidMnsRecord()) {
+                    handleRegistration(msg.arg1 /*masId*/, msg.arg2 /*status*/);
+                } else {
+                    //Should not happen
+                    if (D) Log.d(TAG, "MNS SDP info not available yet - Cannot Connect.");
+                }
                 break;
             case MSG_MNS_SEND_EVENT:
                 sendEventHandler((byte[])msg.obj/*byte[]*/, msg.arg1 /*masId*/);
                 break;
+            case MSG_MNS_SDP_SEARCH_REGISTRATION:
+                //Initiate SDP Search
+                notifyMnsSdpSearch();
+                //Save the mns search info
+                mMnsLstRegRqst = new MnsSdpSearchInfo(true, msg.arg1, msg.arg2);
+                //Handle notification registration.
+                Message msgReg =
+                        mHandler.obtainMessage(MSG_MNS_NOTIFICATION_REGISTRATION,
+                                msg.arg1, msg.arg2);
+                if (V) Log.v(TAG, "SearchReg  masId:  " + msg.arg1 + " notfStatus: " + msg.arg2);
+                mHandler.sendMessageDelayed(msgReg, MNS_SDP_SEARCH_DELAY);
+                break;
             default:
                 break;
             }
@@ -180,24 +222,80 @@
      */
     public synchronized void handleRegistration(int masId, int notificationStatus){
         if(D) Log.d(TAG, "handleRegistration( " + masId + ", " + notificationStatus + ")");
-
-        if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_NO) {
+        boolean sendObserverRegistration = true;
+        if (notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_NO) {
             mRegisteredMasIds.delete(masId);
-        } else if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) {
+            if (mMnsLstRegRqst != null &&  mMnsLstRegRqst.lastMasId == masId) {
+                //Clear last saved MNSSdpSearchInfo , if Disconnect requested for same MasId.
+                mMnsLstRegRqst = null;
+            }
+        } else if (notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) {
             /* Connect if we do not have a connection, and start the content observers providing
              * this thread as Handler.
              */
-            if(isConnected() == false) {
+            if (isConnected() == false) {
                 if(D) Log.d(TAG, "handleRegistration: connect");
                 connect();
             }
+            sendObserverRegistration = isConnected();
             mRegisteredMasIds.put(masId, true); // We don't use the value for anything
+
+            // Clear last saved MNSSdpSearchInfo after connect is processed.
+            mMnsLstRegRqst = null;
         }
-        if(mRegisteredMasIds.size() == 0) {
+
+        if (mRegisteredMasIds.size() == 0) {
             // No more registrations - disconnect
             if(D) Log.d(TAG, "handleRegistration: disconnect");
             disconnect();
         }
+
+        //Register ContentObserver After connect/disconnect MNS channel.
+        if (V) Log.v(TAG, "Send  registerObserver: " + sendObserverRegistration);
+        if (mCallback != null && sendObserverRegistration) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothMapService.MSG_OBSERVER_REGISTRATION;
+            msg.arg1 = masId;
+            msg.arg2 = notificationStatus;
+            msg.sendToTarget();
+        }
+    }
+
+    public boolean isValidMnsRecord() {
+        return (mMnsRecord != null);
+    }
+
+    public void setMnsRecord(SdpMnsRecord mnsRecord) {
+        if (V) Log.v(TAG, "setMNSRecord");
+        if (isValidMnsRecord()) {
+           Log.w(TAG,"MNS Record already available. Still update.");
+        }
+        mMnsRecord = mnsRecord;
+        if (mMnsLstRegRqst != null) {
+            //SDP Search completed.
+            mMnsLstRegRqst.setIsSearchInProgress(false);
+            if (mHandler.hasMessages(MSG_MNS_NOTIFICATION_REGISTRATION)) {
+                mHandler.removeMessages(MSG_MNS_NOTIFICATION_REGISTRATION);
+                //Search Result obtained within MNS_SDP_SEARCH_DELAY timeout
+                if (!isValidMnsRecord()) {
+                    // SDP info still not available for last trial.
+                    // Clear saved info.
+                    mMnsLstRegRqst = null;
+                } else {
+                    if (V) Log.v(TAG, "Handle registration for last saved request");
+                    Message msgReg =
+                            mHandler.obtainMessage(MSG_MNS_NOTIFICATION_REGISTRATION);
+                    msgReg.arg1 = mMnsLstRegRqst.lastMasId;
+                    msgReg.arg2 = mMnsLstRegRqst.lastNotificationStatus;
+                    if (V) Log.v(TAG, "SearchReg  masId:  " + msgReg.arg1
+                        + " notfStatus: " + msgReg.arg2);
+                    //Handle notification registration.
+                    mHandler.sendMessageDelayed(msgReg, MNS_NOTIFICATION_DELAY);
+                }
+            }
+        } else {
+           if (V) Log.v(TAG, "No last saved MNSSDPInfo to handle");
+        }
     }
 
     public void connect() {
@@ -207,11 +305,11 @@
         BluetoothSocket btSocket = null;
         try {
             // TODO: Do SDP record search again?
-            if(mMnsRecord != null && mMnsRecord.getL2capPsm() > 0) {
+            if (isValidMnsRecord() && mMnsRecord.getL2capPsm() > 0) {
                 // Do L2CAP connect
                 btSocket = mRemoteDevice.createL2capSocket(mMnsRecord.getL2capPsm());
 
-            } else if (mMnsRecord != null && mMnsRecord.getRfcommChannelNumber() > 0) {
+            } else if (isValidMnsRecord() && mMnsRecord.getRfcommChannelNumber() > 0) {
                 // Do Rfcomm connect
                 btSocket = mRemoteDevice.createRfcommSocket(mMnsRecord.getRfcommChannelNumber());
             } else {
@@ -280,6 +378,14 @@
         notifyUpdateWakeLock();
     }
 
+    private void notifyMnsSdpSearch() {
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothMapService.MSG_MNS_SDP_SEARCH;
+            msg.sendToTarget();
+        }
+    }
+
     private int sendEventHandler(byte[] eventBytes, int masInstanceId) {
 
         boolean error = false;