Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 283a15c5 authored by Hemant Gupta's avatar Hemant Gupta Committed by Andre Eisenbach
Browse files

PBAP: Add support for PBAP 1.2

Add changes required to do the SDP registration as
supported by 1.2, L2CAP related functionality and counter
related changes to support PBAP 1.2.

Test: Connected with Remote PBAP Client supporting PBAP 1.2 and
      verified that connection and transfer happens over L2CAP.
      Connected with Remote PBAP Client supporting PBAP 1.1 and
      and verified that connection and transfer happens over RFCOMM.
      TestTracker: 89053 

Bug: 33011817
Change-Id: Iddace24f43a7618ccbe6a9b49bcab88dbaae0e06
(cherry picked from commit 490373c2)
parent cdcc6f09
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -241,9 +241,12 @@ public class ObexServerSockets {
     * Signal to the {@link IObexConnectionHandler} that an error have occurred.
     */
    synchronized private void onAcceptFailed() {
        Log.w(TAG,"onAcceptFailed() calling shutdown...");
        mConHandler.onAcceptFailed();
        shutdown(false);
        BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
        if ((mAdapter != null) && (mAdapter.getState() == BluetoothAdapter.STATE_ON)) {
            Log.d(TAG, "onAcceptFailed() calling shutdown...");
            mConHandler.onAcceptFailed();
        }
    }

    /**
+317 −73

File changed.

Preview size limit exceeded, changes collapsed.

+202 −64
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@

package com.android.bluetooth.pbap;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -45,8 +46,11 @@ import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothPbap;
import android.database.sqlite.SQLiteException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
@@ -58,16 +62,22 @@ import android.text.TextUtils;
import android.util.Log;

import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.R;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
import com.android.bluetooth.IObexConnectionHandler;
import com.android.bluetooth.ObexServerSockets;
import com.android.bluetooth.R;
import com.android.bluetooth.sdp.SdpManager;
import com.android.bluetooth.Utils;
import com.android.bluetooth.util.DevicePolicyUtils;

import java.io.IOException;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;

import javax.obex.ServerSession;

public class BluetoothPbapService extends ProfileService {
public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler {
    private static final String TAG = "BluetoothPbapService";

    /**
@@ -136,7 +146,6 @@ public class BluetoothPbapService extends ProfileService {

    private static final int AUTH_TIMEOUT = 3;


    private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;

    private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
@@ -150,10 +159,6 @@ public class BluetoothPbapService extends ProfileService {

    private PowerManager.WakeLock mWakeLock = null;

    private BluetoothAdapter mAdapter;

    private SocketAcceptThread mAcceptThread = null;

    private BluetoothPbapAuthenticator mAuth = null;

    private BluetoothPbapObexServer mPbapServer;
@@ -172,16 +177,58 @@ public class BluetoothPbapService extends ProfileService {

    private static String sRemoteDeviceName = null;

    private boolean mHasStarted = false;

    private volatile boolean mInterrupted;

    private int mState;

    private int mStartId = -1;

    private boolean mIsWaitingAuthorization = false;
    private boolean mIsRegistered = false;

    private ObexServerSockets mServerSockets = null;

    private static final int SDP_PBAP_SERVER_VERSION = 0x0102;

    private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0003;

    private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F;

    private AlarmManager mAlarmManager = null;

    private int mSdpHandle = -1;

    private boolean mRemoveTimeoutMsg = false;

    private int mPermission = BluetoothDevice.ACCESS_UNKNOWN;

    private boolean mSdpSearchInitiated = false;

    private static AtomicLong mDbIndetifier = new AtomicLong();

    static long primaryVersionCounter = 0;

    static long secondaryVersionCounter = 0;

    private boolean isRegisteredObserver = false;

    private static final int SHUTDOWN = 4;

    // package and class name to which we send intent to check phone book access permission
    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
    private static final String ACCESS_AUTHORITY_CLASS =
            "com.android.settings.bluetooth.BluetoothPermissionRequest";

    private class BluetoothPbapContentObserver extends ContentObserver {
        public BluetoothPbapContentObserver() {
            super(new Handler());
        }

        @Override
        public void onChange(boolean selfChange) {
            Log.d(TAG, " onChange on contact uri ");
            primaryVersionCounter++;
        }
    }

    private BluetoothPbapContentObserver mContactChangeObserver;

    public BluetoothPbapService() {
        mState = BluetoothPbap.STATE_DISCONNECTED;
@@ -190,12 +237,10 @@ public class BluetoothPbapService extends ProfileService {
    // process the intent from receiver
    private void parseIntent(final Intent intent) {
        String action = intent.getAction();
        if (DEBUG) Log.d(TAG, "action: " + action);
        if (action == null) return;             // Nothing to do
        if (VERBOSE) Log.v(TAG, "action: " + action);

        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
        if (VERBOSE) Log.v(TAG, "state: " + state);

        if (DEBUG) Log.d(TAG, "state: " + state);
        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
            if (state == BluetoothAdapter.STATE_TURNING_OFF) {
                // Send any pending timeout now, as this service will be destroyed.
@@ -289,16 +334,6 @@ public class BluetoothPbapService extends ProfileService {
        }
    };

    private void startRfcommSocketListener() {
        if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");

        if (mAcceptThread == null) {
            mAcceptThread = new SocketAcceptThread();
            mAcceptThread.setName("BluetoothPbapAcceptThread");
            mAcceptThread.start();
        }
    }

    private final boolean initSocket() {
        if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");

@@ -383,18 +418,6 @@ public class BluetoothPbapService extends ProfileService {

        // exit initSocket early
        mInterrupted = true;
        closeServerSocket();

        if (mAcceptThread != null) {
            try {
                mAcceptThread.shutdown();
                mAcceptThread.join();
                mAcceptThread = null;
            } catch (InterruptedException ex) {
                Log.w(TAG, "mAcceptThread close error" + ex);
            }
        }

        if (mWakeLock != null) {
            mWakeLock.release();
            mWakeLock = null;
@@ -406,12 +429,8 @@ public class BluetoothPbapService extends ProfileService {
        }

        closeConnectionSocket();

        mHasStarted = false;
        if (mStartId != -1 && stopSelfResult(mStartId)) {
            if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
            mStartId = -1;
        }
        closeServerSocket();
        if (mSessionStatusHandler != null) mSessionStatusHandler.removeCallbacksAndMessages(null);
        if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
    }

@@ -469,15 +488,12 @@ public class BluetoothPbapService extends ProfileService {
            mServerSession.close();
            mServerSession = null;
        }

        mAcceptThread = null;

        closeConnectionSocket();

        // Last obex transaction is finished, we start to listen for incoming
        // connection again
        if (mAdapter.isEnabled()) {
            startRfcommSocketListener();
            startSocketListeners();
        }
        setState(BluetoothPbap.STATE_DISCONNECTED);
    }
@@ -618,7 +634,7 @@ public class BluetoothPbapService extends ProfileService {
            switch (msg.what) {
                case START_LISTENER:
                    if (mAdapter.isEnabled()) {
                        startRfcommSocketListener();
                        startSocketListeners();
                    }
                    break;
                case USER_TIMEOUT:
@@ -671,6 +687,9 @@ public class BluetoothPbapService extends ProfileService {
                        Log.w(TAG, "Release Wake Lock");
                    }
                    break;
                case SHUTDOWN:
                    closeService();
                    break;
                default:
                    break;
            }
@@ -778,37 +797,34 @@ public class BluetoothPbapService extends ProfileService {
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        filter.addAction(AUTH_RESPONSE_ACTION);
        filter.addAction(AUTH_CANCELLED_ACTION);

        try {
            registerReceiver(mPbapReceiver, filter);
            mIsRegistered = true;
        } catch (Exception e) {
            Log.w(TAG, "Unable to register pbap receiver", e);
        }
        mInterrupted = false;
        BluetoothPbapConfig.init(this);
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
        if (mContactChangeObserver == null) {
            registerReceiver(mPbapReceiver, filter);
            mContactChangeObserver = new BluetoothPbapContentObserver();
            getContentResolver().registerContentObserver(
                    DevicePolicyUtils.getEnterprisePhoneUri(this), false, mContactChangeObserver);
        }
        return true;
    }

    @Override
    protected boolean stop() {
        Log.v(TAG, "stop()");
        if (!mIsRegistered) {
        if (mContactChangeObserver == null) {
            Log.i(TAG, "Avoid unregister when receiver it is not registered");
            return true;
        }
        try {
            mIsRegistered = false;
            unregisterReceiver(mPbapReceiver);
            getContentResolver().unregisterContentObserver(mContactChangeObserver);
            mContactChangeObserver = null;
        } catch (Exception e) {
            Log.w(TAG, "Unable to unregister pbap receiver", e);
        }
        mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
        setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
        closeService();
        if (mSessionStatusHandler != null) {
            mSessionStatusHandler.removeCallbacksAndMessages(null);
        }
        return true;
    }

@@ -888,4 +904,126 @@ public class BluetoothPbapService extends ProfileService {
            service.disconnect();
        }
    }

    synchronized private void startSocketListeners() {
        if (DEBUG) Log.d(TAG, "startsocketListener");
        if (mServerSession != null) {
            if (DEBUG) Log.d(TAG, "mServerSession exists - shutting it down...");
            mServerSession.close();
            mServerSession = null;
        }
        closeConnectionSocket();
        if (mServerSockets != null) {
            mServerSockets.prepareForNewConnect();
        } else {
            mServerSockets = ObexServerSockets.create(this);
            if (mServerSockets == null) {
                // TODO: Handle - was not handled before
                Log.e(TAG, "Failed to start the listeners");
                return;
            }
            SdpManager sdpManager = SdpManager.getDefaultManager();
            if (sdpManager == null) {
                Log.e(TAG, "Failed to start the listeners sdp null ");
                return;
            }
            if (mAdapter != null && mSdpHandle >= 0) {
                Log.d(TAG, "Removing SDP record for PBAP with SDP handle:" + mSdpHandle);
                boolean status = sdpManager.removeSdpRecord(mSdpHandle);
                Log.d(TAG, "RemoveSDPrecord returns " + status);
                mSdpHandle = -1;
            }
            mSdpHandle = SdpManager.getDefaultManager().createPbapPseRecord(
                    "OBEX Phonebook Access Server", mServerSockets.getRfcommChannel(),
                    mServerSockets.getL2capPsm(), SDP_PBAP_SERVER_VERSION,
                    SDP_PBAP_SUPPORTED_REPOSITORIES, SDP_PBAP_SUPPORTED_FEATURES);
            /* Here we might have changed crucial data, hence reset DB identifier */
            updateDbIdentifier();
            if (DEBUG) Log.d(TAG, "PBAP server with handle:" + mSdpHandle);
        }
    }

    private void updateDbIdentifier() {
        mDbIndetifier.set(Calendar.getInstance().getTime().getTime());
    }

    long getDbIdentifier() {
        return mDbIndetifier.get();
    }

    private void setUserTimeoutAlarm() {
        if (DEBUG) Log.d(TAG, "SetUserTimeOutAlarm()");
        if (mAlarmManager == null) {
            mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
        }
        mRemoveTimeoutMsg = true;
        Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
        PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent);
    }

    @Override
    public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
        mRemoteDevice = remoteDevice;
        if (mRemoteDevice == null || socket == null) {
            Log.i(TAG, "mRemoteDevice :" + mRemoteDevice + " socket :" + socket);
            return false;
        }
        mConnSocket = socket;
        sRemoteDeviceName = mRemoteDevice.getName();
        // In case getRemoteName failed and return null
        if (TextUtils.isEmpty(sRemoteDeviceName)) {
            sRemoteDeviceName = getString(R.string.defaultname);
        }
        int permission = mRemoteDevice.getPhonebookAccessPermission();
        if (DEBUG) Log.d(TAG, "getPhonebookAccessPermission() = " + permission);

        if (permission == BluetoothDevice.ACCESS_ALLOWED) {
            try {
                startObexServerSession();
            } catch (IOException ex) {
                Log.e(TAG, "Caught exception starting obex server session" + ex.toString());
            }
        } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
            if (DEBUG) {
                Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName
                                + " automatically as already rejected device");
            }
            return false;
        } else { // permission == BluetoothDevice.ACCESS_UNKNOWN
            // Send an Intent to Settings app to ask user preference.
            Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
            intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
            intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
                    BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
            intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
            mIsWaitingAuthorization = true;
            sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
            if (VERBOSE)
                Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName);
            /* In case car kit time out and try to use HFP for phonebook
             * access, while UI still there waiting for user to confirm */
            mSessionStatusHandler.sendMessageDelayed(
                    mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
            /* We will continue the process when we receive
             * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */
        }
        return true;
    };

    /**
     * Called when an unrecoverable error occurred in an accept thread.
     * Close down the server socket, and restart.
     * TODO: Change to message, to call start in correct context.
     */
    @Override
    public synchronized void onAcceptFailed() {
        // Force socket listener to restart
        mServerSockets = null;
        if (!mInterrupted && mAdapter != null && mAdapter.isEnabled()) {
            startSocketListeners();
        }
    }
}
+405 −28

File changed.

Preview size limit exceeded, changes collapsed.