Loading android/app/AndroidManifest.xml +1 −4 Original line number Diff line number Diff line Loading @@ -519,12 +519,9 @@ <action android:name="android.bluetooth.IBluetoothPbapClient"/> </intent-filter> </service> <!-- Note: This service doesn't get started, it just indicates to the Authentication framework that we can create accounts of a specific type. As such, its safe to have as enabled on all targets and not just the ones that use PBAP Client --> <service android:process="@string/process" android:name="com.android.bluetooth.pbapclient.AuthenticationService" android:enabled="true" android:enabled="false" android:exported="true"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> Loading android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java +86 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.provider.CallLog; import android.sysprop.BluetoothProperties; import android.util.Log; Loading Loading @@ -60,6 +61,12 @@ public class PbapClientService extends ProfileService { private static final String TAG = "PbapClientService"; private static final String SERVICE_NAME = "Phonebook Access PCE"; /** * The component names for the owned authenticator service */ private static final String AUTHENTICATOR_SERVICE = AuthenticationService.class.getCanonicalName(); // 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 = Loading @@ -70,6 +77,40 @@ public class PbapClientService extends ProfileService { private DatabaseManager mDatabaseManager; /** * There's an ~1-2 second latency between when our Authentication service is set as available to * the system and when the Authentication/Account framework code will recognize it and allow us * to alter accounts. In lieu of the Accounts team dealing with this race condition, we're going * to periodically poll over 3 seconds until our accounts are visible, remove old accounts, and * then notify device state machines that they can create accounts and download contacts. */ // TODO(233361365): Remove this pattern when the framework solves their race condition private static final int ACCOUNT_VISIBILITY_CHECK_MS = 500; private static final int ACCOUNT_VISIBILITY_CHECK_TRIES_MAX = 6; private int mAccountVisibilityCheckTries = 0; private final Handler mAuthServiceHandler = new Handler(); private final Runnable mCheckAuthService = new Runnable() { @Override public void run() { // If our accounts are finally visible to use, clean up old ones and tell devices they // can issue downloads if they're ready. Otherwise, wait and try again. if (isAuthenticationServiceReady()) { Log.i(TAG, "Service ready! Clean up old accounts and try contacts downloads"); removeUncleanAccounts(); for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.tryDownloadIfConnected(); } } else if (mAccountVisibilityCheckTries < ACCOUNT_VISIBILITY_CHECK_TRIES_MAX) { mAccountVisibilityCheckTries += 1; Log.w(TAG, "AccountManager hasn't registered our service yet. Retry " + mAccountVisibilityCheckTries + "/" + ACCOUNT_VISIBILITY_CHECK_TRIES_MAX); mAuthServiceHandler.postDelayed(this, ACCOUNT_VISIBILITY_CHECK_MS); } else { Log.e(TAG, "Failed to register Authenication Service and get account visibility"); } } }; public static boolean isEnabled() { return BluetoothProperties.isProfilePbapClientEnabled().orElse(false); } Loading @@ -88,6 +129,8 @@ public class PbapClientService extends ProfileService { mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when PbapClientService starts"); setComponentAvailable(AUTHENTICATOR_SERVICE, true); IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); // delay initial download until after the user is unlocked to add an account. Loading @@ -101,7 +144,7 @@ public class PbapClientService extends ProfileService { Log.w(TAG, "Unable to register pbapclient receiver", e); } removeUncleanAccounts(); initializeAuthenticationService(); registerSdpRecord(); setPbapClientService(this); return true; Loading @@ -119,7 +162,8 @@ public class PbapClientService extends ProfileService { for (PbapClientStateMachine pbapClientStateMachine : mPbapClientStateMachineMap.values()) { pbapClientStateMachine.doQuit(); } removeUncleanAccounts(); cleanupAuthenicationService(); setComponentAvailable(AUTHENTICATOR_SERVICE, false); return true; } Loading @@ -133,7 +177,45 @@ public class PbapClientService extends ProfileService { } } /** * Periodically check if the account framework has recognized our service and will allow us to * interact with our accounts. Notify state machines once our service is ready so we can trigger * account downloads. */ private void initializeAuthenticationService() { mAuthServiceHandler.postDelayed(mCheckAuthService, ACCOUNT_VISIBILITY_CHECK_MS); } private void cleanupAuthenicationService() { mAuthServiceHandler.removeCallbacks(mCheckAuthService); removeUncleanAccounts(); } /** * Determine if our account type is visible to us yet. If it is, then our service is ready and * our account type is ready to use. * * Make a placeholder device account and determine our visibility relative to it. Note that this * function uses the same restrictions are the other add and remove functions, but is *also* * available to all system apps instead of throwing a runtime SecurityException. */ protected boolean isAuthenticationServiceReady() { Account account = new Account("00:00:00:00:00:00", getString(R.string.pbap_account_type)); AccountManager accountManager = AccountManager.get(this); int visibility = accountManager.getAccountVisibility(account, getPackageName()); if (DBG) { Log.d(TAG, "Checking visibility, visibility=" + visibility); } return visibility == AccountManager.VISIBILITY_VISIBLE || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE; } private void removeUncleanAccounts() { if (!isAuthenticationServiceReady()) { Log.w(TAG, "Can't remove accounts. AccountManager hasn't registered our service yet."); return; } // Find all accounts that match the type "pbap" and delete them. AccountManager accountManager = AccountManager.get(this); Account[] accounts = Loading Loading @@ -208,7 +290,7 @@ public class PbapClientService extends ProfileService { } } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.resumeDownload(); stateMachine.tryDownloadIfConnected(); } } else if (action.equals(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED)) { // PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects. Loading Loading @@ -542,6 +624,7 @@ public class PbapClientService extends ProfileService { @Override public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "isAuthServiceReady: " + isAuthenticationServiceReady()); for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.dump(sb); } Loading android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java +18 −7 Original line number Diff line number Diff line Loading @@ -301,10 +301,7 @@ final class PbapClientStateMachine extends StateMachine { onConnectionStateChanged(mCurrentDevice, mMostRecentState, BluetoothProfile.STATE_CONNECTED); mMostRecentState = BluetoothProfile.STATE_CONNECTED; if (mUserManager.isUserUnlocked()) { mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); } downloadIfReady(); } @Override Loading @@ -321,8 +318,7 @@ final class PbapClientStateMachine extends StateMachine { break; case MSG_RESUME_DOWNLOAD: mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); downloadIfReady(); break; default: Loading @@ -333,6 +329,21 @@ final class PbapClientStateMachine extends StateMachine { } } /** * Trigger a contacts download if the user is unlocked and our accounts are available to us */ private void downloadIfReady() { boolean userReady = mUserManager.isUserUnlocked(); boolean accountServiceReady = mService.isAuthenticationServiceReady(); if (!userReady || !accountServiceReady) { Log.w(TAG, "Cannot download contacts yet, userReady=" + userReady + ", accountServiceReady=" + accountServiceReady); return; } mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); } private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { if (device == null) { Log.w(TAG, "onConnectionStateChanged with invalid device"); Loading @@ -357,7 +368,7 @@ final class PbapClientStateMachine extends StateMachine { sendMessage(MSG_DISCONNECT, device); } public void resumeDownload() { public void tryDownloadIfConnected() { sendMessage(MSG_RESUME_DOWNLOAD); } Loading apex/apex_manifest.json +1 −1 Original line number Diff line number Diff line { "name": "com.android.bluetooth", "version": 330090000, "version": 339990000, "requireNativeLibs": [ "libaptX_encoder.so", "libaptXHD_encoder.so" Loading system/bta/hearing_aid/hearing_aid.cc +11 −11 Original line number Diff line number Diff line Loading @@ -359,8 +359,6 @@ class HearingAidImpl : public HearingAid { void OnGattConnected(tGATT_STATUS status, uint16_t conn_id, tGATT_IF client_if, RawAddress address, tBT_TRANSPORT transport, uint16_t mtu) { VLOG(2) << __func__ << ": address=" << address << ", conn_id=" << conn_id; HearingDevice* hearingDevice = hearingDevices.FindByAddress(address); if (!hearingDevice) { /* When Hearing Aid is quickly disabled and enabled in settings, this case Loading @@ -371,6 +369,8 @@ class HearingAidImpl : public HearingAid { return; } LOG(INFO) << __func__ << ": address=" << address << ", conn_id=" << conn_id; if (status != GATT_SUCCESS) { if (!hearingDevice->connecting_actively) { // acceptlist connection failed, that's ok. Loading Loading @@ -402,6 +402,15 @@ class HearingAidImpl : public HearingAid { hearingDevice->connection_update_status = AWAITING; } if (controller_get_interface()->supports_ble_2m_phy()) { LOG(INFO) << address << " set preferred 2M PHY"; BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0); } // Set data length // TODO(jpawlowski: for 16khz only 87 is required, optimize BTM_SetBleDataLength(address, 167); if (BTM_SecIsSecurityPending(address)) { /* if security collision happened, wait for encryption done * (BTA_GATTC_ENC_CMPL_CB_EVT) */ Loading Loading @@ -890,15 +899,6 @@ class HearingAidImpl : public HearingAid { void ConnectSocket(HearingDevice* hearingDevice, uint16_t psm) { tL2CAP_CFG_INFO cfg_info = tL2CAP_CFG_INFO{.mtu = 512}; if (controller_get_interface()->supports_ble_2m_phy()) { LOG(INFO) << hearingDevice->address << " set preferred PHY to 2M"; BTM_BleSetPhy(hearingDevice->address, PHY_LE_2M, PHY_LE_2M, 0); } // Set data length // TODO(jpawlowski: for 16khz only 87 is required, optimize BTM_SetBleDataLength(hearingDevice->address, 167); SendEnableServiceChangedInd(hearingDevice); uint8_t service_id = hearingDevice->isLeft() Loading Loading
android/app/AndroidManifest.xml +1 −4 Original line number Diff line number Diff line Loading @@ -519,12 +519,9 @@ <action android:name="android.bluetooth.IBluetoothPbapClient"/> </intent-filter> </service> <!-- Note: This service doesn't get started, it just indicates to the Authentication framework that we can create accounts of a specific type. As such, its safe to have as enabled on all targets and not just the ones that use PBAP Client --> <service android:process="@string/process" android:name="com.android.bluetooth.pbapclient.AuthenticationService" android:enabled="true" android:enabled="false" android:exported="true"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> Loading
android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java +86 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.provider.CallLog; import android.sysprop.BluetoothProperties; import android.util.Log; Loading Loading @@ -60,6 +61,12 @@ public class PbapClientService extends ProfileService { private static final String TAG = "PbapClientService"; private static final String SERVICE_NAME = "Phonebook Access PCE"; /** * The component names for the owned authenticator service */ private static final String AUTHENTICATOR_SERVICE = AuthenticationService.class.getCanonicalName(); // 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 = Loading @@ -70,6 +77,40 @@ public class PbapClientService extends ProfileService { private DatabaseManager mDatabaseManager; /** * There's an ~1-2 second latency between when our Authentication service is set as available to * the system and when the Authentication/Account framework code will recognize it and allow us * to alter accounts. In lieu of the Accounts team dealing with this race condition, we're going * to periodically poll over 3 seconds until our accounts are visible, remove old accounts, and * then notify device state machines that they can create accounts and download contacts. */ // TODO(233361365): Remove this pattern when the framework solves their race condition private static final int ACCOUNT_VISIBILITY_CHECK_MS = 500; private static final int ACCOUNT_VISIBILITY_CHECK_TRIES_MAX = 6; private int mAccountVisibilityCheckTries = 0; private final Handler mAuthServiceHandler = new Handler(); private final Runnable mCheckAuthService = new Runnable() { @Override public void run() { // If our accounts are finally visible to use, clean up old ones and tell devices they // can issue downloads if they're ready. Otherwise, wait and try again. if (isAuthenticationServiceReady()) { Log.i(TAG, "Service ready! Clean up old accounts and try contacts downloads"); removeUncleanAccounts(); for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.tryDownloadIfConnected(); } } else if (mAccountVisibilityCheckTries < ACCOUNT_VISIBILITY_CHECK_TRIES_MAX) { mAccountVisibilityCheckTries += 1; Log.w(TAG, "AccountManager hasn't registered our service yet. Retry " + mAccountVisibilityCheckTries + "/" + ACCOUNT_VISIBILITY_CHECK_TRIES_MAX); mAuthServiceHandler.postDelayed(this, ACCOUNT_VISIBILITY_CHECK_MS); } else { Log.e(TAG, "Failed to register Authenication Service and get account visibility"); } } }; public static boolean isEnabled() { return BluetoothProperties.isProfilePbapClientEnabled().orElse(false); } Loading @@ -88,6 +129,8 @@ public class PbapClientService extends ProfileService { mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when PbapClientService starts"); setComponentAvailable(AUTHENTICATOR_SERVICE, true); IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); // delay initial download until after the user is unlocked to add an account. Loading @@ -101,7 +144,7 @@ public class PbapClientService extends ProfileService { Log.w(TAG, "Unable to register pbapclient receiver", e); } removeUncleanAccounts(); initializeAuthenticationService(); registerSdpRecord(); setPbapClientService(this); return true; Loading @@ -119,7 +162,8 @@ public class PbapClientService extends ProfileService { for (PbapClientStateMachine pbapClientStateMachine : mPbapClientStateMachineMap.values()) { pbapClientStateMachine.doQuit(); } removeUncleanAccounts(); cleanupAuthenicationService(); setComponentAvailable(AUTHENTICATOR_SERVICE, false); return true; } Loading @@ -133,7 +177,45 @@ public class PbapClientService extends ProfileService { } } /** * Periodically check if the account framework has recognized our service and will allow us to * interact with our accounts. Notify state machines once our service is ready so we can trigger * account downloads. */ private void initializeAuthenticationService() { mAuthServiceHandler.postDelayed(mCheckAuthService, ACCOUNT_VISIBILITY_CHECK_MS); } private void cleanupAuthenicationService() { mAuthServiceHandler.removeCallbacks(mCheckAuthService); removeUncleanAccounts(); } /** * Determine if our account type is visible to us yet. If it is, then our service is ready and * our account type is ready to use. * * Make a placeholder device account and determine our visibility relative to it. Note that this * function uses the same restrictions are the other add and remove functions, but is *also* * available to all system apps instead of throwing a runtime SecurityException. */ protected boolean isAuthenticationServiceReady() { Account account = new Account("00:00:00:00:00:00", getString(R.string.pbap_account_type)); AccountManager accountManager = AccountManager.get(this); int visibility = accountManager.getAccountVisibility(account, getPackageName()); if (DBG) { Log.d(TAG, "Checking visibility, visibility=" + visibility); } return visibility == AccountManager.VISIBILITY_VISIBLE || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE; } private void removeUncleanAccounts() { if (!isAuthenticationServiceReady()) { Log.w(TAG, "Can't remove accounts. AccountManager hasn't registered our service yet."); return; } // Find all accounts that match the type "pbap" and delete them. AccountManager accountManager = AccountManager.get(this); Account[] accounts = Loading Loading @@ -208,7 +290,7 @@ public class PbapClientService extends ProfileService { } } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.resumeDownload(); stateMachine.tryDownloadIfConnected(); } } else if (action.equals(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED)) { // PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects. Loading Loading @@ -542,6 +624,7 @@ public class PbapClientService extends ProfileService { @Override public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "isAuthServiceReady: " + isAuthenticationServiceReady()); for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) { stateMachine.dump(sb); } Loading
android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java +18 −7 Original line number Diff line number Diff line Loading @@ -301,10 +301,7 @@ final class PbapClientStateMachine extends StateMachine { onConnectionStateChanged(mCurrentDevice, mMostRecentState, BluetoothProfile.STATE_CONNECTED); mMostRecentState = BluetoothProfile.STATE_CONNECTED; if (mUserManager.isUserUnlocked()) { mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); } downloadIfReady(); } @Override Loading @@ -321,8 +318,7 @@ final class PbapClientStateMachine extends StateMachine { break; case MSG_RESUME_DOWNLOAD: mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); downloadIfReady(); break; default: Loading @@ -333,6 +329,21 @@ final class PbapClientStateMachine extends StateMachine { } } /** * Trigger a contacts download if the user is unlocked and our accounts are available to us */ private void downloadIfReady() { boolean userReady = mUserManager.isUserUnlocked(); boolean accountServiceReady = mService.isAuthenticationServiceReady(); if (!userReady || !accountServiceReady) { Log.w(TAG, "Cannot download contacts yet, userReady=" + userReady + ", accountServiceReady=" + accountServiceReady); return; } mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_DOWNLOAD) .sendToTarget(); } private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { if (device == null) { Log.w(TAG, "onConnectionStateChanged with invalid device"); Loading @@ -357,7 +368,7 @@ final class PbapClientStateMachine extends StateMachine { sendMessage(MSG_DISCONNECT, device); } public void resumeDownload() { public void tryDownloadIfConnected() { sendMessage(MSG_RESUME_DOWNLOAD); } Loading
apex/apex_manifest.json +1 −1 Original line number Diff line number Diff line { "name": "com.android.bluetooth", "version": 330090000, "version": 339990000, "requireNativeLibs": [ "libaptX_encoder.so", "libaptXHD_encoder.so" Loading
system/bta/hearing_aid/hearing_aid.cc +11 −11 Original line number Diff line number Diff line Loading @@ -359,8 +359,6 @@ class HearingAidImpl : public HearingAid { void OnGattConnected(tGATT_STATUS status, uint16_t conn_id, tGATT_IF client_if, RawAddress address, tBT_TRANSPORT transport, uint16_t mtu) { VLOG(2) << __func__ << ": address=" << address << ", conn_id=" << conn_id; HearingDevice* hearingDevice = hearingDevices.FindByAddress(address); if (!hearingDevice) { /* When Hearing Aid is quickly disabled and enabled in settings, this case Loading @@ -371,6 +369,8 @@ class HearingAidImpl : public HearingAid { return; } LOG(INFO) << __func__ << ": address=" << address << ", conn_id=" << conn_id; if (status != GATT_SUCCESS) { if (!hearingDevice->connecting_actively) { // acceptlist connection failed, that's ok. Loading Loading @@ -402,6 +402,15 @@ class HearingAidImpl : public HearingAid { hearingDevice->connection_update_status = AWAITING; } if (controller_get_interface()->supports_ble_2m_phy()) { LOG(INFO) << address << " set preferred 2M PHY"; BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0); } // Set data length // TODO(jpawlowski: for 16khz only 87 is required, optimize BTM_SetBleDataLength(address, 167); if (BTM_SecIsSecurityPending(address)) { /* if security collision happened, wait for encryption done * (BTA_GATTC_ENC_CMPL_CB_EVT) */ Loading Loading @@ -890,15 +899,6 @@ class HearingAidImpl : public HearingAid { void ConnectSocket(HearingDevice* hearingDevice, uint16_t psm) { tL2CAP_CFG_INFO cfg_info = tL2CAP_CFG_INFO{.mtu = 512}; if (controller_get_interface()->supports_ble_2m_phy()) { LOG(INFO) << hearingDevice->address << " set preferred PHY to 2M"; BTM_BleSetPhy(hearingDevice->address, PHY_LE_2M, PHY_LE_2M, 0); } // Set data length // TODO(jpawlowski: for 16khz only 87 is required, optimize BTM_SetBleDataLength(hearingDevice->address, 167); SendEnableServiceChangedInd(hearingDevice); uint8_t service_id = hearingDevice->isLeft() Loading