Loading android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java +66 −101 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.app.Activity; import android.app.Activity; import android.app.Notification; import android.app.Notification; Loading @@ -39,7 +41,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.ContentObserver; import android.database.sqlite.SQLiteException; import android.os.Handler; import android.os.Handler; import android.os.HandlerThread; import android.os.HandlerThread; import android.os.Looper; import android.os.Looper; Loading Loading @@ -71,17 +72,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.HashMap; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Objects; // Next tag value for ContentProfileErrorReportUtils.report(): 12 // Next tag value for ContentProfileErrorReportUtils.report(): 12 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { private static final String TAG = "BluetoothPbapService"; private static final String TAG = BluetoothPbapService.class.getSimpleName(); /** * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and restart * com.android.bluetooth process. only enable DEBUG log: "setprop log.tag.BluetoothPbapService * DEBUG"; enable both VERBOSE and DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" */ /** The component name of the owned BluetoothPbapActivity */ /** The component name of the owned BluetoothPbapActivity */ private static final String PBAP_ACTIVITY = BluetoothPbapActivity.class.getCanonicalName(); private static final String PBAP_ACTIVITY = BluetoothPbapActivity.class.getCanonicalName(); Loading Loading @@ -132,8 +126,12 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private static String sLocalPhoneNum; private static String sLocalPhoneNum; private static String sLocalPhoneName; private static String sLocalPhoneName; private final AdapterService mAdapterService; private final Context mContext; private final DatabaseManager mDatabaseManager; private final NotificationManager mNotificationManager; private ObexServerSockets mServerSockets = null; private ObexServerSockets mServerSockets = null; private DatabaseManager mDatabaseManager; private static final int SDP_PBAP_SERVER_VERSION_1_2 = 0x0102; private static final int SDP_PBAP_SERVER_VERSION_1_2 = 0x0102; // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites Loading Loading @@ -172,12 +170,22 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private static final String PBAP_NOTIFICATION_ID = "pbap_notification"; private static final String PBAP_NOTIFICATION_ID = "pbap_notification"; private static final String PBAP_NOTIFICATION_NAME = "BT_PBAP_ADVANCE_SUPPORT"; private static final String PBAP_NOTIFICATION_NAME = "BT_PBAP_ADVANCE_SUPPORT"; private static final int PBAP_ADV_VERSION = 0x0102; private static final int PBAP_ADV_VERSION = 0x0102; private static NotificationManager sNotificationManager; private static boolean sIsPseDynamicVersionUpgradeEnabled; private static boolean sIsPseDynamicVersionUpgradeEnabled; public BluetoothPbapService(Context ctx) { public BluetoothPbapService(AdapterService adapterService) { super(ctx); this( requireNonNull(adapterService), adapterService.getSystemService(NotificationManager.class)); } @VisibleForTesting BluetoothPbapService(AdapterService adapterService, NotificationManager notificationManager) { super(requireNonNull(adapterService)); mContext = adapterService; mAdapterService = adapterService; mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); mNotificationManager = requireNonNull(notificationManager); } } public static boolean isEnabled() { public static boolean isEnabled() { Loading Loading @@ -244,16 +252,15 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect boolean savePreference = boolean savePreference = intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); AdapterService adapterService = AdapterService.getAdapterService(); if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { if (savePreference) { if (savePreference) { adapterService.setPhonebookAccessPermission(device, ACCESS_ALLOWED); mAdapterService.setPhonebookAccessPermission(device, ACCESS_ALLOWED); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); } } sm.sendMessage(PbapStateMachine.AUTHORIZED); sm.sendMessage(PbapStateMachine.AUTHORIZED); } else { } else { if (savePreference) { if (savePreference) { adapterService.setPhonebookAccessPermission(device, ACCESS_REJECTED); mAdapterService.setPhonebookAccessPermission(device, ACCESS_REJECTED); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); } } sm.sendMessage(PbapStateMachine.REJECTED); sm.sendMessage(PbapStateMachine.REJECTED); Loading Loading @@ -425,49 +432,33 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect } } /*Creates Notification for PBAP version upgrade */ /*Creates Notification for PBAP version upgrade */ protected static void createNotification(BluetoothPbapService context) { protected void createNotification() { Log.v(TAG, "Create PBAP Notification for Upgrade"); Log.v(TAG, "Create PBAP Notification for Upgrade"); // create Notification channel. // create Notification channel. sNotificationManager = context.getSystemService(NotificationManager.class); if (sNotificationManager != null) { NotificationChannel mChannel = NotificationChannel mChannel = new NotificationChannel( new NotificationChannel( PBAP_NOTIFICATION_ID, PBAP_NOTIFICATION_ID, PBAP_NOTIFICATION_NAME, PBAP_NOTIFICATION_NAME, NotificationManager.IMPORTANCE_DEFAULT); NotificationManager.IMPORTANCE_DEFAULT); sNotificationManager.createNotificationChannel(mChannel); mNotificationManager.createNotificationChannel(mChannel); // create notification // create notification String title = context.getString(R.string.phonebook_advance_feature_support); String title = getString(R.string.phonebook_advance_feature_support); String contentText = context.getString(R.string.repair_for_adv_phonebook_feature); String contentText = getString(R.string.repair_for_adv_phonebook_feature); int notificationId = android.R.drawable.stat_sys_data_bluetooth; int notificationId = android.R.drawable.stat_sys_data_bluetooth; Notification notification = Notification notification = new Notification.Builder(context, PBAP_NOTIFICATION_ID) new Notification.Builder(this, PBAP_NOTIFICATION_ID) .setContentTitle(title) .setContentTitle(title) .setContentText(contentText) .setContentText(contentText) .setSmallIcon(notificationId) .setSmallIcon(notificationId) .setAutoCancel(true) .setAutoCancel(true) .build(); .build(); sNotificationManager.notify(notificationId, notification); mNotificationManager.notify(notificationId, notification); } else { Log.e(TAG, "sNotificationManager is null"); ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, 6); } } } /* Checks if notification for Version Upgrade is required */ /* Checks if notification for Version Upgrade is required */ protected static void handleNotificationTask( protected void handleNotificationTask(BluetoothDevice remoteDevice) { BluetoothPbapService service, BluetoothDevice remoteDevice) { int pce_version = mAdapterService.getRemotePbapPceVersion(remoteDevice.getAddress()); int pce_version = 0; AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService != null) { pce_version = adapterService.getRemotePbapPceVersion(remoteDevice.getAddress()); Log.d(TAG, "pce_version: " + pce_version); Log.d(TAG, "pce_version: " + pce_version); } boolean matched = boolean matched = InteropUtil.interopMatchAddrOrName( InteropUtil.interopMatchAddrOrName( Loading @@ -477,12 +468,10 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect if (pce_version == PBAP_ADV_VERSION && !matched) { if (pce_version == PBAP_ADV_VERSION && !matched) { Log.d(TAG, "Remote Supports PBAP 1.2. Notify user"); Log.d(TAG, "Remote Supports PBAP 1.2. Notify user"); createNotification(service); createNotification(); } else { } else { Log.d(TAG, "Notification Not Required."); Log.d(TAG, "Notification Not Required."); if (sNotificationManager != null) { mNotificationManager.cancel(android.R.drawable.stat_sys_data_bluetooth); sNotificationManager.cancel(android.R.drawable.stat_sys_data_bluetooth); } } } } } Loading Loading @@ -584,7 +573,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect case HANDLE_VERSION_UPDATE_NOTIFICATION: case HANDLE_VERSION_UPDATE_NOTIFICATION: BluetoothDevice remoteDev = (BluetoothDevice) msg.obj; BluetoothDevice remoteDev = (BluetoothDevice) msg.obj; handleNotificationTask(sBluetoothPbapService, remoteDev); handleNotificationTask(remoteDev); break; break; default: default: break; break; Loading Loading @@ -711,17 +700,13 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect @Override @Override public void start() { public void start() { Log.v(TAG, "start()"); Log.v(TAG, "start()"); mDatabaseManager = Objects.requireNonNull( AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when PbapService starts"); IntentFilter userFilter = new IntentFilter(); IntentFilter userFilter = new IntentFilter(); userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_UNLOCKED); userFilter.addAction(Intent.ACTION_USER_UNLOCKED); getApplicationContext().registerReceiver(mUserChangeReceiver, userFilter); registerReceiver(mUserChangeReceiver, userFilter); // Enable owned Activity component // Enable owned Activity component setComponentAvailable(PBAP_ACTIVITY, true); setComponentAvailable(PBAP_ACTIVITY, true); Loading @@ -738,50 +723,30 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect filter.addAction(AUTH_CANCELLED_ACTION); filter.addAction(AUTH_CANCELLED_ACTION); BluetoothPbapConfig.init(this); BluetoothPbapConfig.init(this); registerReceiver(mPbapReceiver, filter); registerReceiver(mPbapReceiver, filter); try { mContactChangeObserver = new BluetoothPbapContentObserver(); mContactChangeObserver = new BluetoothPbapContentObserver(); getContentResolver() mContext.getContentResolver() .registerContentObserver( .registerContentObserver( DevicePolicyUtils.getEnterprisePhoneUri(this), DevicePolicyUtils.getEnterprisePhoneUri(this), false, false, mContactChangeObserver); mContactChangeObserver); } catch (SQLiteException e) { ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 9); Log.e(TAG, "SQLite exception: " + e); } catch (IllegalStateException e) { ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 10); Log.e(TAG, "Illegal state exception, content observer is already registered"); } setBluetoothPbapService(this); setBluetoothPbapService(this); mSessionStatusHandler.sendMessage( mSessionStatusHandler.sendEmptyMessage(GET_LOCAL_TELEPHONY_DETAILS); mSessionStatusHandler.obtainMessage(GET_LOCAL_TELEPHONY_DETAILS)); mSessionStatusHandler.sendEmptyMessage(LOAD_CONTACTS); mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); mSessionStatusHandler.sendEmptyMessage(START_LISTENER); mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService != null) { sIsPseDynamicVersionUpgradeEnabled = sIsPseDynamicVersionUpgradeEnabled = adapterService.pbapPseDynamicVersionUpgradeIsEnabled(); mAdapterService.pbapPseDynamicVersionUpgradeIsEnabled(); Log.d(TAG, "sIsPseDynamicVersionUpgradeEnabled: " + sIsPseDynamicVersionUpgradeEnabled); Log.d(TAG, "sIsPseDynamicVersionUpgradeEnabled: " + sIsPseDynamicVersionUpgradeEnabled); } } } @Override @Override public void stop() { public void stop() { Log.v(TAG, "stop()"); Log.v(TAG, "stop()"); setBluetoothPbapService(null); setBluetoothPbapService(null); if (mSessionStatusHandler != null) { if (mSessionStatusHandler != null) { mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); mSessionStatusHandler.sendEmptyMessage(SHUTDOWN); } } if (mHandlerThread != null) { if (mHandlerThread != null) { mHandlerThread.quitSafely(); mHandlerThread.quitSafely(); Loading @@ -792,13 +757,13 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect return; return; } } unregisterReceiver(mPbapReceiver); unregisterReceiver(mPbapReceiver); getContentResolver().unregisterContentObserver(mContactChangeObserver); mContext.getContentResolver().unregisterContentObserver(mContactChangeObserver); mContactChangeObserver = null; mContactChangeObserver = null; setComponentAvailable(PBAP_ACTIVITY, false); setComponentAvailable(PBAP_ACTIVITY, false); synchronized (mPbapStateMachineMap) { synchronized (mPbapStateMachineMap) { mPbapStateMachineMap.clear(); mPbapStateMachineMap.clear(); } } getApplicationContext().unregisterReceiver(mUserChangeReceiver); unregisterReceiver(mUserChangeReceiver); } } /** /** Loading Loading @@ -955,7 +920,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { BluetoothDevice device = stateMachine.getRemoteDevice(); BluetoothDevice device = stateMachine.getRemoteDevice(); int permission = AdapterService.getAdapterService().getPhonebookAccessPermission(device); int permission = mAdapterService.getPhonebookAccessPermission(device); Log.d(TAG, "getPhonebookAccessPermission() = " + permission); Log.d(TAG, "getPhonebookAccessPermission() = " + permission); if (permission == ACCESS_ALLOWED) { if (permission == ACCESS_ALLOWED) { Loading Loading @@ -1022,7 +987,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect mPbapStateMachineMap.clear(); mPbapStateMachineMap.clear(); } } mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); mSessionStatusHandler.sendEmptyMessage(START_LISTENER); } } private void loadAllContacts() { private void loadAllContacts() { Loading android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java +26 −26 Original line number Original line Diff line number Diff line Loading @@ -25,13 +25,16 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.os.Message; import android.os.Message; import android.os.UserManager; import android.os.test.TestLooper; import android.os.test.TestLooper; import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; Loading @@ -53,58 +56,55 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule; import java.util.List; @MediumTest @MediumTest @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) public class BluetoothPbapServiceTest { public class BluetoothPbapServiceTest { private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00"; private BluetoothPbapService mService; private BluetoothAdapter mAdapter = null; private BluetoothDevice mRemoteDevice; private boolean mIsAdapterServiceSet; private boolean mIsBluetoothPabpServiceStarted; private TestLooper mTestLooper; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private AdapterService mAdapterService; @Mock private AdapterService mAdapterService; @Mock private DatabaseManager mDatabaseManager; @Mock private DatabaseManager mDatabaseManager; @Mock private NotificationManager mNotificationManager; @Spy private BluetoothMethodProxy mMethodProxy = BluetoothMethodProxy.getInstance(); @Spy private BluetoothMethodProxy mMethodProxy = BluetoothMethodProxy.getInstance(); private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); private final BluetoothDevice mRemoteDevice = TestUtils.getTestDevice(mAdapter, 42); private final Context mTargetContext = InstrumentationRegistry.getTargetContext(); private final MockContentResolver mMockContentResolver = new MockContentResolver(mTargetContext); private BluetoothPbapService mService; private TestLooper mTestLooper; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Context targetContext = InstrumentationRegistry.getTargetContext(); doReturn(mTargetContext.getPackageName()).when(mAdapterService).getPackageName(); doReturn(mTargetContext.getPackageManager()).when(mAdapterService).getPackageManager(); doReturn(mMockContentResolver).when(mAdapterService).getContentResolver(); UserManager manager = TestUtils.mockGetSystemService( mAdapterService, Context.USER_SERVICE, UserManager.class); doReturn(List.of()).when(manager).getAllProfiles(); mTestLooper = new TestLooper(); mTestLooper = new TestLooper(); BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); doReturn(mTestLooper.getLooper()).when(mMethodProxy).handlerThreadGetLooper(any()); doReturn(mTestLooper.getLooper()).when(mMethodProxy).handlerThreadGetLooper(any()); doNothing().when(mMethodProxy).threadStart(any()); doNothing().when(mMethodProxy).threadStart(any()); mTestLooper.startAutoDispatch(); mTestLooper.startAutoDispatch(); TestUtils.setAdapterService(mAdapterService); mIsAdapterServiceSet = true; doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); mService = new BluetoothPbapService(targetContext); mService = new BluetoothPbapService(mAdapterService, mNotificationManager); mService.start(); mService.start(); mService.setAvailable(true); mService.setAvailable(true); mIsBluetoothPabpServiceStarted = true; // Try getting the Bluetooth adapter mAdapter = BluetoothAdapter.getDefaultAdapter(); assertThat(mAdapter).isNotNull(); mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); } } @After @After public void tearDown() throws Exception { public void tearDown() throws Exception { mTestLooper.stopAutoDispatchAndIgnoreExceptions(); mTestLooper.stopAutoDispatchAndIgnoreExceptions(); BluetoothMethodProxy.setInstanceForTesting(null); BluetoothMethodProxy.setInstanceForTesting(null); if (!mIsAdapterServiceSet) { return; } if (mIsBluetoothPabpServiceStarted) { mService.stop(); mService.stop(); mService = BluetoothPbapService.getBluetoothPbapService(); assertThat(BluetoothPbapService.getBluetoothPbapService()).isNull(); assertThat(mService).isNull(); } TestUtils.clearAdapterService(mAdapterService); } } @Test @Test Loading Loading
android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java +66 −101 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.app.Activity; import android.app.Activity; import android.app.Notification; import android.app.Notification; Loading @@ -39,7 +41,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.ContentObserver; import android.database.sqlite.SQLiteException; import android.os.Handler; import android.os.Handler; import android.os.HandlerThread; import android.os.HandlerThread; import android.os.Looper; import android.os.Looper; Loading Loading @@ -71,17 +72,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.HashMap; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Objects; // Next tag value for ContentProfileErrorReportUtils.report(): 12 // Next tag value for ContentProfileErrorReportUtils.report(): 12 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { private static final String TAG = "BluetoothPbapService"; private static final String TAG = BluetoothPbapService.class.getSimpleName(); /** * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and restart * com.android.bluetooth process. only enable DEBUG log: "setprop log.tag.BluetoothPbapService * DEBUG"; enable both VERBOSE and DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" */ /** The component name of the owned BluetoothPbapActivity */ /** The component name of the owned BluetoothPbapActivity */ private static final String PBAP_ACTIVITY = BluetoothPbapActivity.class.getCanonicalName(); private static final String PBAP_ACTIVITY = BluetoothPbapActivity.class.getCanonicalName(); Loading Loading @@ -132,8 +126,12 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private static String sLocalPhoneNum; private static String sLocalPhoneNum; private static String sLocalPhoneName; private static String sLocalPhoneName; private final AdapterService mAdapterService; private final Context mContext; private final DatabaseManager mDatabaseManager; private final NotificationManager mNotificationManager; private ObexServerSockets mServerSockets = null; private ObexServerSockets mServerSockets = null; private DatabaseManager mDatabaseManager; private static final int SDP_PBAP_SERVER_VERSION_1_2 = 0x0102; private static final int SDP_PBAP_SERVER_VERSION_1_2 = 0x0102; // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites Loading Loading @@ -172,12 +170,22 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect private static final String PBAP_NOTIFICATION_ID = "pbap_notification"; private static final String PBAP_NOTIFICATION_ID = "pbap_notification"; private static final String PBAP_NOTIFICATION_NAME = "BT_PBAP_ADVANCE_SUPPORT"; private static final String PBAP_NOTIFICATION_NAME = "BT_PBAP_ADVANCE_SUPPORT"; private static final int PBAP_ADV_VERSION = 0x0102; private static final int PBAP_ADV_VERSION = 0x0102; private static NotificationManager sNotificationManager; private static boolean sIsPseDynamicVersionUpgradeEnabled; private static boolean sIsPseDynamicVersionUpgradeEnabled; public BluetoothPbapService(Context ctx) { public BluetoothPbapService(AdapterService adapterService) { super(ctx); this( requireNonNull(adapterService), adapterService.getSystemService(NotificationManager.class)); } @VisibleForTesting BluetoothPbapService(AdapterService adapterService, NotificationManager notificationManager) { super(requireNonNull(adapterService)); mContext = adapterService; mAdapterService = adapterService; mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); mNotificationManager = requireNonNull(notificationManager); } } public static boolean isEnabled() { public static boolean isEnabled() { Loading Loading @@ -244,16 +252,15 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect boolean savePreference = boolean savePreference = intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); AdapterService adapterService = AdapterService.getAdapterService(); if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { if (savePreference) { if (savePreference) { adapterService.setPhonebookAccessPermission(device, ACCESS_ALLOWED); mAdapterService.setPhonebookAccessPermission(device, ACCESS_ALLOWED); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); } } sm.sendMessage(PbapStateMachine.AUTHORIZED); sm.sendMessage(PbapStateMachine.AUTHORIZED); } else { } else { if (savePreference) { if (savePreference) { adapterService.setPhonebookAccessPermission(device, ACCESS_REJECTED); mAdapterService.setPhonebookAccessPermission(device, ACCESS_REJECTED); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); } } sm.sendMessage(PbapStateMachine.REJECTED); sm.sendMessage(PbapStateMachine.REJECTED); Loading Loading @@ -425,49 +432,33 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect } } /*Creates Notification for PBAP version upgrade */ /*Creates Notification for PBAP version upgrade */ protected static void createNotification(BluetoothPbapService context) { protected void createNotification() { Log.v(TAG, "Create PBAP Notification for Upgrade"); Log.v(TAG, "Create PBAP Notification for Upgrade"); // create Notification channel. // create Notification channel. sNotificationManager = context.getSystemService(NotificationManager.class); if (sNotificationManager != null) { NotificationChannel mChannel = NotificationChannel mChannel = new NotificationChannel( new NotificationChannel( PBAP_NOTIFICATION_ID, PBAP_NOTIFICATION_ID, PBAP_NOTIFICATION_NAME, PBAP_NOTIFICATION_NAME, NotificationManager.IMPORTANCE_DEFAULT); NotificationManager.IMPORTANCE_DEFAULT); sNotificationManager.createNotificationChannel(mChannel); mNotificationManager.createNotificationChannel(mChannel); // create notification // create notification String title = context.getString(R.string.phonebook_advance_feature_support); String title = getString(R.string.phonebook_advance_feature_support); String contentText = context.getString(R.string.repair_for_adv_phonebook_feature); String contentText = getString(R.string.repair_for_adv_phonebook_feature); int notificationId = android.R.drawable.stat_sys_data_bluetooth; int notificationId = android.R.drawable.stat_sys_data_bluetooth; Notification notification = Notification notification = new Notification.Builder(context, PBAP_NOTIFICATION_ID) new Notification.Builder(this, PBAP_NOTIFICATION_ID) .setContentTitle(title) .setContentTitle(title) .setContentText(contentText) .setContentText(contentText) .setSmallIcon(notificationId) .setSmallIcon(notificationId) .setAutoCancel(true) .setAutoCancel(true) .build(); .build(); sNotificationManager.notify(notificationId, notification); mNotificationManager.notify(notificationId, notification); } else { Log.e(TAG, "sNotificationManager is null"); ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, 6); } } } /* Checks if notification for Version Upgrade is required */ /* Checks if notification for Version Upgrade is required */ protected static void handleNotificationTask( protected void handleNotificationTask(BluetoothDevice remoteDevice) { BluetoothPbapService service, BluetoothDevice remoteDevice) { int pce_version = mAdapterService.getRemotePbapPceVersion(remoteDevice.getAddress()); int pce_version = 0; AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService != null) { pce_version = adapterService.getRemotePbapPceVersion(remoteDevice.getAddress()); Log.d(TAG, "pce_version: " + pce_version); Log.d(TAG, "pce_version: " + pce_version); } boolean matched = boolean matched = InteropUtil.interopMatchAddrOrName( InteropUtil.interopMatchAddrOrName( Loading @@ -477,12 +468,10 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect if (pce_version == PBAP_ADV_VERSION && !matched) { if (pce_version == PBAP_ADV_VERSION && !matched) { Log.d(TAG, "Remote Supports PBAP 1.2. Notify user"); Log.d(TAG, "Remote Supports PBAP 1.2. Notify user"); createNotification(service); createNotification(); } else { } else { Log.d(TAG, "Notification Not Required."); Log.d(TAG, "Notification Not Required."); if (sNotificationManager != null) { mNotificationManager.cancel(android.R.drawable.stat_sys_data_bluetooth); sNotificationManager.cancel(android.R.drawable.stat_sys_data_bluetooth); } } } } } Loading Loading @@ -584,7 +573,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect case HANDLE_VERSION_UPDATE_NOTIFICATION: case HANDLE_VERSION_UPDATE_NOTIFICATION: BluetoothDevice remoteDev = (BluetoothDevice) msg.obj; BluetoothDevice remoteDev = (BluetoothDevice) msg.obj; handleNotificationTask(sBluetoothPbapService, remoteDev); handleNotificationTask(remoteDev); break; break; default: default: break; break; Loading Loading @@ -711,17 +700,13 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect @Override @Override public void start() { public void start() { Log.v(TAG, "start()"); Log.v(TAG, "start()"); mDatabaseManager = Objects.requireNonNull( AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when PbapService starts"); IntentFilter userFilter = new IntentFilter(); IntentFilter userFilter = new IntentFilter(); userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_UNLOCKED); userFilter.addAction(Intent.ACTION_USER_UNLOCKED); getApplicationContext().registerReceiver(mUserChangeReceiver, userFilter); registerReceiver(mUserChangeReceiver, userFilter); // Enable owned Activity component // Enable owned Activity component setComponentAvailable(PBAP_ACTIVITY, true); setComponentAvailable(PBAP_ACTIVITY, true); Loading @@ -738,50 +723,30 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect filter.addAction(AUTH_CANCELLED_ACTION); filter.addAction(AUTH_CANCELLED_ACTION); BluetoothPbapConfig.init(this); BluetoothPbapConfig.init(this); registerReceiver(mPbapReceiver, filter); registerReceiver(mPbapReceiver, filter); try { mContactChangeObserver = new BluetoothPbapContentObserver(); mContactChangeObserver = new BluetoothPbapContentObserver(); getContentResolver() mContext.getContentResolver() .registerContentObserver( .registerContentObserver( DevicePolicyUtils.getEnterprisePhoneUri(this), DevicePolicyUtils.getEnterprisePhoneUri(this), false, false, mContactChangeObserver); mContactChangeObserver); } catch (SQLiteException e) { ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 9); Log.e(TAG, "SQLite exception: " + e); } catch (IllegalStateException e) { ContentProfileErrorReportUtils.report( BluetoothProfile.PBAP, BluetoothProtoEnums.BLUETOOTH_PBAP_SERVICE, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 10); Log.e(TAG, "Illegal state exception, content observer is already registered"); } setBluetoothPbapService(this); setBluetoothPbapService(this); mSessionStatusHandler.sendMessage( mSessionStatusHandler.sendEmptyMessage(GET_LOCAL_TELEPHONY_DETAILS); mSessionStatusHandler.obtainMessage(GET_LOCAL_TELEPHONY_DETAILS)); mSessionStatusHandler.sendEmptyMessage(LOAD_CONTACTS); mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); mSessionStatusHandler.sendEmptyMessage(START_LISTENER); mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); AdapterService adapterService = AdapterService.getAdapterService(); if (adapterService != null) { sIsPseDynamicVersionUpgradeEnabled = sIsPseDynamicVersionUpgradeEnabled = adapterService.pbapPseDynamicVersionUpgradeIsEnabled(); mAdapterService.pbapPseDynamicVersionUpgradeIsEnabled(); Log.d(TAG, "sIsPseDynamicVersionUpgradeEnabled: " + sIsPseDynamicVersionUpgradeEnabled); Log.d(TAG, "sIsPseDynamicVersionUpgradeEnabled: " + sIsPseDynamicVersionUpgradeEnabled); } } } @Override @Override public void stop() { public void stop() { Log.v(TAG, "stop()"); Log.v(TAG, "stop()"); setBluetoothPbapService(null); setBluetoothPbapService(null); if (mSessionStatusHandler != null) { if (mSessionStatusHandler != null) { mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); mSessionStatusHandler.sendEmptyMessage(SHUTDOWN); } } if (mHandlerThread != null) { if (mHandlerThread != null) { mHandlerThread.quitSafely(); mHandlerThread.quitSafely(); Loading @@ -792,13 +757,13 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect return; return; } } unregisterReceiver(mPbapReceiver); unregisterReceiver(mPbapReceiver); getContentResolver().unregisterContentObserver(mContactChangeObserver); mContext.getContentResolver().unregisterContentObserver(mContactChangeObserver); mContactChangeObserver = null; mContactChangeObserver = null; setComponentAvailable(PBAP_ACTIVITY, false); setComponentAvailable(PBAP_ACTIVITY, false); synchronized (mPbapStateMachineMap) { synchronized (mPbapStateMachineMap) { mPbapStateMachineMap.clear(); mPbapStateMachineMap.clear(); } } getApplicationContext().unregisterReceiver(mUserChangeReceiver); unregisterReceiver(mUserChangeReceiver); } } /** /** Loading Loading @@ -955,7 +920,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { BluetoothDevice device = stateMachine.getRemoteDevice(); BluetoothDevice device = stateMachine.getRemoteDevice(); int permission = AdapterService.getAdapterService().getPhonebookAccessPermission(device); int permission = mAdapterService.getPhonebookAccessPermission(device); Log.d(TAG, "getPhonebookAccessPermission() = " + permission); Log.d(TAG, "getPhonebookAccessPermission() = " + permission); if (permission == ACCESS_ALLOWED) { if (permission == ACCESS_ALLOWED) { Loading Loading @@ -1022,7 +987,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect mPbapStateMachineMap.clear(); mPbapStateMachineMap.clear(); } } mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); mSessionStatusHandler.sendEmptyMessage(START_LISTENER); } } private void loadAllContacts() { private void loadAllContacts() { Loading
android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java +26 −26 Original line number Original line Diff line number Diff line Loading @@ -25,13 +25,16 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.os.Message; import android.os.Message; import android.os.UserManager; import android.os.test.TestLooper; import android.os.test.TestLooper; import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; Loading @@ -53,58 +56,55 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule; import java.util.List; @MediumTest @MediumTest @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) public class BluetoothPbapServiceTest { public class BluetoothPbapServiceTest { private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00"; private BluetoothPbapService mService; private BluetoothAdapter mAdapter = null; private BluetoothDevice mRemoteDevice; private boolean mIsAdapterServiceSet; private boolean mIsBluetoothPabpServiceStarted; private TestLooper mTestLooper; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private AdapterService mAdapterService; @Mock private AdapterService mAdapterService; @Mock private DatabaseManager mDatabaseManager; @Mock private DatabaseManager mDatabaseManager; @Mock private NotificationManager mNotificationManager; @Spy private BluetoothMethodProxy mMethodProxy = BluetoothMethodProxy.getInstance(); @Spy private BluetoothMethodProxy mMethodProxy = BluetoothMethodProxy.getInstance(); private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); private final BluetoothDevice mRemoteDevice = TestUtils.getTestDevice(mAdapter, 42); private final Context mTargetContext = InstrumentationRegistry.getTargetContext(); private final MockContentResolver mMockContentResolver = new MockContentResolver(mTargetContext); private BluetoothPbapService mService; private TestLooper mTestLooper; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Context targetContext = InstrumentationRegistry.getTargetContext(); doReturn(mTargetContext.getPackageName()).when(mAdapterService).getPackageName(); doReturn(mTargetContext.getPackageManager()).when(mAdapterService).getPackageManager(); doReturn(mMockContentResolver).when(mAdapterService).getContentResolver(); UserManager manager = TestUtils.mockGetSystemService( mAdapterService, Context.USER_SERVICE, UserManager.class); doReturn(List.of()).when(manager).getAllProfiles(); mTestLooper = new TestLooper(); mTestLooper = new TestLooper(); BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); doReturn(mTestLooper.getLooper()).when(mMethodProxy).handlerThreadGetLooper(any()); doReturn(mTestLooper.getLooper()).when(mMethodProxy).handlerThreadGetLooper(any()); doNothing().when(mMethodProxy).threadStart(any()); doNothing().when(mMethodProxy).threadStart(any()); mTestLooper.startAutoDispatch(); mTestLooper.startAutoDispatch(); TestUtils.setAdapterService(mAdapterService); mIsAdapterServiceSet = true; doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); mService = new BluetoothPbapService(targetContext); mService = new BluetoothPbapService(mAdapterService, mNotificationManager); mService.start(); mService.start(); mService.setAvailable(true); mService.setAvailable(true); mIsBluetoothPabpServiceStarted = true; // Try getting the Bluetooth adapter mAdapter = BluetoothAdapter.getDefaultAdapter(); assertThat(mAdapter).isNotNull(); mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); } } @After @After public void tearDown() throws Exception { public void tearDown() throws Exception { mTestLooper.stopAutoDispatchAndIgnoreExceptions(); mTestLooper.stopAutoDispatchAndIgnoreExceptions(); BluetoothMethodProxy.setInstanceForTesting(null); BluetoothMethodProxy.setInstanceForTesting(null); if (!mIsAdapterServiceSet) { return; } if (mIsBluetoothPabpServiceStarted) { mService.stop(); mService.stop(); mService = BluetoothPbapService.getBluetoothPbapService(); assertThat(BluetoothPbapService.getBluetoothPbapService()).isNull(); assertThat(mService).isNull(); } TestUtils.clearAdapterService(mAdapterService); } } @Test @Test Loading