summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java45
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java49
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java132
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java31
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java4
6 files changed, 245 insertions, 18 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 7124096e31b8..bc0a1ac847df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -26,9 +26,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.R;
import java.util.ArrayList;
@@ -54,21 +58,32 @@ public class BluetoothEventManager {
private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver();
private final Collection<BluetoothCallback> mCallbacks = new ArrayList<>();
private final android.os.Handler mReceiverHandler;
+ private final UserHandle mUserHandle;
private final Context mContext;
interface Handler {
void onReceive(Context context, Intent intent, BluetoothDevice device);
}
+ /**
+ * Creates BluetoothEventManager with the ability to pass in {@link UserHandle} that tells it to
+ * listen for bluetooth events for that particular userHandle.
+ *
+ * <p> If passing in userHandle that's different from the user running the process,
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission is required. If
+ * userHandle passed in is {@code null}, we register event receiver for the
+ * {@code context.getUser()} handle.
+ */
BluetoothEventManager(LocalBluetoothAdapter adapter,
CachedBluetoothDeviceManager deviceManager, Context context,
- android.os.Handler handler) {
+ android.os.Handler handler, @Nullable UserHandle userHandle) {
mLocalAdapter = adapter;
mDeviceManager = deviceManager;
mAdapterIntentFilter = new IntentFilter();
mProfileIntentFilter = new IntentFilter();
mHandlerMap = new HashMap<>();
mContext = context;
+ mUserHandle = userHandle;
mReceiverHandler = handler;
// Bluetooth on/off broadcasts
@@ -104,7 +119,7 @@ public class BluetoothEventManager {
addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
new AudioModeChangedHandler());
- mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
+ registerAdapterIntentReceiver();
}
/** Register to start receiving callbacks for Bluetooth events. */
@@ -121,10 +136,31 @@ public class BluetoothEventManager {
}
}
+ @VisibleForTesting
void registerProfileIntentReceiver() {
- mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
+ registerIntentReceiver(mProfileBroadcastReceiver, mProfileIntentFilter);
+ }
+
+ @VisibleForTesting
+ void registerAdapterIntentReceiver() {
+ registerIntentReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+ }
+
+ /**
+ * Registers the provided receiver to receive the broadcasts that correspond to the
+ * passed intent filter, in the context of the provided handler.
+ */
+ private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ if (mUserHandle == null) {
+ // If userHandle has not been provided, simply call registerReceiver.
+ mContext.registerReceiver(receiver, filter, null, mReceiverHandler);
+ } else {
+ // userHandle was explicitly specified, so need to call multi-user aware API.
+ mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler);
+ }
}
+ @VisibleForTesting
void addProfileHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mProfileIntentFilter.addAction(action);
@@ -201,7 +237,8 @@ public class BluetoothEventManager {
}
}
- private void addHandler(String action, Handler handler) {
+ @VisibleForTesting
+ void addHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mAdapterIntentFilter.addAction(action);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
index 7fe6205ed343..53c6075ccff4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
@@ -18,10 +18,14 @@ package com.android.settingslib.bluetooth;
import android.content.Context;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.Log;
import java.lang.ref.WeakReference;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
/**
* LocalBluetoothManager provides a simplified interface on top of a subset of
* the Bluetooth API. Note that {@link #getInstance} will return null
@@ -49,6 +53,7 @@ public class LocalBluetoothManager {
/** The broadcast receiver event manager. */
private final BluetoothEventManager mEventManager;
+ @Nullable
public static synchronized LocalBluetoothManager getInstance(Context context,
BluetoothManagerCallback onInitCallback) {
if (sInstance == null) {
@@ -57,10 +62,11 @@ public class LocalBluetoothManager {
return null;
}
// This will be around as long as this process is
- Context appContext = context.getApplicationContext();
- sInstance = new LocalBluetoothManager(adapter, appContext, null);
+ sInstance = new LocalBluetoothManager(adapter, context, /* handler= */ null,
+ /* userHandle= */ null);
if (onInitCallback != null) {
- onInitCallback.onBluetoothManagerInitialized(appContext, sInstance);
+ onInitCallback.onBluetoothManagerInitialized(context.getApplicationContext(),
+ sInstance);
}
}
@@ -71,22 +77,43 @@ public class LocalBluetoothManager {
* Returns a new instance of {@link LocalBluetoothManager} or null if Bluetooth is not
* supported for this hardware. This instance should be globally cached by the caller.
*/
+ @Nullable
public static LocalBluetoothManager create(Context context, Handler handler) {
LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
if (adapter == null) {
return null;
}
- return new LocalBluetoothManager(adapter, context.getApplicationContext(), handler);
+ return new LocalBluetoothManager(adapter, context, handler, /* userHandle= */ null);
+ }
+
+ /**
+ * Returns a new instance of {@link LocalBluetoothManager} or null if Bluetooth is not
+ * supported for this hardware. This instance should be globally cached by the caller.
+ *
+ * <p> Allows to specify a {@link UserHandle} for which to receive bluetooth events.
+ *
+ * <p> Requires {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ */
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public static LocalBluetoothManager create(Context context, Handler handler,
+ UserHandle userHandle) {
+ LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
+ if (adapter == null) {
+ return null;
+ }
+ return new LocalBluetoothManager(adapter, context, handler,
+ userHandle);
}
- private LocalBluetoothManager(
- LocalBluetoothAdapter adapter, Context context, Handler handler) {
- mContext = context;
+ private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context, Handler handler,
+ UserHandle userHandle) {
+ mContext = context.getApplicationContext();
mLocalAdapter = adapter;
- mCachedDeviceManager = new CachedBluetoothDeviceManager(context, this);
- mEventManager = new BluetoothEventManager(mLocalAdapter,
- mCachedDeviceManager, context, handler);
- mProfileManager = new LocalBluetoothProfileManager(context,
+ mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, this);
+ mEventManager = new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mContext,
+ handler, userHandle);
+ mProfileManager = new LocalBluetoothProfileManager(mContext,
mLocalAdapter, mCachedDeviceManager, mEventManager);
mProfileManager.updateLocalProfiles();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
new file mode 100644
index 000000000000..d0ab46a2f30d
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Test that verifies that BluetoothEventManager can receive broadcasts for non-current
+ * users for all bluetooth events.
+ *
+ * <p>Creation and deletion of users takes a long time, so marking this as a LargeTest.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothEventManagerIntegTest {
+ private static final int LATCH_TIMEOUT = 4;
+
+ private Context mContext;
+ private UserManager mUserManager;
+ private BluetoothEventManager mBluetoothEventManager;
+
+ private UserInfo mOtherUser;
+ private final Intent mTestIntent = new Intent("Test intent");
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUserManager = UserManager.get(mContext);
+
+ mBluetoothEventManager = new BluetoothEventManager(
+ mock(LocalBluetoothAdapter.class), mock(CachedBluetoothDeviceManager.class),
+ mContext, /* handler= */ null, UserHandle.ALL);
+
+ // Create and start another user in the background.
+ mOtherUser = mUserManager.createUser("TestUser", /* flags= */ 0);
+ try {
+ ActivityManager.getService().startUserInBackground(mOtherUser.id);
+ } catch (RemoteException e) {
+ fail("Count't create an additional user.");
+ }
+ }
+
+ @After
+ public void tearDown() {
+ if (mOtherUser != null) {
+ mUserManager.removeUser(mOtherUser.id);
+ }
+ }
+
+ /**
+ * Verify that MultiUserAwareBluetoothEventManager's adapter receiver handles events coming from
+ * users other than current user.
+ */
+ @Test
+ public void registerAdapterReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
+ throws Exception {
+ // Create a latch to listen for the intent.
+ final CountDownLatch broadcastLatch = new CountDownLatch(1);
+
+ // Register adapter receiver.
+ mBluetoothEventManager.addHandler(mTestIntent.getAction(),
+ (context, intent, device) -> broadcastLatch.countDown());
+ mBluetoothEventManager.registerAdapterIntentReceiver();
+
+ // Send broadcast from another user.
+ mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
+
+ // Wait to receive it.
+ assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
+ }
+
+ /**
+ * Verify that MultiUserAwareBluetoothEventManager's profile receiver handles events coming from
+ * users other than current user.
+ */
+ @Test
+ public void registerProfileReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
+ throws Exception {
+ // Create a latch to listen for the intent.
+ final CountDownLatch broadcastLatch = new CountDownLatch(1);
+
+ // Register profile receiver.
+ mBluetoothEventManager.addProfileHandler(mTestIntent.getAction(),
+ (context, intent, device) -> broadcastLatch.countDown());
+ mBluetoothEventManager.registerProfileIntentReceiver();
+
+ // Send broadcast from another user.
+ mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
+
+ // Wait to receive it.
+ assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 14bfb2798183..6648eddb3cbc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -15,12 +15,19 @@
*/
package com.android.settingslib.bluetooth;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.UserHandle;
import android.telephony.TelephonyManager;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -54,7 +61,29 @@ public class BluetoothEventManagerTest {
mContext = RuntimeEnvironment.application;
mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
- mCachedDeviceManager, mContext, null);
+ mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
+ }
+
+ @Test
+ public void ifUserHandleIsNull_registerReceiverIsCalled() {
+ Context mockContext = mock(Context.class);
+ BluetoothEventManager eventManager =
+ new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
+ /* handler= */ null, /* userHandle= */ null);
+
+ verify(mockContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
+ eq(null), eq(null));
+ }
+
+ @Test
+ public void ifUserHandleSpecified_registerReceiverAsUserIsCalled() {
+ Context mockContext = mock(Context.class);
+ BluetoothEventManager eventManager =
+ new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
+ /* handler= */ null, UserHandle.ALL);
+
+ verify(mockContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
+ any(IntentFilter.class), eq(null), eq(null));
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 6f4c29286c53..235699381bb1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -76,7 +76,7 @@ public class LocalBluetoothProfileManagerTest {
mContext = spy(RuntimeEnvironment.application);
mLocalBluetoothAdapter = LocalBluetoothAdapter.getInstance();
mEventManager = spy(new BluetoothEventManager(mLocalBluetoothAdapter, mDeviceManager,
- mContext, null));
+ mContext, /* handler= */ null, /* userHandle= */ null));
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 2c821b25f0a5..4afb79d061ad 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -22,6 +22,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.view.IWindowManager;
@@ -295,7 +296,8 @@ public class Dependency extends SystemUI {
new PluginDependencyProvider(get(PluginManager.class)));
mProviders.put(LocalBluetoothManager.class, () ->
- LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER)));
+ LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
+ UserHandle.ALL));
mProviders.put(VolumeDialogController.class, () ->
new VolumeDialogControllerImpl(mContext));