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

Commit b8c54887 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "PBAP Multi Connection"

parents 7e71c5e9 0232d99a
Loading
Loading
Loading
Loading
+88 −21
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.pbapclient;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothPbapClient;
@@ -23,15 +25,19 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.CallLog;
import android.provider.Settings;
import android.util.Log;

import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.R;
import com.android.bluetooth.Utils;

import java.lang.IllegalArgumentException;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;

/**
 * Provides Bluetooth Phone Book Access Profile Client profile.
@@ -41,7 +47,10 @@ import java.util.List;
public class PbapClientService extends ProfileService {
    private static final boolean DBG = false;
    private static final String TAG = "PbapClientService";
    private PbapClientStateMachine mPbapClientStateMachine;
    // 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();

@@ -67,9 +76,8 @@ public class PbapClientService extends ProfileService {
        } catch (Exception e) {
            Log.w(TAG,"Unable to register pbapclient receiver", e);
        }
        mPbapClientStateMachine = new PbapClientStateMachine(this, this);
        removeUncleanAccounts();
        setPbapClientService(this);
        mPbapClientStateMachine.start();
        return true;
    }

@@ -80,18 +88,47 @@ public class PbapClientService extends ProfileService {
        } catch (Exception e) {
            Log.w(TAG,"Unable to unregister pbapclient receiver", e);
        }
        if (mPbapClientStateMachine != null) {
            mPbapClientStateMachine.doQuit();
        for (PbapClientStateMachine pbapClientStateMachine : mPbapClientStateMachineMap.values()) {
            pbapClientStateMachine.doQuit();
        }
        return true;
    }

    @Override
    protected boolean cleanup() {
        removeUncleanAccounts();
        clearPbapClientService();
        return true;
    }

    void cleanupDevice(BluetoothDevice device) {
        Log.w(TAG, "Cleanup device: " + device);
        synchronized (mPbapClientStateMachineMap) {
            PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
            if (pbapClientStateMachine != null) {
                mPbapClientStateMachineMap.remove(device);
            }
        }
    }

    private void removeUncleanAccounts() {
        // Find all accounts that match the type "pbap" and delete them.
        AccountManager accountManager = AccountManager.get(this);
        Account[] accounts =
                accountManager.getAccountsByType(getString(R.string.pbap_account_type));
        Log.w(TAG, "Found " + accounts.length + " unclean accounts");
        for (Account acc : accounts) {
            Log.w(TAG, "Deleting " + acc);
            // The device ID is the name of the account.
            accountManager.removeAccountExplicitly(acc);
        }
        try {
            getContentResolver().delete(CallLog.Calls.CONTENT_URI, null, null);
        } catch (IllegalArgumentException e) {
            Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.");
        }
    }

    private class PbapBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -103,7 +140,9 @@ public class PbapClientService extends ProfileService {
                    disconnect(device);
                }
            } else if(action.equals(Intent.ACTION_USER_UNLOCKED)) {
                mPbapClientStateMachine.resumeDownload();
                for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
                    stateMachine.resumeDownload();
                }
            }
        }
    }
@@ -225,7 +264,7 @@ public class PbapClientService extends ProfileService {
    private static synchronized void setPbapClientService(PbapClientService instance) {
        if (instance != null && instance.isAvailable()) {
            if (DBG) {
                Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService);
                Log.d(TAG, "setPbapClientService(): previously set to: " + sPbapClientService);
            }
            sPbapClientService = instance;
        } else {
@@ -247,24 +286,36 @@ public class PbapClientService extends ProfileService {
        if (device == null) throw new IllegalArgumentException("Null device");
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress());
        int connectionState = mPbapClientStateMachine.getConnectionState();
        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
                connectionState == BluetoothProfile.STATE_CONNECTING) {
            Log.w(TAG,"Received connect request while already connecting/connected.");
        if (getPriority(device) <= BluetoothProfile.PRIORITY_OFF) {
            return false;
        }
        if (getPriority(device) > BluetoothProfile.PRIORITY_OFF) {
            mPbapClientStateMachine.connect(device);
        synchronized (mPbapClientStateMachineMap) {
            PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
            if (pbapClientStateMachine == null
                    && mPbapClientStateMachineMap.size() < MAXIMUM_DEVICES) {
                pbapClientStateMachine = new PbapClientStateMachine(this, device);
                pbapClientStateMachine.start();
                mPbapClientStateMachineMap.put(device, pbapClientStateMachine);
                return true;
        }
            } else {
                Log.w(TAG, "Received connect request while already connecting/connected.");
                return false;
            }
        }
    }

    boolean disconnect(BluetoothDevice device) {
        if (device == null) throw new IllegalArgumentException("Null device");
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        mPbapClientStateMachine.disconnect(device);
        PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
        if (pbapClientStateMachine != null) {
            pbapClientStateMachine.disconnect(device);
            return true;

        } else {
            Log.w(TAG, "disconnect() called on unconnected device.");
            return false;
        }
    }

    public List<BluetoothDevice> getConnectedDevices() {
@@ -275,13 +326,29 @@ public class PbapClientService extends ProfileService {

    private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        return mPbapClientStateMachine.getDevicesMatchingConnectionStates(states);
        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(0);
        for (Map.Entry<BluetoothDevice, PbapClientStateMachine> stateMachineEntry :
                mPbapClientStateMachineMap.entrySet()) {
            int currentDeviceState = stateMachineEntry.getValue().getConnectionState();
            for (int state : states) {
                if (currentDeviceState == state) {
                    deviceList.add(stateMachineEntry.getKey());
                    break;
                }
            }
        }
        return deviceList;
    }

    int getConnectionState(BluetoothDevice device) {
        if (device == null) throw new IllegalArgumentException("Null device");
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        return mPbapClientStateMachine.getConnectionState(device);
        PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
        if (pbapClientStateMachine == null) {
            return BluetoothProfile.STATE_DISCONNECTED;
        } else {
            return pbapClientStateMachine.getConnectionState(device);
        }
    }

    public boolean setPriority(BluetoothDevice device, int priority) {
@@ -308,8 +375,8 @@ public class PbapClientService extends ProfileService {
    @Override
    public void dump(StringBuilder sb) {
        super.dump(sb);
        if (mPbapClientStateMachine != null) {
            mPbapClientStateMachine.dump(sb);
        for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
            stateMachine.dump(sb);
        }
    }
}
+24 −100
Original line number Diff line number Diff line
@@ -41,8 +41,6 @@
 */
package com.android.bluetooth.pbapclient;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothPbapClient;
@@ -56,7 +54,6 @@ import android.os.Message;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.UserManager;
import android.provider.CallLog;
import android.util.Log;

import com.android.bluetooth.btservice.ProfileService;
@@ -65,7 +62,6 @@ import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;

import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;

@@ -74,7 +70,6 @@ final class PbapClientStateMachine extends StateMachine {
    private static final String TAG = "PbapClientStateMachine";

    // Messages for handling connect/disconnect requests.
    private static final int MSG_CONNECT = 1;
    private static final int MSG_DISCONNECT = 2;
    private static final int MSG_SDP_COMPLETE = 9;

@@ -98,9 +93,8 @@ final class PbapClientStateMachine extends StateMachine {
    private State mDisconnecting;

    // mCurrentDevice may only be changed in Disconnected State.
    private BluetoothDevice mCurrentDevice = null;
    private final BluetoothDevice mCurrentDevice;
    private PbapClientService mService;
    private Context mContext;
    private PbapClientConnectionHandler mConnectionHandler;
    private HandlerThread mHandlerThread = null;
    private UserManager mUserManager = null;
@@ -108,25 +102,24 @@ final class PbapClientStateMachine extends StateMachine {
    // mMostRecentState maintains previous state for broadcasting transitions.
    private int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;

    PbapClientStateMachine(PbapClientService svc, Context context) {
    PbapClientStateMachine(PbapClientService svc, BluetoothDevice device) {
        super(TAG);

        mService = svc;
        mContext = context;
        mCurrentDevice = device;
        mLock = new Object();
        mUserManager = UserManager.get(mContext);
        mUserManager = UserManager.get(mService);
        mDisconnected = new Disconnected();
        mConnecting = new Connecting();
        mDisconnecting = new Disconnecting();
        mConnected = new Connected();
        removeUncleanAccounts();

        addState(mDisconnected);
        addState(mConnecting);
        addState(mDisconnecting);
        addState(mConnected);

        setInitialState(mDisconnected);
        setInitialState(mConnecting);
    }

    class Disconnected extends State {
@@ -136,44 +129,7 @@ final class PbapClientStateMachine extends StateMachine {
            onConnectionStateChanged(mCurrentDevice, mMostRecentState,
                    BluetoothProfile.STATE_DISCONNECTED);
            mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
            synchronized (mLock) {
                mCurrentDevice = null;
            }

        }

        @Override
        public boolean processMessage(Message message) {
            if (DBG) Log.d(TAG, "Processing MSG " + message.what + " from " + this.getName());
            switch (message.what) {
                case MSG_CONNECT:
                    if (message.obj instanceof BluetoothDevice) {
                        synchronized (mLock) {
                            mCurrentDevice = (BluetoothDevice) message.obj;
                        }
                        transitionTo(mConnecting);
                    } else {
                        Log.w(TAG, "Received CONNECT without valid device");
                        throw new IllegalStateException("invalid device");
                    }
                    break;

                case MSG_DISCONNECT:
                    Log.w(TAG, "Received unexpected disconnect while disconnected.");
                    // It is possible if something crashed for others to think we are connected
                    // already, just remind them.
                    if (message.obj instanceof BluetoothDevice) {
                        onConnectionStateChanged((BluetoothDevice) message.obj,
                                BluetoothProfile.STATE_DISCONNECTED,
                                BluetoothProfile.STATE_DISCONNECTED);
                    }
                    break;

                default:
                    Log.w(TAG, "Received unexpected message while disconnected.");
                    return NOT_HANDLED;
            }
            return HANDLED;
            quit();
        }
    }

@@ -197,9 +153,10 @@ final class PbapClientStateMachine extends StateMachine {
            mHandlerThread.start();
            mConnectionHandler = new PbapClientConnectionHandler.Builder()
                                         .setLooper(mHandlerThread.getLooper())
                    .setContext(mContext)
                                         .setContext(mService)
                                         .setClientSM(PbapClientStateMachine.this)
                    .setRemoteDevice(mCurrentDevice).build();
                                         .setRemoteDevice(mCurrentDevice)
                                         .build();

            sendMessageDelayed(MSG_CONNECT_TIMEOUT, CONNECT_TIMEOUT);
        }
@@ -209,8 +166,8 @@ final class PbapClientStateMachine extends StateMachine {
            if (DBG) Log.d(TAG, "Processing MSG " + message.what + " from " + this.getName());
            switch (message.what) {
                case MSG_DISCONNECT:
                    if (message.obj instanceof BluetoothDevice &&
                            ((BluetoothDevice) message.obj).equals(mCurrentDevice)) {
                    if (message.obj instanceof BluetoothDevice
                            && message.obj.equals(mCurrentDevice)) {
                        removeMessages(MSG_CONNECT_TIMEOUT);
                        transitionTo(mDisconnecting);
                    }
@@ -227,10 +184,6 @@ final class PbapClientStateMachine extends StateMachine {
                    transitionTo(mDisconnecting);
                    break;

                case MSG_CONNECT:
                    Log.w(TAG, "Connecting already in progress");
                    break;

                case MSG_SDP_COMPLETE:
                    mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_CONNECT,
                            message.obj).sendToTarget();
@@ -275,11 +228,11 @@ final class PbapClientStateMachine extends StateMachine {
            public void register() {
                IntentFilter filter = new IntentFilter();
                filter.addAction(BluetoothDevice.ACTION_SDP_RECORD);
                mContext.registerReceiver(this, filter);
                mService.registerReceiver(this, filter);
            }

            public void unregister() {
                mContext.unregisterReceiver(this);
                mService.unregisterReceiver(this);
            }
        }
    }
@@ -306,7 +259,6 @@ final class PbapClientStateMachine extends StateMachine {
                    transitionTo(mDisconnected);
                    break;

                case MSG_CONNECT:
                case MSG_DISCONNECT:
                    deferMessage(message);
                    break;
@@ -345,14 +297,6 @@ final class PbapClientStateMachine extends StateMachine {
        public boolean processMessage(Message message) {
            if (DBG) Log.d(TAG, "Processing MSG " + message.what + " from " + this.getName());
            switch (message.what) {
                case MSG_CONNECT:
                    onConnectionStateChanged(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
                            BluetoothProfile.STATE_CONNECTED);


                    Log.w(TAG, "Received CONNECT while Connected, ignoring");
                    break;

                case MSG_DISCONNECT:
                    if ((message.obj instanceof BluetoothDevice) &&
                            ((BluetoothDevice) message.obj).equals(mCurrentDevice)) {
@@ -384,34 +328,32 @@ final class PbapClientStateMachine extends StateMachine {
        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
        mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
        mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.PBAP_CLIENT, state,
                prevState);
    }

    public void connect(BluetoothDevice device) {
        Log.d(TAG, "Connect Request " + device.getAddress());
        sendMessage(MSG_CONNECT, device);
    }

    public void disconnect(BluetoothDevice device) {
        Log.d(TAG, "Disconnect Request " + device);
        sendMessage(MSG_DISCONNECT, device);
    }

    public void resumeDownload() {
        removeUncleanAccounts();
        sendMessage(MSG_RESUME_DOWNLOAD);
    }

    void doQuit() {
        removeUncleanAccounts();
        if (mHandlerThread != null) {
            mHandlerThread.quitSafely();
        }
        quitNow();
    }

    @Override
    protected void onQuitting() {
        mService.cleanupDevice(mCurrentDevice);
    }

    public int getConnectionState() {
        IState currentState = getCurrentState();
        if (currentState instanceof Disconnected) {
@@ -428,8 +370,8 @@ final class PbapClientStateMachine extends StateMachine {
    }

    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        int clientState = -1;
        BluetoothDevice currentDevice = null;
        int clientState;
        BluetoothDevice currentDevice;
        synchronized (mLock) {
            clientState = getConnectionState();
            currentDevice = getDevice();
@@ -472,25 +414,7 @@ final class PbapClientStateMachine extends StateMachine {
    }

    Context getContext() {
        return mContext;
    }

    private void removeUncleanAccounts() {
        // Find all accounts that match the type "pbap" and delete them.
        AccountManager accountManager = AccountManager.get(mContext);
        Account[] accounts = accountManager.getAccountsByType(
                mContext.getString(R.string.pbap_account_type));
        Log.w(TAG, "Found " + accounts.length + " unclean accounts");
        for (Account acc : accounts) {
            Log.w(TAG, "Deleting " + acc);
            // The device ID is the name of the account.
            accountManager.removeAccountExplicitly(acc);
        }
        try {
            mContext.getContentResolver().delete(CallLog.Calls.CONTENT_URI, null, null);
        } catch (IllegalArgumentException e) {
            // CallLogs could not be deleted, they may not exist yet.
        }
        return mService;
    }

    public void dump(StringBuilder sb) {