summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java46
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl7
-rw-r--r--core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl35
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java33
7 files changed, 193 insertions, 13 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a4cea3364998..ab29df357268 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1051,6 +1051,52 @@ public final class AccessibilityManager {
}
/**
+ * Registers callback for when user initialization has completed.
+ * Does nothing if the same callback is already registered.
+ *
+ * @param callback The callback to be registered
+ * @hide
+ */
+ public void registerUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.registerUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re);
+ }
+ }
+
+ /**
+ * Unregisters callback for when user initialization has completed.
+ *
+ * @param callback The callback to be unregistered
+ * @hide
+ */
+ public void unregisterUserInitializationCompleteCallback(
+ @NonNull IUserInitializationCompleteCallback callback) {
+ IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.unregisterUserInitializationCompleteCallback(callback);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG,
+ "Error while unregistering userInitializationCompleteCallback. ", re);
+ }
+ }
+
+ /**
* Whether the current accessibility request comes from an
* {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
* property set to true.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 72a1fe424906..bf79a2c6c6ea 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,6 +29,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.InputEvent;
import android.view.IWindow;
import android.view.MagnificationSpec;
@@ -192,4 +193,10 @@ interface IAccessibilityManager {
@EnforcePermission("MANAGE_ACCESSIBILITY")
Bundle getA11yFeatureToTileMap(int userId);
+
+ @RequiresNoPermission
+ void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
+
+ @RequiresNoPermission
+ void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
}
diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
new file mode 100644
index 000000000000..fe6c8e25dd00
--- /dev/null
+++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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 android.view.accessibility;
+
+/**
+ * A callback for when a new user finishes initializing
+ * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock.
+ * oneway allows it to return immediately and not hold the lock for longer than is necessary.
+ * @hide
+ */
+
+oneway interface IUserInitializationCompleteCallback {
+
+ /**
+ * Called when a user initialization completes.
+ *
+ * @param userId the id of the initialized user
+ */
+ @RequiresNoPermission
+ void onUserInitializationComplete(int userId);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index e4b7b7e69c61..275147e6694c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -21,11 +21,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import android.content.Context;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.annotation.MainThread;
@@ -68,6 +70,9 @@ public class AccessibilityFloatingMenuController implements
private int mBtnMode;
private String mBtnTargets;
private boolean mIsKeyguardVisible;
+ private boolean mIsUserInInitialization;
+ @VisibleForTesting
+ Handler mHandler;
@VisibleForTesting
final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@@ -86,18 +91,14 @@ public class AccessibilityFloatingMenuController implements
@Override
public void onUserSwitching(int userId) {
destroyFloatingMenu();
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
- mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
- mBtnTargets =
- mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
- handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
+ mIsUserInInitialization = true;
}
};
+ @VisibleForTesting
+ final UserInitializationCompleteCallback mUserInitializationCompleteCallback =
+ new UserInitializationCompleteCallback();
+
@Inject
public AccessibilityFloatingMenuController(Context context,
WindowManager windowManager,
@@ -109,7 +110,8 @@ public class AccessibilityFloatingMenuController implements
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecureSettings secureSettings,
DisplayTracker displayTracker,
- NavigationModeController navigationModeController) {
+ NavigationModeController navigationModeController,
+ Handler handler) {
mContext = context;
mWindowManager = windowManager;
mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
@@ -121,6 +123,7 @@ public class AccessibilityFloatingMenuController implements
mSecureSettings = secureSettings;
mDisplayTracker = displayTracker;
mNavigationModeController = navigationModeController;
+ mHandler = handler;
mIsKeyguardVisible = false;
}
@@ -159,6 +162,8 @@ public class AccessibilityFloatingMenuController implements
mAccessibilityButtonModeObserver.addListener(this);
mAccessibilityButtonTargetsObserver.addListener(this);
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+ mAccessibilityManager.registerUserInitializationCompleteCallback(
+ mUserInitializationCompleteCallback);
}
/**
@@ -172,7 +177,7 @@ public class AccessibilityFloatingMenuController implements
*/
private void handleFloatingMenuVisibility(boolean keyguardVisible,
@AccessibilityButtonMode int mode, String targets) {
- if (keyguardVisible) {
+ if (keyguardVisible || mIsUserInInitialization) {
destroyFloatingMenu();
return;
}
@@ -210,4 +215,18 @@ public class AccessibilityFloatingMenuController implements
mFloatingMenu.hide();
mFloatingMenu = null;
}
+
+ class UserInitializationCompleteCallback
+ extends IUserInitializationCompleteCallback.Stub {
+ @Override
+ public void onUserInitializationComplete(int userId) {
+ mIsUserInInitialization = false;
+ mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+ mBtnTargets =
+ mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
+ mHandler.post(
+ () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets));
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 113a8c05ee66..5e37d4cd1faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.TestableLooper;
@@ -80,6 +81,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
+ private TestableLooper mTestableLooper;
@Mock
private AccessibilityButtonTargetsObserver mTargetsObserver;
@Mock
@@ -108,6 +110,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+ mTestableLooper = TestableLooper.get(this);
when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
.thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
@@ -231,7 +234,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mKeyguardCallback.onKeyguardVisibilityChanged(false);
mKeyguardCallback.onUserSwitching(fakeUserId);
- mKeyguardCallback.onUserSwitchComplete(fakeUserId);
+ mController.mUserInitializationCompleteCallback.onUserInitializationComplete(1);
+ mTestableLooper.processAllMessages();
assertThat(mController.mFloatingMenu).isNotNull();
}
@@ -346,7 +350,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings,
- displayTracker, mNavigationModeController);
+ displayTracker, mNavigationModeController, new Handler(
+ mTestableLooper.getLooper()));
controller.init();
return controller;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7cbb97e56b01..f1a8b5a96080 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -163,6 +163,7 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.inputmethod.EditorInfo;
import com.android.internal.R;
@@ -366,6 +367,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables =
new ArrayList<>();
+ @VisibleForTesting
+ final HashSet<IUserInitializationCompleteCallback>
+ mUserInitializationCompleteCallbacks =
+ new HashSet<IUserInitializationCompleteCallback>();
+
@GuardedBy("mLock")
private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -2034,6 +2040,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this),
WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
}
+
+ for (IUserInitializationCompleteCallback callback
+ : mUserInitializationCompleteCallbacks) {
+ try {
+ callback.onUserInitializationComplete(mCurrentUserId);
+ } catch (RemoteException re) {
+ Log.e("AccessibilityManagerService",
+ "Error while dispatching userInitializationComplete callback: ",
+ re);
+ }
+ }
}
}
@@ -6251,6 +6268,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
+ @RequiresNoPermission
+ public void registerUserInitializationCompleteCallback(
+ IUserInitializationCompleteCallback callback) {
+ synchronized (mLock) {
+ mUserInitializationCompleteCallbacks.add(callback);
+ }
+ }
+
+ @Override
+ @RequiresNoPermission
+ public void unregisterUserInitializationCompleteCallback(
+ IUserInitializationCompleteCallback callback) {
+ synchronized (mLock) {
+ mUserInitializationCompleteCallbacks.remove(callback);
+ }
+ }
+
+ @Override
@EnforcePermission(INJECT_EVENTS)
public void injectInputEventToInputFilter(InputEvent event) {
injectInputEventToInputFilter_enforcePermission();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index c8cbbb5957ec..2e6c93cb92aa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -102,6 +102,7 @@ import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
@@ -137,6 +138,7 @@ import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.mockito.internal.util.reflection.FieldReader;
import org.mockito.internal.util.reflection.FieldSetter;
import org.mockito.stubbing.Answer;
@@ -209,6 +211,7 @@ public class AccessibilityManagerServiceTest {
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
@Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private IAccessibilityManager mA11yManagerServiceOnDevice;
private AccessibilityServiceConnection mAccessibilityServiceConnection;
@@ -2042,6 +2045,36 @@ public class AccessibilityManagerServiceTest {
.isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
}
+ @Test
+ public void registerUserInitializationCompleteCallback_isRegistered() {
+ mA11yms.mUserInitializationCompleteCallbacks.clear();
+
+ mA11yms.registerUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+ assertThat(mA11yms.mUserInitializationCompleteCallbacks).containsExactly(
+ mUserInitializationCompleteCallback);
+ }
+
+ @Test
+ public void unregisterUserInitializationCompleteCallback_isUnregistered() {
+ mA11yms.mUserInitializationCompleteCallbacks.clear();
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+ mA11yms.unregisterUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+ assertThat(mA11yms.mUserInitializationCompleteCallbacks).isEmpty();
+ }
+
+ @Test
+ public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException {
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+ mA11yms.switchUser(UserHandle.MIN_SECONDARY_USER_ID);
+
+ verify(mUserInitializationCompleteCallback).onUserInitializationComplete(
+ UserHandle.MIN_SECONDARY_USER_ID);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(