@SmallTest
  public void testParser() {
    byte[] scanRecord =
        new byte[] {
          0x02,
          0x01,
          0x1a, // advertising flags
          0x05,
          0x02,
          0x0b,
          0x11,
          0x0a,
          0x11, // 16 bit service uuids
          0x04,
          0x09,
          0x50,
          0x65,
          0x64, // name
          0x02,
          0x0A,
          (byte) 0xec, // tx power level
          0x05,
          0x16,
          0x0b,
          0x11,
          0x50,
          0x64, // service data
          0x05,
          (byte) 0xff,
          (byte) 0xe0,
          0x00,
          0x02,
          0x15, // manufacturer specific data
          0x03,
          0x50,
          0x01,
          0x02, // an unknown data type won't cause trouble
        };
    ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
    assertEquals(0x1a, data.getAdvertiseFlags());
    ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
    ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
    assertTrue(data.getServiceUuids().contains(uuid1));
    assertTrue(data.getServiceUuids().contains(uuid2));

    assertEquals("Ped", data.getDeviceName());
    assertEquals(-20, data.getTxPowerLevel());

    assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null);
    assertArrayEquals(new byte[] {0x02, 0x15}, data.getManufacturerSpecificData().get(0x00E0));

    assertTrue(data.getServiceData().containsKey(uuid2));
    assertArrayEquals(new byte[] {0x50, 0x64}, data.getServiceData().get(uuid2));
  }
  public ParcelUuid[] getCharacteristicUuids() {

    ArrayList<ParcelUuid> uuidList = new ArrayList<ParcelUuid>();

    if (!discoveryDone) mHelper.waitDiscoveryDone();

    if (characteristicPaths == null) return null;

    int count = characteristicPaths.length;

    for (int i = 0; i < count; i++) {

      String value = getCharacteristicProperty(characteristicPaths[i], "UUID");

      if (value != null) uuidList.add(ParcelUuid.fromString(value));

      Log.d(TAG, "Characteristic UUID: " + value);
    }

    ParcelUuid[] uuids = new ParcelUuid[count];

    uuidList.toArray(uuids);

    return uuids;
  }
Exemple #3
0
  @Override
  public void onScanResult(int callbackType, ScanResult result) {
    System.out.println("onScanResult()");
    System.out.println("  Name: " + result.getDevice().getName());

    if (result.getScanRecord() != null && result.getDevice() != null) {
      System.out.println("  DeviceName: " + result.getScanRecord().getDeviceName());
      System.out.println("  toString: " + result.toString());
      System.out.println("  address: " + result.getDevice().getAddress());
      if (result.getDevice().getUuids() != null) {
        System.out.println("  UUIDs:");
        for (ParcelUuid id : result.getDevice().getUuids())
          System.out.println("   - " + id.toString());
      }
    }

    BluetoothDevice scanDevice = result.getDevice();
    // System.out.println("  started service discovery: " + scanDevice.fetchUuidsWithSdp());
  }
  public ParcelUuid getCharacteristicUuid(String path) {

    ParcelUuid uuid = null;

    if (!discoveryDone) mHelper.waitDiscoveryDone();

    String value = getCharacteristicProperty(path, "UUID");

    if (value != null) {
      uuid = ParcelUuid.fromString(value);

      Log.d(TAG, "Characteristic UUID: " + value);
    }
    return uuid;
  }
  private static byte[] constructEIR(
      String namestr, byte[] btmac, ParcelUuid[] uuids, byte[] bclass) {

    byte[] name = namestr.getBytes();

    Vector<Byte> eir = new Vector<Byte>();

    // Placeholder for length
    eir.addElement((byte) 0x0);
    eir.addElement((byte) 0x0);

    // byte order for the bluetooth is reversed
    for (byte b : btmac) eir.add(2, b);

    // Extended Inquiry Response (EIR) Data Length:
    // Length inkl type field
    eir.add((byte) (name.length + 1));
    // EIR Data Type: Complete Local Name
    eir.add((byte) 0x09);
    // Name
    for (byte b : name) eir.add(b);

    // EIR rec lenght 4 byte
    eir.add((byte) 4);
    // EIR Data Type: Class of Device
    eir.add((byte) 0x0D);

    // TODO: Get this from the Bluetooth Class
    // Class of Device:
    // 0x20: Service Class = Audio
    // 0x04: Major Device Class = Audio and Video
    // 0x14: Minor Device Class = Loudspeaker

    for (byte b : new byte[] {0x14, 0x04, 0x24}) eir.add(b);

    // Sony: 0x14 0x04 0x24

    // Length 2 byte for each uuid + 1 for record type
    eir.add((byte) (uuids.length * 2 + 1));
    // EIR Data Type: 16 bit Service Class UUID list (complete)
    eir.add((byte) 0x03);

    // 16 bit Service Class UUID list (complete): 0x110D = A2D2P
    // 0x110B = A2DP (Audio Sink)

    for (ParcelUuid p : uuids) {
      UUID u = p.getUuid();
      String foo = u.toString();
      System.out.println(foo);

      // Evil, evil, evil
      long upperbits = u.getMostSignificantBits();

      long highbyte = (upperbits >> (5 * 8)) & 0xff;
      long lowbyte = (upperbits >> (4 * 8)) & 0xff;

      eir.add((byte) lowbyte);
      eir.add((byte) highbyte);
    }

    // Sony: 0x1108, 0x110B, 0x110c, 0x110d, 0x110e, 0x111e, 0x1113, 0x1200

    byte[] bytes = new byte[eir.size()];

    // Length
    bytes[0] = (byte) (eir.size() % 256);
    bytes[1] = (byte) (eir.size() / 256);

    for (int i = 2; i < bytes.length; i++) {
      bytes[i] = eir.get(i);
    }
    return bytes;
  }
Exemple #6
0
    /** Creates and discovers a new device. */
    @CalledByNative("FakeBluetoothAdapter")
    public void simulateLowEnergyDevice(int deviceOrdinal) {
      if (mFakeScanner == null) {
        return;
      }

      switch (deviceOrdinal) {
        case 1:
          {
            ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2);
            uuids.add(ParcelUuid.fromString("00001800-0000-1000-8000-00805f9b34fb"));
            uuids.add(ParcelUuid.fromString("00001801-0000-1000-8000-00805f9b34fb"));

            mFakeScanner.mScanCallback.onScanResult(
                ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
                new FakeScanResult(
                    new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", "FakeBluetoothDevice"),
                    uuids));
            break;
          }
        case 2:
          {
            ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2);
            uuids.add(ParcelUuid.fromString("00001802-0000-1000-8000-00805f9b34fb"));
            uuids.add(ParcelUuid.fromString("00001803-0000-1000-8000-00805f9b34fb"));

            mFakeScanner.mScanCallback.onScanResult(
                ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
                new FakeScanResult(
                    new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", "FakeBluetoothDevice"),
                    uuids));
            break;
          }
        case 3:
          {
            ArrayList<ParcelUuid> uuids = null;
            mFakeScanner.mScanCallback.onScanResult(
                ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
                new FakeScanResult(new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", ""), uuids));

            break;
          }
        case 4:
          {
            ArrayList<ParcelUuid> uuids = null;
            mFakeScanner.mScanCallback.onScanResult(
                ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
                new FakeScanResult(new FakeBluetoothDevice(this, "02:00:00:8B:74:63", ""), uuids));

            break;
          }
        case 5:
          {
            ArrayList<ParcelUuid> uuids = null;
            mFakeScanner.mScanCallback.onScanResult(
                ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
                new FakeScanResult(
                    new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", null), uuids));
            break;
          }
      }
    }
Exemple #7
0
/** This class run an MNS session. */
public class BluetoothMns implements MessageNotificationListener {
  private static final String TAG = "BtMns";

  private static final boolean V = BluetoothMasService.VERBOSE;

  public static final int RFCOMM_ERROR = 10;

  public static final int RFCOMM_CONNECTED = 11;

  public static final int MNS_CONNECT = 13;

  public static final int MNS_DISCONNECT = 14;

  public static final int MNS_SEND_EVENT = 15;

  public static final int MNS_SEND_EVENT_DONE = 16;

  public static final int MNS_SEND_TIMEOUT = 17;

  public static final int MNS_BLUETOOTH_OFF = 18;

  public static final int MNS_SEND_TIMEOUT_DURATION = 30000; // 30 secs

  private static final short MNS_UUID16 = 0x1133;

  public static final String NEW_MESSAGE = "NewMessage";

  public static final String DELIVERY_SUCCESS = "DeliverySuccess";

  public static final String SENDING_SUCCESS = "SendingSuccess";

  public static final String DELIVERY_FAILURE = "DeliveryFailure";

  public static final String SENDING_FAILURE = "SendingFailure";

  public static final String MEMORY_FULL = "MemoryFull";

  public static final String MEMORY_AVAILABLE = "MemoryAvailable";

  public static final String MESSAGE_DELETED = "MessageDeleted";

  public static final String MESSAGE_SHIFT = "MessageShift";

  private Context mContext;

  private BluetoothAdapter mAdapter;

  private BluetoothMnsObexSession mSession;

  private EventHandler mSessionHandler;

  private List<MnsClient> mMnsClients = new ArrayList<MnsClient>();
  public static final ParcelUuid BluetoothUuid_ObexMns =
      ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");

  private HashSet<Integer> mWaitingMasId = new HashSet<Integer>();
  private final Queue<Pair<Integer, String>> mEventQueue =
      new ConcurrentLinkedQueue<Pair<Integer, String>>();
  private boolean mSendingEvent = false;

  public BluetoothMns(Context context) {
    /* check Bluetooth enable status */
    /*
     * normally it's impossible to reach here if BT is disabled. Just check
     * for safety
     */

    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mContext = context;

    for (int i = 0; i < MAX_INSTANCES; i++) {
      try {
        // TODO: must be updated when Class<? extends MnsClient>'s constructor is changed
        Constructor<? extends MnsClient> constructor;
        constructor = MAS_INS_INFO[i].mMnsClientClass.getConstructor(Context.class, Integer.class);
        mMnsClients.add(constructor.newInstance(mContext, i));
      } catch (IllegalArgumentException e) {
        Log.e(
            TAG,
            "The "
                + MAS_INS_INFO[i].mMnsClientClass.getName()
                + "'s constructor arguments mismatch",
            e);
      } catch (InstantiationException e) {
        Log.e(
            TAG, "The " + MAS_INS_INFO[i].mMnsClientClass.getName() + " cannot be instantiated", e);
      } catch (IllegalAccessException e) {
        Log.e(
            TAG, "The " + MAS_INS_INFO[i].mMnsClientClass.getName() + " cannot be instantiated", e);
      } catch (InvocationTargetException e) {
        Log.e(
            TAG,
            "Exception during "
                + MAS_INS_INFO[i].mMnsClientClass.getName()
                + "'s constructor invocation",
            e);
      } catch (SecurityException e) {
        Log.e(
            TAG, MAS_INS_INFO[i].mMnsClientClass.getName() + "'s constructor is not accessible", e);
      } catch (NoSuchMethodException e) {
        Log.e(TAG, MAS_INS_INFO[i].mMnsClientClass.getName() + " has no matched constructor", e);
      }
    }

    if (!mAdapter.isEnabled()) {
      Log.e(TAG, "Can't send event when Bluetooth is disabled ");
      return;
    }

    mSessionHandler = new EventHandler();

    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
    filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
    mContext.registerReceiver(mStorageStatusReceiver, filter);
  }

  public Handler getHandler() {
    return mSessionHandler;
  }

  /**
   * Asserting masId
   *
   * @param masId
   * @return true if MnsClient is created for masId; otherwise false.
   */
  private boolean assertMasid(final int masId) {
    final int size = mMnsClients.size();
    if (masId < 0 || masId >= size) {
      Log.e(TAG, "MAS id: " + masId + " is out of maximum number of MAS instances: " + size);
      return false;
    }
    return true;
  }

  private boolean register(final int masId) {
    if (!assertMasid(masId)) {
      Log.e(TAG, "Attempt to register MAS id: " + masId);
      return false;
    }
    final MnsClient client = mMnsClients.get(masId);
    if (!client.isRegistered()) {
      try {
        client.register(BluetoothMns.this);
      } catch (Exception e) {
        Log.e(TAG, "Exception occured while register MNS for MAS id: " + masId, e);
        return false;
      }
    }
    return true;
  }

  private synchronized boolean canDisconnect() {
    for (MnsClient client : mMnsClients) {
      if (client.isRegistered()) {
        return false;
      }
    }
    return true;
  }

  private void deregister(final int masId) {
    if (!assertMasid(masId)) {
      Log.e(TAG, "Attempt to register MAS id: " + masId);
      return;
    }
    final MnsClient client = mMnsClients.get(masId);
    if (client.isRegistered()) {
      client.register(null);
    }
  }

  private void deregisterAll() {
    for (MnsClient client : mMnsClients) {
      if (client.isRegistered()) {
        client.register(null);
      }
    }
  }

  private void mnsCleanupInstances() {
    if (V) Log.v(TAG, "MNS_BT: entered mnsCleanupInstances");
    if (mStorageStatusReceiver != null) {
      mContext.unregisterReceiver(mStorageStatusReceiver);
      mStorageStatusReceiver = null;
    }
    for (MnsClient client : mMnsClients) {
      if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: inside for loop");
      if (client.isRegistered()) {
        if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: Attempt to deregister MnsClient");
        client.register(null);
        client = null;
        if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: made client = null");
      }
    }
  }

  /*
   * Receives events from mConnectThread & mSession back in the main thread.
   */
  private class EventHandler extends Handler {
    public EventHandler() {
      super();
    }

    @Override
    public void handleMessage(Message msg) {
      if (V) {
        Log.v(TAG, " Handle Message " + msg.what);
      }
      switch (msg.what) {
        case MNS_CONNECT:
          {
            final int masId = msg.arg1;
            final BluetoothDevice device = (BluetoothDevice) msg.obj;
            if (mSession != null) {
              if (V) Log.v(TAG, "is MNS session connected? " + mSession.isConnected());
              if (mSession.isConnected()) {
                if (!register(masId)) {
                  // failed to register, disconnect
                  obtainMessage(MNS_DISCONNECT, masId, -1).sendToTarget();
                }
                break;
              }
            }
            if (mWaitingMasId.isEmpty()) {
              mWaitingMasId.add(masId);
              mConnectThread = new SocketConnectThread(device);
              mConnectThread.start();
            } else {
              mWaitingMasId.add(masId);
            }
            break;
          }
        case MNS_DISCONNECT:
          {
            final int masId = msg.arg1;
            new Thread(
                    new Runnable() {
                      public void run() {
                        deregister(masId);
                        if (canDisconnect()) {
                          stop();
                        }
                      }
                    })
                .start();
            break;
          }
        case MNS_BLUETOOTH_OFF:
          if (V) Log.v(TAG, "MNS_BT: receive MNS_BLUETOOTH_OFF msg");
          new Thread(
                  new Runnable() {
                    public void run() {
                      if (V) Log.v(TAG, "MNS_BT: Started Deregister Thread");
                      if (canDisconnect()) {
                        stop();
                      }
                      mnsCleanupInstances();
                    }
                  })
              .start();
          break;
          /*
           * RFCOMM connect fail is for outbound share only! Mark batch
           * failed, and all shares in batch failed
           */
        case RFCOMM_ERROR:
          if (V) Log.v(TAG, "receive RFCOMM_ERROR msg");
          deregisterAll();
          if (canDisconnect()) {
            stop();
          }
          break;
          /*
           * RFCOMM connected. Do an OBEX connect by starting the session
           */
        case RFCOMM_CONNECTED:
          {
            if (V) Log.v(TAG, "Transfer receive RFCOMM_CONNECTED msg");
            ObexTransport transport = (ObexTransport) msg.obj;
            try {
              startObexSession(transport);
            } catch (NullPointerException ne) {
              sendEmptyMessage(RFCOMM_ERROR);
              return;
            }
            for (int masId : mWaitingMasId) {
              register(masId);
            }
            mWaitingMasId.clear();
            break;
          }
          /* Handle the error state of an Obex session */
        case BluetoothMnsObexSession.MSG_SESSION_ERROR:
          if (V) Log.v(TAG, "receive MSG_SESSION_ERROR");
          deregisterAll();
          stop();
          break;
        case MNS_SEND_EVENT:
          {
            final String xml = (String) msg.obj;
            final int masId = msg.arg1;
            if (mSendingEvent) {
              mEventQueue.add(new Pair<Integer, String>(masId, xml));
            } else {
              mSendingEvent = true;
              new Thread(new SendEventTask(xml, masId)).start();
            }
            break;
          }
        case MNS_SEND_EVENT_DONE:
          if (mEventQueue.isEmpty()) {
            mSendingEvent = false;
          } else {
            final Pair<Integer, String> p = mEventQueue.remove();
            final int masId = p.first;
            final String xml = p.second;
            new Thread(new SendEventTask(xml, masId)).start();
          }
          break;
        case MNS_SEND_TIMEOUT:
          {
            if (V) Log.v(TAG, "MNS_SEND_TIMEOUT disconnecting.");
            deregisterAll();
            stop();
            break;
          }
      }
    }

    private void setTimeout(int masId) {
      if (V) Log.v(TAG, "setTimeout MNS_SEND_TIMEOUT for instance " + masId);
      sendMessageDelayed(obtainMessage(MNS_SEND_TIMEOUT, masId, -1), MNS_SEND_TIMEOUT_DURATION);
    }

    private void removeTimeout() {
      if (hasMessages(MNS_SEND_TIMEOUT)) {
        removeMessages(MNS_SEND_TIMEOUT);
        sendEventDone();
      }
    }

    private void sendEventDone() {
      if (V) Log.v(TAG, "post MNS_SEND_EVENT_DONE");
      obtainMessage(MNS_SEND_EVENT_DONE).sendToTarget();
    }

    class SendEventTask implements Runnable {
      final String mXml;
      final int mMasId;

      SendEventTask(String xml, int masId) {
        mXml = xml;
        mMasId = masId;
      }

      public void run() {
        if (V) Log.v(TAG, "MNS_SEND_EVENT started");
        setTimeout(mMasId);
        sendEvent(mXml, mMasId);
        removeTimeout();
        if (V) Log.v(TAG, "MNS_SEND_EVENT finished");
      }
    }
  }

  /*
   * Class to hold message handle for MCE Initiated operation
   */
  public class BluetoothMnsMsgHndlMceInitOp {
    public String msgHandle;
    Time time;
  }

  /*
   * Keep track of Message Handles on which the operation was
   * initiated by MCE
   */
  List<BluetoothMnsMsgHndlMceInitOp> opList = new ArrayList<BluetoothMnsMsgHndlMceInitOp>();

  /*
   * Adds the Message Handle to the list for tracking
   * MCE initiated operation
   */
  public void addMceInitiatedOperation(String msgHandle) {
    BluetoothMnsMsgHndlMceInitOp op = new BluetoothMnsMsgHndlMceInitOp();
    op.msgHandle = msgHandle;
    op.time = new Time();
    op.time.setToNow();
    opList.add(op);
  }
  /*
   * Removes the Message Handle from the list for tracking
   * MCE initiated operation
   */
  public void removeMceInitiatedOperation(int location) {
    opList.remove(location);
  }

  /*
   * Finds the location in the list of the given msgHandle, if
   * available. "+" indicates the next (any) operation
   */
  public int findLocationMceInitiatedOperation(String msgHandle) {
    int location = -1;

    Time currentTime = new Time();
    currentTime.setToNow();

    List<BluetoothMnsMsgHndlMceInitOp> staleOpList = new ArrayList<BluetoothMnsMsgHndlMceInitOp>();
    for (BluetoothMnsMsgHndlMceInitOp op : opList) {
      if (currentTime.toMillis(false) - op.time.toMillis(false) > 10000) {
        // add stale entries
        staleOpList.add(op);
      }
    }
    if (!staleOpList.isEmpty()) {
      for (BluetoothMnsMsgHndlMceInitOp op : staleOpList) {
        // Remove stale entries
        opList.remove(op);
      }
    }

    for (BluetoothMnsMsgHndlMceInitOp op : opList) {
      if (op.msgHandle.equalsIgnoreCase(msgHandle)) {
        location = opList.indexOf(op);
        break;
      }
    }

    if (location == -1) {
      for (BluetoothMnsMsgHndlMceInitOp op : opList) {
        if (op.msgHandle.equalsIgnoreCase("+")) {
          location = opList.indexOf(op);
          break;
        }
      }
    }
    return location;
  }

  /** Post a MNS Event to the MNS thread */
  public void sendMnsEvent(
      int masId, String msg, String handle, String folder, String old_folder, String msgType) {
    if (V) {
      Log.v(TAG, "sendMnsEvent()");
      Log.v(TAG, "msg: " + msg);
      Log.v(TAG, "handle: " + handle);
      Log.v(TAG, "folder: " + folder);
      Log.v(TAG, "old_folder: " + old_folder);
      Log.v(TAG, "msgType: " + msgType);
    }
    int location = -1;

    /* Send the notification, only if it was not initiated
     * by MCE. MEMORY_FULL and MEMORY_AVAILABLE cannot be
     * MCE initiated
     */
    if (msg.equals(MEMORY_AVAILABLE) || msg.equals(MEMORY_FULL)) {
      location = -1;
    } else {
      location = findLocationMceInitiatedOperation(handle);
    }

    if (location == -1) {
      String str = MapUtils.mapEventReportXML(msg, handle, folder, old_folder, msgType);
      if (V) Log.v(TAG, "Notification to MAS " + masId + ", msgType = " + msgType);
      mSessionHandler.obtainMessage(MNS_SEND_EVENT, masId, -1, str).sendToTarget();
    } else {
      removeMceInitiatedOperation(location);
    }
  }

  /** Push the message over Obex client session */
  private void sendEvent(String str, int masId) {
    if (str != null && (str.length() > 0)) {
      if (V) {
        Log.v(TAG, "--------------");
        Log.v(TAG, " CONTENT OF EVENT REPORT FILE: " + str);
      }

      final String FILENAME = "EventReport" + masId;
      FileOutputStream fos = null;
      File file = new File(mContext.getFilesDir() + "/" + FILENAME);
      file.delete();
      try {
        fos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
        fos.write(str.getBytes());
        fos.flush();
        fos.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }

      File fileR = new File(mContext.getFilesDir() + "/" + FILENAME);
      if (fileR.exists() == true) {
        if (V) {
          Log.v(TAG, " Sending event report file for Mas " + masId);
        }
        try {
          if (mSession != null) {
            mSession.sendEvent(fileR, (byte) masId);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      } else {
        if (V) {
          Log.v(TAG, " ERROR IN CREATING SEND EVENT OBJ FILE");
        }
      }
    } else if (V) {
      Log.v(TAG, "sendEvent(null, " + masId + ")");
    }
  }

  private BroadcastReceiver mStorageStatusReceiver =
      new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
          if (intent != null && mSession != null) {
            final String action = intent.getAction();
            if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
              Log.d(TAG, " Memory Full ");
              sendMnsEventMemory(MEMORY_FULL);
            } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
              Log.d(TAG, " Memory Available ");
              sendMnsEventMemory(MEMORY_AVAILABLE);
            }
          }
        }
      };

  /** Stop the transfer */
  public void stop() {
    if (V) Log.v(TAG, "stop");
    if (mSession != null) {
      if (V) Log.v(TAG, "Stop mSession");
      mSession.disconnect();
      mSession = null;
    }
  }

  /** Connect the MNS Obex client to remote server */
  private void startObexSession(ObexTransport transport) throws NullPointerException {
    if (V) Log.v(TAG, "Create Client session with transport " + transport.toString());
    mSession = new BluetoothMnsObexSession(mContext, transport);
    mSession.connect();
  }

  private SocketConnectThread mConnectThread;
  /** This thread is used to establish rfcomm connection to remote device */
  private class SocketConnectThread extends Thread {
    private final BluetoothDevice device;

    private long timestamp;

    /* create a Rfcomm Socket */
    public SocketConnectThread(BluetoothDevice device) {
      super("Socket Connect Thread");
      this.device = device;
    }

    public void interrupt() {}

    @Override
    public void run() {
      timestamp = System.currentTimeMillis();

      BluetoothSocket btSocket = null;
      try {
        btSocket =
            device.createInsecureRfcommSocketToServiceRecord(BluetoothUuid_ObexMns.getUuid());
        btSocket.connect();
      } catch (IOException e) {
        Log.e(TAG, "BtSocket Connect error " + e.getMessage(), e);
        markConnectionFailed(btSocket);
        return;
      }

      if (V)
        Log.v(
            TAG,
            "Rfcomm socket connection attempt took "
                + (System.currentTimeMillis() - timestamp)
                + " ms");
      ObexTransport transport;
      transport = new BluetoothMnsRfcommTransport(btSocket);
      if (V) Log.v(TAG, "Send transport message " + transport.toString());

      mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
    }

    /** RFCOMM connection failed */
    private void markConnectionFailed(BluetoothSocket s) {
      try {
        if (s != null) {
          s.close();
        }
      } catch (IOException e) {
        Log.e(TAG, "Error when close socket");
      }
      mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
      return;
    }
  }

  public void sendMnsEventMemory(String msg) {
    // Sending "MemoryFull" or "MemoryAvailable" to all registered Mas Instances
    for (MnsClient client : mMnsClients) {
      if (client.isRegistered()) {
        sendMnsEvent(client.getMasId(), msg, null, null, null, null);
      }
    }
  }

  public void onDeliveryFailure(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, DELIVERY_FAILURE, handle, folder, null, msgType);
  }

  public void onDeliverySuccess(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, DELIVERY_SUCCESS, handle, folder, null, msgType);
  }

  public void onMessageShift(
      int masId, String handle, String toFolder, String fromFolder, String msgType) {
    sendMnsEvent(masId, MESSAGE_SHIFT, handle, toFolder, fromFolder, msgType);
  }

  public void onNewMessage(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, NEW_MESSAGE, handle, folder, null, msgType);
  }

  public void onSendingFailure(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, SENDING_FAILURE, handle, folder, null, msgType);
  }

  public void onSendingSuccess(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, SENDING_SUCCESS, handle, folder, null, msgType);
  }

  public void onMessageDeleted(int masId, String handle, String folder, String msgType) {
    sendMnsEvent(masId, MESSAGE_DELETED, handle, folder, null, msgType);
  }

  public abstract static class MnsClient implements MnsRegister {
    public static final String TAG = "MnsClient";
    public static final boolean V = BluetoothMasService.VERBOSE;
    protected static final String PRE_PATH = TELECOM + "/" + MSG + "/";

    protected Context mContext;
    protected MessageNotificationListener mListener = null;
    protected int mMasId;

    protected final long OFFSET_START;
    protected final long OFFSET_END;

    public MnsClient(Context context, int masId) {
      mContext = context;
      mMasId = masId;
      OFFSET_START = HANDLE_OFFSET[masId];
      OFFSET_END = HANDLE_OFFSET[masId + 1] - 1;
    }

    public synchronized void register(MessageNotificationListener listener) {
      if (V) Log.v(TAG, "MNS_BT: register entered");
      if (listener != null) {
        mListener = listener;
        registerContentObserver();
      } else {
        if (V) Log.v(TAG, "MNS_BT: register(null)");
        unregisterContentObserver();
        mListener = null;
      }
    }

    public boolean isRegistered() {
      return mListener != null;
    }

    public int getMasId() {
      return mMasId;
    }

    protected abstract void registerContentObserver();

    protected abstract void unregisterContentObserver();
  }
}