Bluetooth: Fix to avoid DUN crash due to incorrect readIndex

- Fix to avoid indexOutOfBoundsException due to incorrect
  readIndex in DownlinkThread after reading response from
  Dun Server while disconnecting DUN from PC side.
- Added Dynamic Verbose logging support
- Optimized DUN Service code

Change-Id: Ia7c3392e92e84b9c7e3cbdb62ea0ae9224588b5c
CRs-Fixed: 702364
diff --git a/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java b/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java
index 39c4eb4..ff1871d 100644
--- a/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java
+++ b/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java
@@ -73,8 +73,8 @@
 public class BluetoothDunService extends Service {
 
     private static final String TAG                             = "BluetoothDunService";
-    private static final boolean DBG                            = false;
-    public  static final boolean VERBOSE                        = false;
+    public static final String LOG_TAG                          = "BluetoothDun";
+    public static boolean VERBOSE;
 
     private final static String DUN_SERVER                      = "qcom.dun.server";
 
@@ -284,7 +284,7 @@
     public void onCreate() {
 
         super.onCreate();
-
+        VERBOSE = Log.isLoggable(LOG_TAG, Log.VERBOSE) ? true : false;
         if (VERBOSE) Log.v(TAG, "Dun Service onCreate");
 
         mDunDevices = new HashMap<BluetoothDevice, BluetoothDunDevice>();
@@ -363,7 +363,7 @@
                             closeRfcommSocket();
                             startRfcommListenerThread();
 
-                            if (DBG) Log.d(TAG,"DUN user Authorization Timeout");
+                            if (VERBOSE) Log.v(TAG, "DUN user Authorization Timeout");
                         }
                         break;
                 }
@@ -374,16 +374,14 @@
     private void parseIntent(final Intent intent) {
 
         String action = intent.getStringExtra("action");
-        if (VERBOSE) Log.v(TAG, "action: " + action);
+        Log.d(TAG, "parseIntent: action: " + action);
 
         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
-        if (VERBOSE) Log.v(TAG, "state: " + state);
+        if (VERBOSE) Log.v(TAG, "parseIntent: state: " + state);
 
         boolean removeTimeoutMsg = true;
         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
-
             if ((state == BluetoothAdapter.STATE_ON) && (!mDunEnable)) {
-
                 synchronized(mConnection) {
                     try {
                         if (mAdapterService == null) {
@@ -422,8 +420,7 @@
                     }
                     return;
                 }
-            }
-            else if (state == BluetoothAdapter.STATE_TURNING_OFF) {
+            } else if (state == BluetoothAdapter.STATE_TURNING_OFF) {
                 // Send any pending timeout now, as this service will be destroyed.
                 if (mDunHandler.hasMessages(MESSAGE_DUN_USER_TIMEOUT)) {
                     Intent timeoutIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
@@ -474,16 +471,14 @@
                removeTimeoutMsg = false;
             }
 
-        }  else if (action.equals(DUN_ACCESS_ALLOWED_ACTION)) {
+        } else if (action.equals(DUN_ACCESS_ALLOWED_ACTION)) {
+            if (VERBOSE) Log.v(TAG, "DunService-Received ACCESS_ALLOWED_ACTION");
+
             if (mRemoteDevice == null) {
                Log.e(TAG, "Unexpected error!");
                return;
             }
 
-            if (VERBOSE) {
-
-                Log.v(TAG, "DunService-Received ACCESS_ALLOWED_ACTION");
-            }
             synchronized(mAuthLock) {
                 if (!mIsWaitingAuthorization) {
                     // this reply is not for us
@@ -509,10 +504,10 @@
             mIsWaitingAuthorization = false;
             closeRfcommSocket();
             startRfcommListenerThread();
-        } else if ( BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
+        } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
 
             if (intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
-               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 if(device != null)
                     Log.d(TAG,"device: "+ device.getName());
                 if(mRemoteDevice != null)
@@ -679,7 +674,7 @@
 
     private final synchronized void closeListenSocket() {
         // exit SocketAcceptThread early
-        if (VERBOSE) Log.v(TAG, "Close listen Socket : " );
+        Log.d(TAG, "Close listen Socket : ");
         if (mListenSocket != null) {
             try {
                 // this will cause mListenSocket.accept() return early with
@@ -693,7 +688,7 @@
     }
 
     private final synchronized void closeRfcommSocket() {
-        if (VERBOSE) Log.v(TAG, "Close rfcomm Socket : " );
+        Log.d(TAG, "Close rfcomm Socket : ");
         if (mRfcommSocket != null) {
             try {
                 mRfcommSocket.close();
@@ -705,7 +700,7 @@
     }
 
     private final synchronized void closeDundSocket() {
-        if (VERBOSE) Log.v(TAG, "Close dund  Socket : " );
+        Log.d(TAG, "Close dund  Socket : ");
         if (mDundSocket != null) {
             try {
                 mDundSocket.close();
@@ -717,7 +712,7 @@
     }
 
     private final void closeDunService() {
-        if (VERBOSE) Log.v(TAG, "Dun Service closeDunService in");
+        Log.d(TAG, "Dun Service closeDunService in");
 
         // exit initRfcommSocket early
         mInterrupted = true;
@@ -781,7 +776,7 @@
 
         stopSelf();
 
-        if (VERBOSE) Log.v(TAG, "Dun Service closeDunService out");
+        Log.d(TAG, "Dun Service closeDunService out");
     }
 
     private void createDunNotification(BluetoothDevice device) {
@@ -854,13 +849,12 @@
 
             while (!stopped) {
                 try {
-                    if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
                     listenSocket = mListenSocket;
                     if (listenSocket == null) {
                         Log.w(TAG, "mListenSocket is null");
                         break;
                     }
-
+                    Log.d(TAG, "Listening for rfcomm socket connection...");
                     mRfcommSocket = mListenSocket.accept();
 
                     synchronized (BluetoothDunService.this) {
@@ -897,7 +891,7 @@
                     if (VERBOSE) Log.v(TAG, "SocketAcceptThread stopped ");
                 } catch (IOException ex) {
                     stopped=true;
-                    if (VERBOSE) Log.v(TAG, "Accept thread exception: " + ex.toString());
+                    Log.w(TAG, "Handled Accept thread exception: " + ex.toString());
                 }
             }
         }
@@ -935,29 +929,28 @@
                     return;
                 }
             }
-            if(mDundSocket != null) {
-                if( mDundSocket.isConnected())
-                    try {
-                        mDundOutputStream = mDundSocket.getOutputStream();
-                    } catch (IOException ex) {
-                        if (VERBOSE)
-                            Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
-                    }
-                else return;
-            }
-            else return;
 
-            if (mRfcommSocket != null) {
-                if(mRfcommSocket.isConnected())
-                    try {
-                        mRfcommInputStream = mRfcommSocket.getInputStream();
-                    } catch (IOException ex) {
-                        if (VERBOSE)
-                            Log.v(TAG, "mRfcommInputStream exception: " + ex.toString());
-                    }
-                else return;
+            if (mDundSocket != null && mDundSocket.isConnected()) {
+                try {
+                    mDundOutputStream = mDundSocket.getOutputStream();
+                } catch (IOException ex) {
+                    Log.w(TAG, "Handled mDundOutputStream exception: " + ex.toString());
+                }
+            } else {
+                Log.w(TAG, "UplinkThread: Dund Socket is not connected: " +  mDundSocket);
+                return;
             }
-            else return;
+
+            if (mRfcommSocket != null && mRfcommSocket.isConnected()) {
+                try {
+                    mRfcommInputStream = mRfcommSocket.getInputStream();
+                } catch (IOException ex) {
+                    Log.w(TAG, "Handled mRfcommInputStream exception: " + ex.toString());
+                }
+            } else {
+                Log.w(TAG, "UplinkThread: rfcomm Socket is not connected: " +  mRfcommSocket);
+                return;
+            }
 
             // create the downlink thread for reading the responses
             //from DUN server process
@@ -972,12 +965,11 @@
                     /* Read the DUN request from Rfcomm channel */
                     NumRead = mRfcommInputStream.read(IpcMsgBuffer.array(), DUN_IPC_MSG_OFF_MSG,
                                                                                 DUN_MAX_MSG_LEN);
-                    if ( NumRead < 0) {
+                    if (NumRead < 0) {
                         IntExit = true;
                         break;
-                    }
-                    else if ( NumRead != 0) {
-                        /* Wrtie the same DUN request to the DUN server socket with
+                    } else if (NumRead != 0) {
+                        /* Write the same DUN request to the DUN server socket with
                            some additional parameters */
                         IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_DUN_REQUEST);
                         IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN, (short)NumRead);
@@ -995,7 +987,7 @@
                     }
                 } catch (IOException ex) {
                     IntExit = true;
-                    if (VERBOSE) Log.v(TAG, "Rfcomm channel Read exception: " + ex.toString());
+                    Log.w(TAG, "Handled Rfcomm channel Read exception: " + ex.toString());
                     break;
                 }
             }
@@ -1046,7 +1038,7 @@
             }
             /* reset the modem status */
             mRmtMdmStatus = 0x00;
-            if (VERBOSE) Log.v(TAG, "uplink thread exited ");
+            Log.d(TAG, "uplink thread exited");
         }
 
         void shutdown() {
@@ -1084,22 +1076,24 @@
                 }
                 try {
                     if ((mRfcommSocket == null) || (!mRfcommSocket.isConnected())) {
+                        Log.w(TAG, "MonitorThread: rfcomm Socket is not connected: "
+                                + mRfcommSocket);
                         break;
                     }
                     len = mRfcommSocket.getSocketOpt(BTSOCK_OPT_GET_MODEM_BITS, IpcMsgBuffer.array());
                     if(len != DUN_IPC_MDM_STATUS_MSG_SIZE)  {
-                        Log.e(TAG, "getSocketOpt return length of socket option mismatch:");
+                        Log.w(TAG, "getSocketOpt return mismatch len: " + len);
                         continue;
                     }
                 } catch (IOException ex) {
-                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                    Log.w(TAG, "Handled getSocketOpt Exception: " + ex.toString());
                 }
                 if( mdmBits != IpcMsgBuffer.get(0)) {
                     mdmBits = IpcMsgBuffer.get(0);
                     notifyModemStatus(mdmBits);
                 }
             }
-            if (VERBOSE) Log.v(TAG, "MonitorThread thread exited ");
+            Log.d(TAG, "MonitorThread thread exited");
         }
 
         void shutdown() {
@@ -1127,40 +1121,39 @@
         @Override
         public void run() {
 
-            if (mDundSocket != null) {
-                if ( mDundSocket.isConnected())
-                    try {
-                        mDundInputStream = mDundSocket.getInputStream();
-                    } catch (IOException ex) {
-                        if (VERBOSE) Log.v(TAG, "mDundInputStream exception: " + ex.toString());
-                    }
-                else return;
+            if (mDundSocket != null && mDundSocket.isConnected()) {
+                try {
+                    mDundInputStream = mDundSocket.getInputStream();
+                } catch (IOException ex) {
+                    Log.w(TAG, "Handled mDundInputStream exception: " + ex.toString());
+                }
+            } else {
+                Log.w(TAG, "DownlinkThread: Dund Socket is not connected: " +  mDundSocket);
+                return;
             }
-            else return;
 
-            if (mRfcommSocket != null) {
-                if (mRfcommSocket.isConnected())
-                    try {
-                        mRfcommOutputStream = mRfcommSocket.getOutputStream();
-                    } catch (IOException ex) {
-                        if (VERBOSE) Log.v(TAG, "mRfcommOutputStream exception: " + ex.toString());
-                    }
-                else return;
+            if (mRfcommSocket != null && mRfcommSocket.isConnected()) {
+                try {
+                    mRfcommOutputStream = mRfcommSocket.getOutputStream();
+                } catch (IOException ex) {
+                    Log.w(TAG, "Handled mRfcommOutputStream exception: " + ex.toString());
+                }
+            } else {
+                Log.w(TAG, "DownlinkThread: rfcomm Socket is not connected: " +  mRfcommSocket);
+                return;
             }
-            else return;
 
             while (!stopped) {
                 try {
-                    if (VERBOSE) Log.v(TAG, "Reading the DUN responses from Dund");
                     /* Read the DUN responses from DUN server */
                     NumRead = mDundInputStream.read(IpcMsgBuffer.array(),0, DUN_MAX_IPC_MSG_LEN);
-                    if (VERBOSE) Log.v(TAG, "NumRead" + NumRead);
-                    if ( NumRead < 0) {
+                    Log.d(TAG, "Read the DUN response from Dund, NumRead: " + NumRead);
+                    if (NumRead < 0) {
                         break;
-                    }
-                    else if( NumRead != 0) {
+                    } else if(NumRead != 0) {
                         /* some times read buffer contains multiple responses */
                         do {
+                            if (VERBOSE) Log.v(TAG, "ReadIndex: " + ReadIndex);
                             /* swap bytes of message len as buffer is Big Endian */
                             TempByte = IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
 
@@ -1171,10 +1164,25 @@
 
                             MsgLen =  IpcMsgBuffer.getShort(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
 
-                            if((ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE) > NumRead) {
-                                if (VERBOSE) Log.v(TAG, "Boundary condition hit");
-                                mDundInputStream.read(IpcMsgBuffer.array(), NumRead , ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE - NumRead);
+                            /* Max DUN msg/response length including IPC header is 32767,
+                               which can only be read from the DunD socket in single read.
+                               As msg/response length is varied & single msg/response can
+                               be smaller than max length, So multiple msgs/responses are
+                               read in single read. Boundary condition hits, when max length
+                               is already read, which include multiple responses, but some of
+                               the bytes of last response still need to be read. */
 
+                            if((ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE) > NumRead) {
+                                int tempNumRead = 0;
+                                tempNumRead = mDundInputStream.read(IpcMsgBuffer.array(), NumRead,
+                                        ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE - NumRead);
+                                Log.w(TAG, "Boundary condition hit, ReadIndex: " + ReadIndex
+                                        + " MsgLen: " + MsgLen + " NumRead: " + NumRead
+                                        + " tempNumRead: " + tempNumRead);
+                                if (tempNumRead >= 0)
+                                    NumRead += tempNumRead;
+                                else
+                                    break;
                             }
 
                             if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_TYPE) == DUN_IPC_MSG_DUN_RESPONSE) {
@@ -1205,15 +1213,13 @@
                                 if (VERBOSE)
                                     Log.v(TAG, "DownlinkThread modem status message received");
                             }
-
-                            ReadIndex = ReadIndex + IpcMsgBuffer.getShort(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
-                            ReadIndex = ReadIndex + DUN_IPC_HEADER_SIZE;
-                        } while(ReadIndex < NumRead);
+                            ReadIndex = ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE;
+                        } while(ReadIndex > 0 && ReadIndex < NumRead);
                     }
 
                 } catch (IOException ex) {
                     stopped = true;
-                    if (VERBOSE) Log.v(TAG, "Dund Read exception: " + ex.toString());
+                    Log.w(TAG, "Handled Dund Read exception: " + ex.toString());
                 }
                 /* reset the ReadIndex */
                 ReadIndex = 0;
@@ -1223,7 +1229,7 @@
             /* close the rfcomm socket */
             closeRfcommSocket();
 
-            if (VERBOSE) Log.v(TAG, "downlink thread exited ");
+            Log.d(TAG, "downlink thread exited ");
         }
 
         void shutdown() {
@@ -1239,16 +1245,16 @@
         int WriteLen = DUN_IPC_HEADER_SIZE + DUN_IPC_CTRL_MSG_SIZE;
         ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
 
-        if (mDundSocket != null) {
-            if ( mDundSocket.isConnected())
-                try {
-                    mDundOutputStream = mDundSocket.getOutputStream();
-                } catch (IOException ex) {
-                    if (VERBOSE) Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
-                }
-            else return false;
+        if (mDundSocket != null && mDundSocket.isConnected()) {
+            try {
+                mDundOutputStream = mDundSocket.getOutputStream();
+            } catch (IOException ex) {
+                Log.w(TAG, "disconnect: Handled mDundOutputStream exception: " + ex.toString());
+            }
+        } else {
+            Log.w(TAG, "disconnect: Dund Socket is not connected: " +  mDundSocket);
+            return false;
         }
-        else return false;
 
         IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_CTRL_REQUEST);
         IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN,DUN_IPC_CTRL_MSG_SIZE);
@@ -1256,7 +1262,7 @@
         try {
             mDundOutputStream.write(IpcMsgBuffer.array(), 0, WriteLen);
         } catch (IOException ex) {
-            if (VERBOSE) Log.v(TAG, "mDundOutputStream wrtie exception: " + ex.toString());
+            Log.w(TAG, "disconnect: Handled mDundOutputStream write exception: " + ex.toString());
         }
         return true;
     }
@@ -1266,18 +1272,18 @@
         OutputStream mDundOutputStream = null;
         int WriteLen = DUN_IPC_HEADER_SIZE + DUN_IPC_MDM_STATUS_MSG_SIZE;
         ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
-        if (DBG) Log.d(TAG, "notifyModemStatus: status: " + status);
+        Log.d(TAG, "notifyModemStatus: status: " + status);
 
-        if (mDundSocket != null) {
-            if ( mDundSocket.isConnected())
-                try {
-                    mDundOutputStream = mDundSocket.getOutputStream();
-                } catch (IOException ex) {
-                    if (VERBOSE) Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
-                }
-            else return false;
+        if (mDundSocket != null && mDundSocket.isConnected()) {
+            try {
+                mDundOutputStream = mDundSocket.getOutputStream();
+            } catch (IOException ex) {
+                Log.w(TAG, "notifyModemStatus: mDundOutputStream exception: " + ex.toString());
+            }
+        } else {
+            Log.w(TAG, "notifyModemStatus: Dund Socket is not connected: " +  mDundSocket);
+            return false;
         }
-        else return false;
 
         IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_MDM_STATUS);
         IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN,DUN_IPC_MDM_STATUS_MSG_SIZE);
@@ -1285,7 +1291,7 @@
         try {
             mDundOutputStream.write(IpcMsgBuffer.array(), 0, WriteLen);
         } catch (IOException ex) {
-            if (VERBOSE) Log.v(TAG, "mDundOutputStream wrtie exception: " + ex.toString());
+            Log.e(TAG, "Handled mDundOutputStream write exception: " + ex.toString());
         }
         return true;
     }
@@ -1310,7 +1316,6 @@
     }
 
     private final void handleDunDeviceStateChange(BluetoothDevice device, int state) {
-        if (DBG) Log.d(TAG, "handleDunDeviceStateChange: device: " + device + " state: " + state);
         int prevState;
         BluetoothDunDevice dunDevice = mDunDevices.get(device);
         if (dunDevice == null) {
@@ -1318,7 +1323,8 @@
         } else {
             prevState = dunDevice.mState;
         }
-        Log.d(TAG, "handleDunDeviceStateChange preState: " + prevState + " state: " + state);
+        Log.d(TAG, "handleDunDeviceStateChange device: " + device + " preState: " + prevState
+                + " state: " + state);
         if (prevState == state) return;
 
         if (dunDevice == null) {
@@ -1332,8 +1338,6 @@
            the intent for connection state change, as it was causing a race
            condition, with the UI not being updated with the correct connection
            state. */
-        Log.d(TAG, "Dun Device state : device: " + device + " State:" +
-                       prevState + "->" + state);
         notifyProfileConnectionStateChanged(device, BluetoothProfile.DUN, state, prevState);
         Intent intent = new Intent(BluetoothDun.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
@@ -1348,30 +1352,31 @@
         int WriteLen = DUN_IPC_MDM_STATUS_MSG_SIZE;
         ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
 
-        if (DBG) Log.d(TAG, "handleModemStatusChange  status: " + status);
+        if (VERBOSE) Log.v(TAG, "handleModemStatusChange  status: " + status);
 
         if ((mRfcommSocket == null) || (!mRfcommSocket.isConnected())) {
+            Log.w(TAG, "rfcomm socket is not connected" + mRfcommSocket);
             return;
         }
         if(mRmtMdmStatus != status) {
             mdmBits = (byte)((~(int)mRmtMdmStatus) & ((int)mRmtMdmStatus ^ (int)status));
-            if (DBG) Log.d(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
+            if (VERBOSE) Log.v(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
             if(mdmBits > 0) {
                 IpcMsgBuffer.put(0, mdmBits);
                 try {
                     mRfcommSocket.setSocketOpt(BTSOCK_OPT_SET_MODEM_BITS, IpcMsgBuffer.array(), WriteLen);
                 } catch (IOException ex) {
-                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                    Log.w(TAG, "Handled getSocketOpt Exception: " + ex.toString());
                 }
             }
             mdmBits = (byte)(((int)mRmtMdmStatus) & ((int)mRmtMdmStatus ^ (int)status));
-            if (DBG) Log.d(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
+            if (VERBOSE) Log.v(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
             if(mdmBits > 0) {
                 IpcMsgBuffer.put(0, mdmBits);
                 try {
                     mRfcommSocket.setSocketOpt(BTSOCK_OPT_CLR_MODEM_BITS, IpcMsgBuffer.array(), WriteLen);
                 } catch (IOException ex) {
-                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                     Log.w(TAG, "Handled getSocketOpt Exception: " + ex.toString());
                 }
             }
             mRmtMdmStatus = status;