Loading android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,9 @@ import android.os.ParcelFileDescriptor; import android.provider.Telephony; import android.provider.Telephony; import android.util.Log; import android.util.Log; import com.android.bluetooth.gatt.AppAdvertiseStats; import com.android.bluetooth.gatt.ContextMap; import com.android.bluetooth.gatt.GattService; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.obex.HeaderSet; import com.android.obex.HeaderSet; Loading Loading @@ -212,4 +215,12 @@ public class BluetoothMethodProxy { int advHandle, PeriodicAdvertisingCallback callback) { int advHandle, PeriodicAdvertisingCallback callback) { manager.transferSetInfo(bda, serviceData, advHandle, callback); manager.transferSetInfo(bda, serviceData, advHandle, callback); } } /** * Proxies {@link AppAdvertiseStats}. */ public AppAdvertiseStats createAppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { return new AppAdvertiseStats(appUid, id, name, map, service); } } } android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java +6 −2 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,8 @@ import android.bluetooth.le.PeriodicAdvertisingParameters; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.util.SparseArray; import android.util.SparseArray; import androidx.annotation.VisibleForTesting; import java.time.Duration; import java.time.Duration; import java.time.Instant; import java.time.Instant; import java.time.ZoneId; import java.time.ZoneId; Loading @@ -35,7 +37,8 @@ import java.util.Map; * on a per application basis. * on a per application basis. * @hide * @hide */ */ /*package*/ class AppAdvertiseStats { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public class AppAdvertiseStats { private static final String TAG = AppAdvertiseStats.class.getSimpleName(); private static final String TAG = AppAdvertiseStats.class.getSimpleName(); private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss") private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss") Loading Loading @@ -98,7 +101,8 @@ import java.util.Map; public ArrayList<AppAdvertiserRecord> mAdvertiserRecords = public ArrayList<AppAdvertiserRecord> mAdvertiserRecords = new ArrayList<AppAdvertiserRecord>(); new ArrayList<AppAdvertiserRecord>(); AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { this.mAppUid = appUid; this.mAppUid = appUid; this.mId = id; this.mId = id; this.mAppName = name; this.mAppName = name; Loading android/app/src/com/android/bluetooth/gatt/ContextMap.java +7 −3 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource; import android.util.Log; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.bluetooth.BluetoothMethodProxy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.google.common.collect.EvictingQueue; import com.google.common.collect.EvictingQueue; Loading @@ -46,7 +49,8 @@ import java.util.UUID; * This class manages application callbacks and keeps track of GATT connections. * This class manages application callbacks and keeps track of GATT connections. * @hide * @hide */ */ /*package*/ class ContextMap<C, T> { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public class ContextMap<C, T> { private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; /** /** Loading Loading @@ -242,8 +246,8 @@ import java.util.UUID; synchronized (mAppsLock) { synchronized (mAppsLock) { synchronized (this) { synchronized (this) { if (!mAppAdvertiseStats.containsKey(id)) { if (!mAppAdvertiseStats.containsKey(id)) { AppAdvertiseStats appAdvertiseStats = AppAdvertiseStats appAdvertiseStats = BluetoothMethodProxy.getInstance() new AppAdvertiseStats(appUid, id, appName, this, service); .createAppAdvertiseStats(appUid, id, appName, this, service); mAppAdvertiseStats.put(id, appAdvertiseStats); mAppAdvertiseStats.put(id, appAdvertiseStats); } } } } Loading android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java +78 −0 Original line number Original line Diff line number Diff line Loading @@ -20,13 +20,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.PeriodicAdvertisingParameters; import android.os.Binder; import android.os.Binder; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ServiceTestRule; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.TestUtils; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.AdapterService; Loading @@ -37,6 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.UUID; import java.util.UUID; Loading @@ -55,9 +61,16 @@ public class ContextMapTest { @Mock @Mock private AdapterService mAdapterService; private AdapterService mAdapterService; @Mock private AppAdvertiseStats appAdvertiseStats; @Spy private BluetoothMethodProxy mMapMethodProxy = BluetoothMethodProxy.getInstance(); @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); BluetoothMethodProxy.setInstanceForTesting(mMapMethodProxy); TestUtils.setAdapterService(mAdapterService); TestUtils.setAdapterService(mAdapterService); doReturn(true).when(mAdapterService).isStartedProfile(anyString()); doReturn(true).when(mAdapterService).isStartedProfile(anyString()); Loading @@ -72,6 +85,8 @@ public class ContextMapTest { return; return; } } BluetoothMethodProxy.setInstanceForTesting(null); doReturn(false).when(mAdapterService).isStartedProfile(anyString()); doReturn(false).when(mAdapterService).isStartedProfile(anyString()); TestUtils.stopService(mServiceRule, GattService.class); TestUtils.stopService(mServiceRule, GattService.class); mService = GattService.getGattService(); mService = GattService.getGattService(); Loading @@ -98,6 +113,64 @@ public class ContextMapTest { assertThat(contextMapByName.name).isEqualTo(appName); assertThat(contextMapByName.name).isEqualTo(appName); } } @Test public void advertisingSetAndData() { ContextMap contextMap = new ContextMap<>(); int appUid = Binder.getCallingUid(); int id = 12345; String appName = mService.getPackageManager().getNameForUid(appUid); doReturn(appAdvertiseStats).when(mMapMethodProxy) .createAppAdvertiseStats(appUid, id, appName, contextMap, mService); contextMap.add(id, null, mService); int duration = 60; int maxExtAdvEvents = 100; contextMap.enableAdvertisingSet(id, true, duration, maxExtAdvEvents); verify(appAdvertiseStats).enableAdvertisingSet(true, duration, maxExtAdvEvents); AdvertiseData advertiseData = new AdvertiseData.Builder().build(); contextMap.setAdvertisingData(id, advertiseData); verify(appAdvertiseStats).setAdvertisingData(advertiseData); AdvertiseData scanResponse = new AdvertiseData.Builder().build(); contextMap.setScanResponseData(id, scanResponse); verify(appAdvertiseStats).setScanResponseData(scanResponse); AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build(); contextMap.setAdvertisingParameters(id, parameters); verify(appAdvertiseStats).setAdvertisingParameters(parameters); PeriodicAdvertisingParameters periodicParameters = new PeriodicAdvertisingParameters.Builder().build(); contextMap.setPeriodicAdvertisingParameters(id, periodicParameters); verify(appAdvertiseStats).setPeriodicAdvertisingParameters(periodicParameters); AdvertiseData periodicData = new AdvertiseData.Builder().build(); contextMap.setPeriodicAdvertisingData(id, periodicData); verify(appAdvertiseStats).setPeriodicAdvertisingData(periodicData); contextMap.onPeriodicAdvertiseEnabled(id, true); verify(appAdvertiseStats).onPeriodicAdvertiseEnabled(true); AppAdvertiseStats toBeRemoved = contextMap.getAppAdvertiseStatsById(id); assertThat(toBeRemoved).isNotNull(); contextMap.removeAppAdvertiseStats(id); AppAdvertiseStats isRemoved = contextMap.getAppAdvertiseStatsById(id); assertThat(isRemoved).isNull(); } @Test public void emptyStop_doesNotCrash() throws Exception { ContextMap contextMap = new ContextMap<>(); int id = 12345; contextMap.recordAdvertiseStop(id); } @Test @Test public void testDump_doesNotCrash() throws Exception { public void testDump_doesNotCrash() throws Exception { StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder(); Loading @@ -109,6 +182,11 @@ public class ContextMapTest { contextMap.add(UUID.randomUUID(), null, null, null, mService); contextMap.add(UUID.randomUUID(), null, null, null, mService); contextMap.recordAdvertiseStop(id); int idSecond = 54321; contextMap.add(idSecond, null, mService); contextMap.dump(sb); contextMap.dump(sb); contextMap.dumpAdvertiser(sb); contextMap.dumpAdvertiser(sb); Loading Loading
android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,9 @@ import android.os.ParcelFileDescriptor; import android.provider.Telephony; import android.provider.Telephony; import android.util.Log; import android.util.Log; import com.android.bluetooth.gatt.AppAdvertiseStats; import com.android.bluetooth.gatt.ContextMap; import com.android.bluetooth.gatt.GattService; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.obex.HeaderSet; import com.android.obex.HeaderSet; Loading Loading @@ -212,4 +215,12 @@ public class BluetoothMethodProxy { int advHandle, PeriodicAdvertisingCallback callback) { int advHandle, PeriodicAdvertisingCallback callback) { manager.transferSetInfo(bda, serviceData, advHandle, callback); manager.transferSetInfo(bda, serviceData, advHandle, callback); } } /** * Proxies {@link AppAdvertiseStats}. */ public AppAdvertiseStats createAppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { return new AppAdvertiseStats(appUid, id, name, map, service); } } }
android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java +6 −2 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,8 @@ import android.bluetooth.le.PeriodicAdvertisingParameters; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.util.SparseArray; import android.util.SparseArray; import androidx.annotation.VisibleForTesting; import java.time.Duration; import java.time.Duration; import java.time.Instant; import java.time.Instant; import java.time.ZoneId; import java.time.ZoneId; Loading @@ -35,7 +37,8 @@ import java.util.Map; * on a per application basis. * on a per application basis. * @hide * @hide */ */ /*package*/ class AppAdvertiseStats { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public class AppAdvertiseStats { private static final String TAG = AppAdvertiseStats.class.getSimpleName(); private static final String TAG = AppAdvertiseStats.class.getSimpleName(); private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss") private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss") Loading Loading @@ -98,7 +101,8 @@ import java.util.Map; public ArrayList<AppAdvertiserRecord> mAdvertiserRecords = public ArrayList<AppAdvertiserRecord> mAdvertiserRecords = new ArrayList<AppAdvertiserRecord>(); new ArrayList<AppAdvertiserRecord>(); AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) { this.mAppUid = appUid; this.mAppUid = appUid; this.mId = id; this.mId = id; this.mAppName = name; this.mAppName = name; Loading
android/app/src/com/android/bluetooth/gatt/ContextMap.java +7 −3 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource; import android.util.Log; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.bluetooth.BluetoothMethodProxy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.google.common.collect.EvictingQueue; import com.google.common.collect.EvictingQueue; Loading @@ -46,7 +49,8 @@ import java.util.UUID; * This class manages application callbacks and keeps track of GATT connections. * This class manages application callbacks and keeps track of GATT connections. * @hide * @hide */ */ /*package*/ class ContextMap<C, T> { @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public class ContextMap<C, T> { private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; /** /** Loading Loading @@ -242,8 +246,8 @@ import java.util.UUID; synchronized (mAppsLock) { synchronized (mAppsLock) { synchronized (this) { synchronized (this) { if (!mAppAdvertiseStats.containsKey(id)) { if (!mAppAdvertiseStats.containsKey(id)) { AppAdvertiseStats appAdvertiseStats = AppAdvertiseStats appAdvertiseStats = BluetoothMethodProxy.getInstance() new AppAdvertiseStats(appUid, id, appName, this, service); .createAppAdvertiseStats(appUid, id, appName, this, service); mAppAdvertiseStats.put(id, appAdvertiseStats); mAppAdvertiseStats.put(id, appAdvertiseStats); } } } } Loading
android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java +78 −0 Original line number Original line Diff line number Diff line Loading @@ -20,13 +20,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.PeriodicAdvertisingParameters; import android.os.Binder; import android.os.Binder; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ServiceTestRule; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.TestUtils; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.AdapterService; Loading @@ -37,6 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.UUID; import java.util.UUID; Loading @@ -55,9 +61,16 @@ public class ContextMapTest { @Mock @Mock private AdapterService mAdapterService; private AdapterService mAdapterService; @Mock private AppAdvertiseStats appAdvertiseStats; @Spy private BluetoothMethodProxy mMapMethodProxy = BluetoothMethodProxy.getInstance(); @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); BluetoothMethodProxy.setInstanceForTesting(mMapMethodProxy); TestUtils.setAdapterService(mAdapterService); TestUtils.setAdapterService(mAdapterService); doReturn(true).when(mAdapterService).isStartedProfile(anyString()); doReturn(true).when(mAdapterService).isStartedProfile(anyString()); Loading @@ -72,6 +85,8 @@ public class ContextMapTest { return; return; } } BluetoothMethodProxy.setInstanceForTesting(null); doReturn(false).when(mAdapterService).isStartedProfile(anyString()); doReturn(false).when(mAdapterService).isStartedProfile(anyString()); TestUtils.stopService(mServiceRule, GattService.class); TestUtils.stopService(mServiceRule, GattService.class); mService = GattService.getGattService(); mService = GattService.getGattService(); Loading @@ -98,6 +113,64 @@ public class ContextMapTest { assertThat(contextMapByName.name).isEqualTo(appName); assertThat(contextMapByName.name).isEqualTo(appName); } } @Test public void advertisingSetAndData() { ContextMap contextMap = new ContextMap<>(); int appUid = Binder.getCallingUid(); int id = 12345; String appName = mService.getPackageManager().getNameForUid(appUid); doReturn(appAdvertiseStats).when(mMapMethodProxy) .createAppAdvertiseStats(appUid, id, appName, contextMap, mService); contextMap.add(id, null, mService); int duration = 60; int maxExtAdvEvents = 100; contextMap.enableAdvertisingSet(id, true, duration, maxExtAdvEvents); verify(appAdvertiseStats).enableAdvertisingSet(true, duration, maxExtAdvEvents); AdvertiseData advertiseData = new AdvertiseData.Builder().build(); contextMap.setAdvertisingData(id, advertiseData); verify(appAdvertiseStats).setAdvertisingData(advertiseData); AdvertiseData scanResponse = new AdvertiseData.Builder().build(); contextMap.setScanResponseData(id, scanResponse); verify(appAdvertiseStats).setScanResponseData(scanResponse); AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build(); contextMap.setAdvertisingParameters(id, parameters); verify(appAdvertiseStats).setAdvertisingParameters(parameters); PeriodicAdvertisingParameters periodicParameters = new PeriodicAdvertisingParameters.Builder().build(); contextMap.setPeriodicAdvertisingParameters(id, periodicParameters); verify(appAdvertiseStats).setPeriodicAdvertisingParameters(periodicParameters); AdvertiseData periodicData = new AdvertiseData.Builder().build(); contextMap.setPeriodicAdvertisingData(id, periodicData); verify(appAdvertiseStats).setPeriodicAdvertisingData(periodicData); contextMap.onPeriodicAdvertiseEnabled(id, true); verify(appAdvertiseStats).onPeriodicAdvertiseEnabled(true); AppAdvertiseStats toBeRemoved = contextMap.getAppAdvertiseStatsById(id); assertThat(toBeRemoved).isNotNull(); contextMap.removeAppAdvertiseStats(id); AppAdvertiseStats isRemoved = contextMap.getAppAdvertiseStatsById(id); assertThat(isRemoved).isNull(); } @Test public void emptyStop_doesNotCrash() throws Exception { ContextMap contextMap = new ContextMap<>(); int id = 12345; contextMap.recordAdvertiseStop(id); } @Test @Test public void testDump_doesNotCrash() throws Exception { public void testDump_doesNotCrash() throws Exception { StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder(); Loading @@ -109,6 +182,11 @@ public class ContextMapTest { contextMap.add(UUID.randomUUID(), null, null, null, mService); contextMap.add(UUID.randomUUID(), null, null, null, mService); contextMap.recordAdvertiseStop(id); int idSecond = 54321; contextMap.add(idSecond, null, mService); contextMap.dump(sb); contextMap.dump(sb); contextMap.dumpAdvertiser(sb); contextMap.dumpAdvertiser(sb); Loading