Loading android/app/AndroidManifest.xml +0 −11 Original line number Diff line number Diff line Loading @@ -221,17 +221,6 @@ </intent-filter> </service> <!-- Generic Attribute (GATT) Profile Service --> <service android:process="@string/process" android:name="com.android.bluetooth.gatt.GattService" android:enabled="true" android:exported="true" android:permission="android.permission.ACCESS_BLUETOOTH_SHARE"> <intent-filter> <action android:name="android.bluetooth.IBluetoothGatt"/> </intent-filter> </service> <!-- Hearing Aid Profile (HAP) client Profile Service --> <service android:process="@string/process" android:name="com.android.bluetooth.hap.HapClientService" Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +13 −82 Original line number Diff line number Diff line Loading @@ -79,11 +79,9 @@ import android.bluetooth.UidTraffic; import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.AsyncTask; Loading Loading @@ -371,7 +369,7 @@ public class AdapterService extends Service { private BassClientService mBassClientService; private BatteryService mBatteryService; private BluetoothQualityReportNativeInterface mBluetoothQualityReportNativeInterface; private IBluetoothGatt mBluetoothGatt; private GattService mGattService; private volatile boolean mTestModeEnabled = false; Loading Loading @@ -425,8 +423,6 @@ public class AdapterService extends Service { private static final int MESSAGE_PROFILE_SERVICE_REGISTERED = 2; private static final int MESSAGE_PROFILE_SERVICE_UNREGISTERED = 3; private static final int MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT = 4; private static final int MESSAGE_ON_PROFILE_SERVICE_BIND = 5; private static final int MESSAGE_ON_PROFILE_SERVICE_UNBIND = 6; class AdapterServiceHandler extends Handler { AdapterServiceHandler(Looper looper) { Loading @@ -450,14 +446,6 @@ public class AdapterService extends Service { verboseLog("handleMessage() - MESSAGE_PROFILE_SERVICE_UNREGISTERED"); unregisterProfileService((ProfileService) msg.obj); break; case MESSAGE_ON_PROFILE_SERVICE_BIND: verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_BIND"); onGattBind((IBinder) msg.obj); break; case MESSAGE_ON_PROFILE_SERVICE_UNBIND: verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_UNBIND"); onGattUnbind(); break; case MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT: errorLog( "handleMessage() - " Loading Loading @@ -497,22 +485,6 @@ public class AdapterService extends Service { mRegisteredProfiles.remove(profile); } private void onGattBind(IBinder service) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); try { mBluetoothGatt.startService(); } catch (RemoteException e) { Log.e(TAG, "onGattBind: RemoteException", e); } } private void onGattUnbind() { mBluetoothGatt = null; Log.e( TAG, "onGattUnbind: Gatt service has disconnected from AdapterService unexpectedly"); } private void processProfileServiceStateChanged(ProfileService profile, int state) { switch (state) { case BluetoothAdapter.STATE_ON: Loading Loading @@ -1005,47 +977,11 @@ public class AdapterService extends Service { } } class GattServiceConnection implements ServiceConnection { public void onServiceConnected(ComponentName componentName, IBinder service) { String name = componentName.getClassName(); if (DBG) { Log.d(TAG, "GattServiceConnection.onServiceConnected: " + name); } if (!name.equals(GattService.class.getName())) { Log.e(TAG, "Unknown service connected: " + name); return; } mHandler.obtainMessage(MESSAGE_ON_PROFILE_SERVICE_BIND, service).sendToTarget(); } public void onServiceDisconnected(ComponentName componentName) { // Called if we unexpectedly disconnect. This should never happen. String name = componentName.getClassName(); Log.e(TAG, "GattServiceConnection.onServiceDisconnected: " + name); if (!name.equals(GattService.class.getName())) { Log.e(TAG, "Unknown service disconnected: " + name); return; } mHandler.sendEmptyMessage(MESSAGE_ON_PROFILE_SERVICE_UNBIND); } } private GattServiceConnection mGattConnection = new GattServiceConnection(); private void startGattProfileService() { mStartedProfiles.add(GattService.class.getSimpleName()); Intent intent = new Intent(this, GattService.class); if (!bindServiceAsUser( intent, mGattConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT)) { // This should never happen // unbindService will be called during stopGattProfileService triggered by AdapterState Log.e(TAG, "Error while binding to gatt. This Bluetooth session will timeout"); unbindService(mGattConnection); } mGattService = new GattService(this); ((ProfileService) mGattService).doStart(); } private void stopGattProfileService() { Loading @@ -1056,15 +992,10 @@ public class AdapterService extends Service { } mStartedProfiles.remove(GattService.class.getSimpleName()); try { if (mBluetoothGatt != null) { mBluetoothGatt.stopService(); if (mGattService != null) { ((ProfileService) mGattService).doStop(); mGattService = null; } } catch (RemoteException e) { Log.e(TAG, "stopGattProfileService: RemoteException", e); } unbindService(mGattConnection); } private void invalidateBluetoothGetStateCache() { Loading Loading @@ -5244,6 +5175,7 @@ public class AdapterService extends Service { try { AdapterService service = getService(); if (service != null) { enforceBluetoothPrivilegedPermission(service); service.unregAllGattClient(source); } receiver.send(null); Loading Loading @@ -6883,16 +6815,15 @@ public class AdapterService extends Service { } IBluetoothGatt getBluetoothGatt() { return mBluetoothGatt; if (mGattService == null) { return null; } return IBluetoothGatt.Stub.asInterface(((ProfileService) mGattService).getBinder()); } void unregAllGattClient(AttributionSource source) { if (mBluetoothGatt != null) { try { mBluetoothGatt.unregAll(source); } catch (RemoteException e) { Log.e(TAG, "Unable to disconnect all apps.", e); } if (mGattService != null) { mGattService.unregAll(source); } } Loading android/app/src/com/android/bluetooth/btservice/ProfileService.java +9 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.bluetooth.btservice; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.Service; Loading Loading @@ -180,6 +182,11 @@ public abstract class ProfileService extends Service { return mBinder; } IBinder getBinder() { requireNonNull(mBinder, "Binder is null. onCreate need to be called first"); return mBinder; } @Override // Suppressed since this is called from framework @SuppressLint("AndroidFrameworkRequiresPermission") Loading Loading @@ -295,7 +302,7 @@ public abstract class ProfileService extends Service { android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS }) protected void doStart() { void doStart() { Log.v(mName, "doStart"); if (mAdapter == null) { Log.w(mName, "Can't start profile service: device does not have BT"); Loading @@ -321,7 +328,7 @@ public abstract class ProfileService extends Service { mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON); } protected void doStop() { void doStop() { Log.v(mName, "doStop"); if (mAdapterService == null || mAdapterService.isStartedProfile(mName)) { Log.w(mName, "Unexpectedly do Stop, don't stop."); Loading android/app/src/com/android/bluetooth/gatt/GattService.java +7 −38 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.bluetooth.le.ScanSettings; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.PackageInfoFlags; Loading Loading @@ -290,6 +291,11 @@ public class GattService extends ProfileService { private Handler mTestModeHandler; private final Object mTestModeLock = new Object(); public GattService(Context ctx) { attachBaseContext(ctx); onCreate(); } public static boolean isEnabled() { return BluetoothProperties.isProfileGattEnabled().orElse(true); } Loading Loading @@ -611,34 +617,6 @@ public class GattService extends ProfileService { return null; } @Override public void startService() { GattService service = mService; if (service == null) { Log.e(TAG, "startService: Service is null"); return; } if (!Utils.checkConnectPermissionForDataDelivery( service, null, "GattService startService")) { return; } service.doStart(); } @Override public void stopService() { GattService service = mService; if (service == null) { Log.e(TAG, "stopService: Service is null"); return; } if (!Utils.checkConnectPermissionForDataDelivery( service, null, "GattService stopService")) { return; } service.doStop(); } @Override public void getDevicesMatchingConnectionStates(int[] states, AttributionSource attributionSource, SynchronousResultReceiver receiver) { Loading Loading @@ -1777,15 +1755,6 @@ public class GattService extends ProfileService { service.disconnectAll(attributionSource); } @Override public void unregAll(AttributionSource attributionSource) { GattService service = getService(); if (service == null) { return; } service.unregAll(attributionSource); } @Override public void numHwTrackFiltersAvailable(AttributionSource attributionSource, SynchronousResultReceiver receiver) { Loading Loading @@ -3435,7 +3404,7 @@ public class GattService extends ProfileService { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) void unregAll(AttributionSource attributionSource) { public void unregAll(AttributionSource attributionSource) { for (Integer appId : mClientMap.getAllAppsIds()) { if (DBG) { Log.d(TAG, "unreg:" + appId); Loading android/app/tests/unit/src/com/android/bluetooth/TestUtils.java +19 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import androidx.test.uiautomator.UiDevice; import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.gatt.GattService; import org.junit.Assert; import org.junit.rules.TestRule; Loading Loading @@ -138,6 +139,21 @@ public class TestUtils { method.invoke(null, adapterService); } /** Helper function to mock getSystemService calls */ public static <T> void mockGetSystemService( Context ctx, String serviceName, Class<T> serviceClass, T mockService) { when(ctx.getSystemService(eq(serviceName))).thenReturn(mockService); when(ctx.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName); } /** Helper function to mock getSystemService calls */ public static <T> T mockGetSystemService( Context ctx, String serviceName, Class<T> serviceClass) { T mockedService = mock(serviceClass); mockGetSystemService(ctx, serviceName, serviceClass, mockedService); return mockedService; } /** * Start a profile service using the given {@link ServiceTestRule} and verify through * {@link AdapterService#getAdapterService()} that the service is actually started within Loading @@ -156,6 +172,9 @@ public class TestUtils { */ public static <T extends ProfileService> void startService(ServiceTestRule serviceTestRule, Class<T> profileServiceClass) throws TimeoutException { if (profileServiceClass == GattService.class) { Assert.assertFalse("GattService cannot be started as a service", true); } AdapterService adapterService = AdapterService.getAdapterService(); Assert.assertNotNull("Adapter service should not be null", adapterService); Assert.assertTrue("AdapterService.getAdapterService() must return a mocked or spied object" Loading Loading
android/app/AndroidManifest.xml +0 −11 Original line number Diff line number Diff line Loading @@ -221,17 +221,6 @@ </intent-filter> </service> <!-- Generic Attribute (GATT) Profile Service --> <service android:process="@string/process" android:name="com.android.bluetooth.gatt.GattService" android:enabled="true" android:exported="true" android:permission="android.permission.ACCESS_BLUETOOTH_SHARE"> <intent-filter> <action android:name="android.bluetooth.IBluetoothGatt"/> </intent-filter> </service> <!-- Hearing Aid Profile (HAP) client Profile Service --> <service android:process="@string/process" android:name="com.android.bluetooth.hap.HapClientService" Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +13 −82 Original line number Diff line number Diff line Loading @@ -79,11 +79,9 @@ import android.bluetooth.UidTraffic; import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.AsyncTask; Loading Loading @@ -371,7 +369,7 @@ public class AdapterService extends Service { private BassClientService mBassClientService; private BatteryService mBatteryService; private BluetoothQualityReportNativeInterface mBluetoothQualityReportNativeInterface; private IBluetoothGatt mBluetoothGatt; private GattService mGattService; private volatile boolean mTestModeEnabled = false; Loading Loading @@ -425,8 +423,6 @@ public class AdapterService extends Service { private static final int MESSAGE_PROFILE_SERVICE_REGISTERED = 2; private static final int MESSAGE_PROFILE_SERVICE_UNREGISTERED = 3; private static final int MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT = 4; private static final int MESSAGE_ON_PROFILE_SERVICE_BIND = 5; private static final int MESSAGE_ON_PROFILE_SERVICE_UNBIND = 6; class AdapterServiceHandler extends Handler { AdapterServiceHandler(Looper looper) { Loading @@ -450,14 +446,6 @@ public class AdapterService extends Service { verboseLog("handleMessage() - MESSAGE_PROFILE_SERVICE_UNREGISTERED"); unregisterProfileService((ProfileService) msg.obj); break; case MESSAGE_ON_PROFILE_SERVICE_BIND: verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_BIND"); onGattBind((IBinder) msg.obj); break; case MESSAGE_ON_PROFILE_SERVICE_UNBIND: verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_UNBIND"); onGattUnbind(); break; case MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT: errorLog( "handleMessage() - " Loading Loading @@ -497,22 +485,6 @@ public class AdapterService extends Service { mRegisteredProfiles.remove(profile); } private void onGattBind(IBinder service) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); try { mBluetoothGatt.startService(); } catch (RemoteException e) { Log.e(TAG, "onGattBind: RemoteException", e); } } private void onGattUnbind() { mBluetoothGatt = null; Log.e( TAG, "onGattUnbind: Gatt service has disconnected from AdapterService unexpectedly"); } private void processProfileServiceStateChanged(ProfileService profile, int state) { switch (state) { case BluetoothAdapter.STATE_ON: Loading Loading @@ -1005,47 +977,11 @@ public class AdapterService extends Service { } } class GattServiceConnection implements ServiceConnection { public void onServiceConnected(ComponentName componentName, IBinder service) { String name = componentName.getClassName(); if (DBG) { Log.d(TAG, "GattServiceConnection.onServiceConnected: " + name); } if (!name.equals(GattService.class.getName())) { Log.e(TAG, "Unknown service connected: " + name); return; } mHandler.obtainMessage(MESSAGE_ON_PROFILE_SERVICE_BIND, service).sendToTarget(); } public void onServiceDisconnected(ComponentName componentName) { // Called if we unexpectedly disconnect. This should never happen. String name = componentName.getClassName(); Log.e(TAG, "GattServiceConnection.onServiceDisconnected: " + name); if (!name.equals(GattService.class.getName())) { Log.e(TAG, "Unknown service disconnected: " + name); return; } mHandler.sendEmptyMessage(MESSAGE_ON_PROFILE_SERVICE_UNBIND); } } private GattServiceConnection mGattConnection = new GattServiceConnection(); private void startGattProfileService() { mStartedProfiles.add(GattService.class.getSimpleName()); Intent intent = new Intent(this, GattService.class); if (!bindServiceAsUser( intent, mGattConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT)) { // This should never happen // unbindService will be called during stopGattProfileService triggered by AdapterState Log.e(TAG, "Error while binding to gatt. This Bluetooth session will timeout"); unbindService(mGattConnection); } mGattService = new GattService(this); ((ProfileService) mGattService).doStart(); } private void stopGattProfileService() { Loading @@ -1056,15 +992,10 @@ public class AdapterService extends Service { } mStartedProfiles.remove(GattService.class.getSimpleName()); try { if (mBluetoothGatt != null) { mBluetoothGatt.stopService(); if (mGattService != null) { ((ProfileService) mGattService).doStop(); mGattService = null; } } catch (RemoteException e) { Log.e(TAG, "stopGattProfileService: RemoteException", e); } unbindService(mGattConnection); } private void invalidateBluetoothGetStateCache() { Loading Loading @@ -5244,6 +5175,7 @@ public class AdapterService extends Service { try { AdapterService service = getService(); if (service != null) { enforceBluetoothPrivilegedPermission(service); service.unregAllGattClient(source); } receiver.send(null); Loading Loading @@ -6883,16 +6815,15 @@ public class AdapterService extends Service { } IBluetoothGatt getBluetoothGatt() { return mBluetoothGatt; if (mGattService == null) { return null; } return IBluetoothGatt.Stub.asInterface(((ProfileService) mGattService).getBinder()); } void unregAllGattClient(AttributionSource source) { if (mBluetoothGatt != null) { try { mBluetoothGatt.unregAll(source); } catch (RemoteException e) { Log.e(TAG, "Unable to disconnect all apps.", e); } if (mGattService != null) { mGattService.unregAll(source); } } Loading
android/app/src/com/android/bluetooth/btservice/ProfileService.java +9 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.bluetooth.btservice; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.Service; Loading Loading @@ -180,6 +182,11 @@ public abstract class ProfileService extends Service { return mBinder; } IBinder getBinder() { requireNonNull(mBinder, "Binder is null. onCreate need to be called first"); return mBinder; } @Override // Suppressed since this is called from framework @SuppressLint("AndroidFrameworkRequiresPermission") Loading Loading @@ -295,7 +302,7 @@ public abstract class ProfileService extends Service { android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS }) protected void doStart() { void doStart() { Log.v(mName, "doStart"); if (mAdapter == null) { Log.w(mName, "Can't start profile service: device does not have BT"); Loading @@ -321,7 +328,7 @@ public abstract class ProfileService extends Service { mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON); } protected void doStop() { void doStop() { Log.v(mName, "doStop"); if (mAdapterService == null || mAdapterService.isStartedProfile(mName)) { Log.w(mName, "Unexpectedly do Stop, don't stop."); Loading
android/app/src/com/android/bluetooth/gatt/GattService.java +7 −38 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.bluetooth.le.ScanSettings; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.PackageInfoFlags; Loading Loading @@ -290,6 +291,11 @@ public class GattService extends ProfileService { private Handler mTestModeHandler; private final Object mTestModeLock = new Object(); public GattService(Context ctx) { attachBaseContext(ctx); onCreate(); } public static boolean isEnabled() { return BluetoothProperties.isProfileGattEnabled().orElse(true); } Loading Loading @@ -611,34 +617,6 @@ public class GattService extends ProfileService { return null; } @Override public void startService() { GattService service = mService; if (service == null) { Log.e(TAG, "startService: Service is null"); return; } if (!Utils.checkConnectPermissionForDataDelivery( service, null, "GattService startService")) { return; } service.doStart(); } @Override public void stopService() { GattService service = mService; if (service == null) { Log.e(TAG, "stopService: Service is null"); return; } if (!Utils.checkConnectPermissionForDataDelivery( service, null, "GattService stopService")) { return; } service.doStop(); } @Override public void getDevicesMatchingConnectionStates(int[] states, AttributionSource attributionSource, SynchronousResultReceiver receiver) { Loading Loading @@ -1777,15 +1755,6 @@ public class GattService extends ProfileService { service.disconnectAll(attributionSource); } @Override public void unregAll(AttributionSource attributionSource) { GattService service = getService(); if (service == null) { return; } service.unregAll(attributionSource); } @Override public void numHwTrackFiltersAvailable(AttributionSource attributionSource, SynchronousResultReceiver receiver) { Loading Loading @@ -3435,7 +3404,7 @@ public class GattService extends ProfileService { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) void unregAll(AttributionSource attributionSource) { public void unregAll(AttributionSource attributionSource) { for (Integer appId : mClientMap.getAllAppsIds()) { if (DBG) { Log.d(TAG, "unreg:" + appId); Loading
android/app/tests/unit/src/com/android/bluetooth/TestUtils.java +19 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import androidx.test.uiautomator.UiDevice; import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.gatt.GattService; import org.junit.Assert; import org.junit.rules.TestRule; Loading Loading @@ -138,6 +139,21 @@ public class TestUtils { method.invoke(null, adapterService); } /** Helper function to mock getSystemService calls */ public static <T> void mockGetSystemService( Context ctx, String serviceName, Class<T> serviceClass, T mockService) { when(ctx.getSystemService(eq(serviceName))).thenReturn(mockService); when(ctx.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName); } /** Helper function to mock getSystemService calls */ public static <T> T mockGetSystemService( Context ctx, String serviceName, Class<T> serviceClass) { T mockedService = mock(serviceClass); mockGetSystemService(ctx, serviceName, serviceClass, mockedService); return mockedService; } /** * Start a profile service using the given {@link ServiceTestRule} and verify through * {@link AdapterService#getAdapterService()} that the service is actually started within Loading @@ -156,6 +172,9 @@ public class TestUtils { */ public static <T extends ProfileService> void startService(ServiceTestRule serviceTestRule, Class<T> profileServiceClass) throws TimeoutException { if (profileServiceClass == GattService.class) { Assert.assertFalse("GattService cannot be started as a service", true); } AdapterService adapterService = AdapterService.getAdapterService(); Assert.assertNotNull("Adapter service should not be null", adapterService); Assert.assertTrue("AdapterService.getAdapterService() must return a mocked or spied object" Loading