summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java5
-rw-r--r--location/java/android/location/LocationManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt)7
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt240
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt292
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java3
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java30
-rw-r--r--services/core/java/com/android/server/location/LocationProviderManager.java217
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java22
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java20
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java23
-rw-r--r--services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java4
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java187
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerRegistration.java54
-rw-r--r--services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java2
-rw-r--r--services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java75
-rw-r--r--services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java13
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java12
42 files changed, 1234 insertions, 554 deletions
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index eb59f0f59be1..da26930ca727 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -409,6 +409,11 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier";
+ /**
+ * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms
+ */
+ public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1803027743f6..6fc702e4a068 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -29,6 +29,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -215,6 +217,7 @@ public class LocationManager {
* @see #EXTRA_PROVIDER_ENABLED
* @see #isProviderEnabled(String)
*/
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
/**
@@ -243,6 +246,7 @@ public class LocationManager {
* @see #EXTRA_LOCATION_ENABLED
* @see #isLocationEnabled()
*/
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
/**
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index d1149d37d431..f4c865e1d131 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -38,7 +38,7 @@ import java.util.Set;
*
* NOTE: Clients of this class should take care to pass in the correct user context when querying
* settings, otherwise you will always read/write for user 0 which is almost never what you want.
- * See {@link CurrentUserContextTracker} for a simple way to get the current context
+ * See {@link UserContextProvider} for a simple way to get the current context
*/
public final class Prefs {
private Prefs() {} // no instantation
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d213ac1132bb..3e64749c0dce 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -130,7 +130,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -252,7 +252,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final RingerModeTracker mRingerModeTracker;
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
private Handler mMainHandler;
- private CurrentUserContextTracker mCurrentUserContextTracker;
+ private UserContextProvider mUserContextProvider;
@VisibleForTesting
boolean mShowLockScreenCardsAndControls = false;
@@ -313,7 +313,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
ControlsComponent controlsComponent,
- CurrentUserContextTracker currentUserContextTracker) {
+ UserContextProvider userContextProvider) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -342,7 +342,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mControlsControllerOptional = controlsComponent.getControlsController();
mSysUiState = sysUiState;
mMainHandler = handler;
- mCurrentUserContextTracker = currentUserContextTracker;
+ mUserContextProvider = userContextProvider;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -436,7 +436,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
String[] preferredControlsPackages = mContext.getResources()
.getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages);
- SharedPreferences prefs = mCurrentUserContextTracker.getCurrentUserContext()
+ SharedPreferences prefs = mUserContextProvider.getUserContext()
.getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
Collections.emptySet());
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 8ec3db59117d..3bf118d1f39f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -40,7 +40,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.io.IOException;
@@ -79,12 +79,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private final Executor mLongExecutor;
private final UiEventLogger mUiEventLogger;
private final NotificationManager mNotificationManager;
- private final CurrentUserContextTracker mUserContextTracker;
+ private final UserContextProvider mUserContextTracker;
@Inject
public RecordingService(RecordingController controller, @LongRunning Executor executor,
UiEventLogger uiEventLogger, NotificationManager notificationManager,
- CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
+ UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
mController = controller;
mLongExecutor = executor;
mUiEventLogger = uiEventLogger;
@@ -120,7 +120,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
String action = intent.getAction();
Log.d(TAG, "onStartCommand " + action);
- int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId();
+ int mCurrentUserId = mUserContextTracker.getUserContext().getUserId();
UserHandle currentUser = new UserHandle(mCurrentUserId);
switch (action) {
case ACTION_START:
@@ -136,7 +136,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
setTapsVisible(mShowTaps);
mRecorder = new ScreenMediaRecorder(
- mUserContextTracker.getCurrentUserContext(),
+ mUserContextTracker.getUserContext(),
mCurrentUserId,
mAudioSource,
this
@@ -156,7 +156,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
// we want to post the notifications for that user, which is NOT current user
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId == -1) {
- userId = mUserContextTracker.getCurrentUserContext().getUserId();
+ userId = mUserContextTracker.getUserContext().getUserId();
}
Log.d(TAG, "notifying for user " + userId);
stopRecording(userId);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index dc47ab4dff63..2b62a29587e6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -35,7 +35,7 @@ import android.widget.Spinner;
import android.widget.Switch;
import com.android.systemui.R;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import java.util.ArrayList;
import java.util.List;
@@ -51,7 +51,7 @@ public class ScreenRecordDialog extends Activity {
private static final String TAG = "ScreenRecordDialog";
private final RecordingController mController;
- private final CurrentUserContextTracker mCurrentUserContextTracker;
+ private final UserContextProvider mUserContextProvider;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
@@ -59,9 +59,9 @@ public class ScreenRecordDialog extends Activity {
@Inject
public ScreenRecordDialog(RecordingController controller,
- CurrentUserContextTracker currentUserContextTracker) {
+ UserContextProvider userContextProvider) {
mController = controller;
- mCurrentUserContextTracker = currentUserContextTracker;
+ mUserContextProvider = userContextProvider;
}
@Override
@@ -108,7 +108,7 @@ public class ScreenRecordDialog extends Activity {
}
private void requestScreenCapture() {
- Context userContext = mCurrentUserContextTracker.getCurrentUserContext();
+ Context userContext = mUserContextProvider.getUserContext();
boolean showTaps = mTapsSwitch.isChecked();
ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
deleted file mode 100644
index d7c4caaa4f9d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 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.systemui.settings
-
-import android.content.ContentResolver
-import android.content.Context
-import android.os.UserHandle
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.util.Assert
-import java.lang.IllegalStateException
-
-/**
- * Tracks a reference to the context for the current user
- *
- * Constructor is injected at SettingsModule
- */
-class CurrentUserContextTracker internal constructor(
- private val sysuiContext: Context,
- broadcastDispatcher: BroadcastDispatcher
-) : CurrentUserContentResolverProvider {
- private val userTracker: CurrentUserTracker
- private var initialized = false
-
- private var _curUserContext: Context? = null
- val currentUserContext: Context
- get() {
- if (!initialized) {
- throw IllegalStateException("Must initialize before getting context")
- }
- return _curUserContext!!
- }
-
- override val currentUserContentResolver: ContentResolver
- get() = currentUserContext.contentResolver
-
- init {
- userTracker = object : CurrentUserTracker(broadcastDispatcher) {
- override fun onUserSwitched(newUserId: Int) {
- handleUserSwitched(newUserId)
- }
- }
- }
-
- fun initialize() {
- initialized = true
- userTracker.startTracking()
- _curUserContext = makeUserContext(userTracker.currentUserId)
- }
-
- @VisibleForTesting
- fun handleUserSwitched(newUserId: Int) {
- _curUserContext = makeUserContext(newUserId)
- }
-
- private fun makeUserContext(uid: Int): Context {
- Assert.isMainThread()
- return sysuiContext.createContextAsUser(UserHandle.of(uid), 0)
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
index 9d05843b42bf..e0c0c15cba31 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
@@ -18,7 +18,10 @@ package com.android.systemui.settings
import android.content.ContentResolver
-interface CurrentUserContentResolverProvider {
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContentResolverProvider {
- val currentUserContentResolver: ContentResolver
+ val userContentResolver: ContentResolver
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
new file mode 100644
index 000000000000..27af15222327
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.systemui.settings
+
+import android.content.Context
+
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContextProvider {
+ val userContext: Context
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
new file mode 100644
index 000000000000..26d408fe4ab7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.systemui.settings
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import java.util.concurrent.Executor
+
+/**
+ * User tracker for SystemUI.
+ *
+ * This tracker provides async access to current user information, as well as callbacks for
+ * user/profile change.
+ */
+interface UserTracker : UserContentResolverProvider, UserContextProvider {
+
+ /**
+ * Current user's id.
+ */
+ val userId: Int
+
+ /**
+ * [UserHandle] for current user
+ */
+ val userHandle: UserHandle
+
+ /**
+ * List of profiles associated with the current user.
+ */
+ val userProfiles: List<UserInfo>
+
+ /**
+ * Add a [Callback] to be notified of chances, on a particular [Executor]
+ */
+ fun addCallback(callback: Callback, executor: Executor)
+
+ /**
+ * Remove a [Callback] previously added.
+ */
+ fun removeCallback(callback: Callback)
+
+ /**
+ * Ćallback for notifying of changes.
+ */
+ interface Callback {
+
+ /**
+ * Notifies that the current user has changed.
+ */
+ @JvmDefault
+ fun onUserChanged(newUser: Int, userContext: Context) {}
+
+ /**
+ * Notifies that the current user's profiles have changed.
+ */
+ @JvmDefault
+ fun onProfilesChanged(profiles: List<UserInfo>) {}
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
new file mode 100644
index 000000000000..4cc0eeee712c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 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.systemui.settings
+
+import android.content.BroadcastReceiver
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.WorkerThread
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.Assert
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/**
+ * SystemUI cache for keeping track of the current user and associated values.
+ *
+ * The values provided asynchronously are NOT copies, but shared among all requesters. Do not
+ * modify them.
+ *
+ * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as
+ * soon as possible (and reduce its dependency graph).
+ * Other classes that want to listen to the broadcasts listened here SHOULD
+ * subscribe to this class instead.
+ *
+ * @see UserTracker
+ *
+ * Class constructed and initialized in [SettingsModule].
+ */
+class UserTrackerImpl internal constructor(
+ private val context: Context,
+ private val userManager: UserManager,
+ private val dumpManager: DumpManager,
+ private val backgroundHandler: Handler
+) : UserTracker, Dumpable, BroadcastReceiver() {
+
+ companion object {
+ private const val TAG = "UserTrackerImpl"
+ }
+
+ var initialized = false
+ private set
+
+ private val mutex = Any()
+
+ override var userId: Int by SynchronizedDelegate(context.userId)
+ private set
+
+ override var userHandle: UserHandle by SynchronizedDelegate(context.user)
+ private set
+
+ override var userContext: Context by SynchronizedDelegate(context)
+ private set
+
+ override val userContentResolver: ContentResolver
+ get() = userContext.contentResolver
+
+ /**
+ * Returns a [List<UserInfo>] of all profiles associated with the current user.
+ *
+ * The list returned is not a copy, so a copy should be made if its elements need to be
+ * modified.
+ */
+ override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList())
+ private set
+
+ @GuardedBy("callbacks")
+ private val callbacks: MutableList<DataItem> = ArrayList()
+
+ fun initialize(startingUser: Int) {
+ if (initialized) {
+ return
+ }
+ initialized = true
+ setUserIdInternal(startingUser)
+
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ }
+ context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+
+ dumpManager.registerDumpable(TAG, this)
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ when (intent.action) {
+ Intent.ACTION_USER_SWITCHED -> {
+ handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
+ }
+ Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE -> {
+ handleProfilesChanged()
+ }
+ }
+ }
+
+ private fun setUserIdInternal(user: Int): Pair<Context, List<UserInfo>> {
+ val profiles = userManager.getProfiles(user)
+ val handle = UserHandle(user)
+ val ctx = context.createContextAsUser(handle, 0)
+
+ synchronized(mutex) {
+ userId = user
+ userHandle = handle
+ userContext = ctx
+ userProfiles = profiles.map { UserInfo(it) }
+ }
+ return ctx to profiles
+ }
+
+ @WorkerThread
+ private fun handleSwitchUser(newUser: Int) {
+ Assert.isNotMainThread()
+ if (newUser == UserHandle.USER_NULL) {
+ Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
+ return
+ }
+
+ if (newUser == userId) return
+ Log.i(TAG, "Switching to user $newUser")
+
+ val (ctx, profiles) = setUserIdInternal(newUser)
+
+ notifySubscribers {
+ onUserChanged(newUser, ctx)
+ onProfilesChanged(profiles)
+ }
+ }
+
+ @WorkerThread
+ private fun handleProfilesChanged() {
+ Assert.isNotMainThread()
+
+ val profiles = userManager.getProfiles(userId)
+ synchronized(mutex) {
+ userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
+ }
+ notifySubscribers {
+ onProfilesChanged(profiles)
+ }
+ }
+
+ override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
+ synchronized(callbacks) {
+ callbacks.add(DataItem(WeakReference(callback), executor))
+ }
+ }
+
+ override fun removeCallback(callback: UserTracker.Callback) {
+ synchronized(callbacks) {
+ callbacks.removeIf { it.sameOrEmpty(callback) }
+ }
+ }
+
+ private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+ val list = synchronized(callbacks) {
+ callbacks.toList()
+ }
+ list.forEach {
+ if (it.callback.get() != null) {
+ it.executor.execute {
+ it.callback.get()?.action()
+ }
+ }
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("Initialized: $initialized")
+ if (initialized) {
+ pw.println("userId: $userId")
+ val ids = userProfiles.map { it.id }
+ pw.println("userProfiles: $ids")
+ }
+ val list = synchronized(callbacks) {
+ callbacks.toList()
+ }
+ pw.println("Callbacks:")
+ list.forEach {
+ it.callback.get()?.let {
+ pw.println(" $it")
+ }
+ }
+ }
+
+ private class SynchronizedDelegate<T : Any>(
+ private var value: T
+ ) : ReadWriteProperty<UserTrackerImpl, T> {
+
+ @GuardedBy("mutex")
+ override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T {
+ if (!thisRef.initialized) {
+ throw IllegalStateException("Must initialize before getting ${property.name}")
+ }
+ return synchronized(thisRef.mutex) { value }
+ }
+
+ @GuardedBy("mutex")
+ override fun setValue(thisRef: UserTrackerImpl, property: KProperty<*>, value: T) {
+ synchronized(thisRef.mutex) { this.value = value }
+ }
+ }
+}
+
+private data class DataItem(
+ val callback: WeakReference<UserTracker.Callback>,
+ val executor: Executor
+) {
+ fun sameOrEmpty(other: UserTracker.Callback): Boolean {
+ return callback.get()?.equals(other) ?: true
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
index b1ed77275187..7084d3ffc9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
@@ -16,12 +16,18 @@
package com.android.systemui.settings.dagger;
+import android.app.ActivityManager;
import android.content.Context;
+import android.os.Handler;
+import android.os.UserManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.settings.CurrentUserContentResolverProvider;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserContentResolverProvider;
+import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.settings.UserTrackerImpl;
import dagger.Binds;
import dagger.Module;
@@ -33,22 +39,27 @@ import dagger.Provides;
@Module
public abstract class SettingsModule {
- /**
- * Provides and initializes a CurrentUserContextTracker
- */
+
+ @Binds
+ @SysUISingleton
+ abstract UserContextProvider bindUserContextProvider(UserTracker tracker);
+
+ @Binds
+ @SysUISingleton
+ abstract UserContentResolverProvider bindUserContentResolverProvider(
+ UserTracker tracker);
+
@SysUISingleton
@Provides
- static CurrentUserContextTracker provideCurrentUserContextTracker(
+ static UserTracker provideUserTracker(
Context context,
- BroadcastDispatcher broadcastDispatcher) {
- CurrentUserContextTracker tracker =
- new CurrentUserContextTracker(context, broadcastDispatcher);
- tracker.initialize();
+ UserManager userManager,
+ DumpManager dumpManager,
+ @Background Handler handler
+ ) {
+ int startingUser = ActivityManager.getCurrentUser();
+ UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+ tracker.initialize(startingUser);
return tracker;
}
-
- @Binds
- @SysUISingleton
- abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker(
- CurrentUserContextTracker tracker);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index eca4c8082dfe..0df69a0a1f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -185,6 +185,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
mAlertEntries.put(entry.getKey(), alertEntry);
onAlertEntryAdded(alertEntry);
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.setIsAlerting(true);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 387247eb6c5b..8ce9d944b865 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -177,6 +177,7 @@ public final class NotificationEntry extends ListEntry {
@Nullable private Long mPendingAnimationDuration;
private boolean mIsMarkedForUserTriggeredMovement;
private boolean mShelfIconVisible;
+ private boolean mIsAlerting;
/**
* @param sbn the StatusBarNotification from system server
@@ -955,6 +956,14 @@ public final class NotificationEntry extends ListEntry {
mIsMarkedForUserTriggeredMovement = marked;
}
+ public void setIsAlerting(boolean isAlerting) {
+ mIsAlerting = isAlerting;
+ }
+
+ public boolean isAlerting() {
+ return mIsAlerting;
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6d01324f1b7b..f3ed95bd2d76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -34,7 +34,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -127,7 +127,7 @@ public interface NotificationsModule {
LauncherApps launcherApps,
ShortcutManager shortcutManager,
ChannelEditorDialogController channelEditorDialogController,
- CurrentUserContextTracker contextTracker,
+ UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
BubbleController bubbleController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 60074f608969..7d418f30e4c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -52,7 +52,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -121,7 +121,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final INotificationManager mNotificationManager;
private final LauncherApps mLauncherApps;
private final ShortcutManager mShortcutManager;
- private final CurrentUserContextTracker mContextTracker;
+ private final UserContextProvider mContextTracker;
private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
private final UiEventLogger mUiEventLogger;
@@ -138,7 +138,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
LauncherApps launcherApps,
ShortcutManager shortcutManager,
ChannelEditorDialogController channelEditorDialogController,
- CurrentUserContextTracker contextTracker,
+ UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
BubbleController bubbleController,
@@ -484,7 +484,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
onSettingsClick,
onSnoozeClickListener,
iconFactoryLoader,
- mContextTracker.getCurrentUserContext(),
+ mContextTracker.getUserContext(),
mBuilderProvider,
mDeviceProvisionedController.isDeviceProvisioned(),
mMainHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index d5e55315ff0a..0302b2b450e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -81,17 +81,17 @@ public class AmbientState {
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
private float mDozeAmount = 0.0f;
- private HeadsUpManager mHeadUpManager;
private Runnable mOnPulseHeightChangedListener;
private ExpandableNotificationRow mTrackedHeadsUpRow;
private float mAppearFraction;
+ /** Tracks the state from AlertingNotificationManager#hasNotifications() */
+ private boolean mHasAlertEntries;
+
public AmbientState(
Context context,
- @NonNull SectionProvider sectionProvider,
- HeadsUpManager headsUpManager) {
+ @NonNull SectionProvider sectionProvider) {
mSectionProvider = sectionProvider;
- mHeadUpManager = headsUpManager;
reload(context);
}
@@ -393,7 +393,7 @@ public class AmbientState {
}
public boolean hasPulsingNotifications() {
- return mPulsing && mHeadUpManager != null && mHeadUpManager.hasNotifications();
+ return mPulsing && mHasAlertEntries;
}
public void setPulsing(boolean hasPulsing) {
@@ -408,10 +408,7 @@ public class AmbientState {
}
public boolean isPulsing(NotificationEntry entry) {
- if (!mPulsing || mHeadUpManager == null) {
- return false;
- }
- return mHeadUpManager.isAlerting(entry.getKey());
+ return mPulsing && entry.isAlerting();
}
public boolean isPanelTracking() {
@@ -568,4 +565,8 @@ public class AmbientState {
public float getAppearFraction() {
return mAppearFraction;
}
+
+ public void setHasAlertEntries(boolean hasAlertEntries) {
+ mHasAlertEntries = hasAlertEntries;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b1a9efe40fdb..8a0fcf0d88f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -92,7 +92,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
@@ -102,7 +101,6 @@ import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -132,7 +130,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
@@ -340,13 +337,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private HeadsUpManagerPhone mHeadsUpManager;
private final NotificationRoundnessManager mRoundnessManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
- private FalsingManager mFalsingManager;
private boolean mAnimationRunning;
private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@@ -513,6 +508,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationStackScrollLayoutController mController;
private boolean mKeyguardMediaControllorVisible;
+ private NotificationEntry mTopHeadsUpEntry;
+ private long mNumHeadsUp;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
new ExpandableView.OnHeightChangedListener() {
@@ -562,8 +559,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
NotificationRoundnessManager notificationRoundnessManager,
DynamicPrivacyController dynamicPrivacyController,
SysuiStatusBarStateController statusbarStateController,
- HeadsUpManagerPhone headsUpManager,
- FalsingManager falsingManager,
NotificationGutsManager notificationGutsManager,
NotificationSectionsManager notificationSectionsManager,
ForegroundServiceSectionController fgsSectionController,
@@ -580,9 +575,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mRoundnessManager = notificationRoundnessManager;
mNotificationGutsManager = notificationGutsManager;
- mHeadsUpManager = headsUpManager;
- mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
- mFalsingManager = falsingManager;
mFgsSectionController = fgsSectionController;
mSectionsManager = notificationSectionsManager;
@@ -594,7 +586,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
});
mSections = mSectionsManager.createSectionsForBuckets();
- mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
+ mAmbientState = new AmbientState(context, mSectionsManager);
mBgColor = context.getColor(R.color.notification_shade_background_color);
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
@@ -750,27 +742,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return false;
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public RemoteInputController.Delegate createDelegate() {
- return new RemoteInputController.Delegate() {
- public void setRemoteInputActive(NotificationEntry entry,
- boolean remoteInputActive) {
- mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- entry.notifyHeightChanged(true /* needsAnimation */);
- updateFooter();
- }
-
- public void lockScrollTo(NotificationEntry entry) {
- NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
- }
-
- public void requestDisallowLongPressAndDismiss() {
- requestDisallowLongPress();
- requestDisallowDismiss();
- }
- };
- }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
@@ -1472,11 +1443,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getTopHeadsUpPinnedHeight() {
- NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry == null) {
+ if (mTopHeadsUpEntry == null) {
return 0;
}
- ExpandableNotificationRow row = topEntry.getRow();
+ ExpandableNotificationRow row = mTopHeadsUpEntry.getRow();
if (row.isChildInGroup()) {
final NotificationEntry groupSummary =
mGroupManager.getGroupSummary(row.getEntry().getSbn());
@@ -1497,7 +1467,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
int visibleNotifCount = getVisibleNotificationCount();
if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
if (isHeadsUpTransition()
- || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
+ || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
}
@@ -1655,9 +1625,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
NotificationEntry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().getRow() != row
+ && mTopHeadsUpEntry.getRow() != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().getSbn())
+ mTopHeadsUpEntry.getSbn())
!= entry) {
continue;
}
@@ -2285,7 +2255,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// In current design, it only use the top HUN to treat all of HUNs
// although there are more than one HUNs
int contentHeight = mContentHeight;
- if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
+ if (!isExpanded() && mInHeadsUpPinnedMode) {
contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
}
int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
@@ -2636,7 +2606,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
false /* shiftPulsingWithFirst */);
minTopPosition = firstVisibleSection.getBounds().top;
}
- boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
+ boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
&& (mAmbientState.isDozing()
|| (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
for (NotificationSection section : mSections) {
@@ -3511,7 +3481,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen
|| (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
- && mHeadsUpManager.hasPinnedHeadsUp());
+ && mInHeadsUpPinnedMode);
if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
@@ -5811,23 +5781,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCurrentUserId = userId;
}
- void onMenuShown(View row) {
- mSwipeHelper.onMenuShown(row);
- }
-
- void onMenuReset(View row) {
- View translatingParentView = mSwipeHelper.getTranslatingParentView();
- if (translatingParentView != null && row == translatingParentView) {
- mSwipeHelper.clearExposedMenuView();
- mSwipeHelper.clearTranslatingParentView();
- if (row instanceof ExpandableNotificationRow) {
- mHeadsUpManager.setMenuShown(
- ((ExpandableNotificationRow) row).getEntry(), false);
-
- }
- }
- }
-
void addSwipedOutView(View v) {
mSwipedOutViews.add(v);
}
@@ -5840,6 +5793,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAmbientState.onDragFinished(view);
}
+ void setTopHeadsUpEntry(NotificationEntry topEntry) {
+ mTopHeadsUpEntry = topEntry;
+ }
+
+ void setNumHeadsUp(long numHeadsUp) {
+ mNumHeadsUp = numHeadsUp;
+ mAmbientState.setHasAlertEntries(numHeadsUp > 0);
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 73b4cad4ced5..b9fc5781def4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -110,6 +111,7 @@ public class NotificationStackScrollLayoutController {
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
+ private NotificationSwipeHelper mSwipeHelper;
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
@@ -217,7 +219,16 @@ public class NotificationStackScrollLayoutController {
@Override
public void onMenuReset(View row) {
- mView.onMenuReset(row);
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ if (translatingParentView != null && row == translatingParentView) {
+ mSwipeHelper.clearExposedMenuView();
+ mSwipeHelper.clearTranslatingParentView();
+ if (row instanceof ExpandableNotificationRow) {
+ mHeadsUpManager.setMenuShown(
+ ((ExpandableNotificationRow) row).getEntry(), false);
+
+ }
+ }
}
@Override
@@ -228,7 +239,7 @@ public class NotificationStackScrollLayoutController {
.setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
.setType(MetricsEvent.TYPE_ACTION));
mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
- mView.onMenuShown(row);
+ mSwipeHelper.onMenuShown(row);
mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
@@ -438,7 +449,31 @@ public class NotificationStackScrollLayoutController {
}
};
- private NotificationSwipeHelper mSwipeHelper;
+ private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+ new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+ mView.setInHeadsUpPinnedMode(inPinnedMode);
+ }
+
+ @Override
+ public void onHeadsUpPinned(NotificationEntry entry) {
+
+ }
+
+ @Override
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
+
+ }
+
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ long numEntries = mHeadsUpManager.getAllEntries().count();
+ NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
+ mView.setNumHeadsUp(numEntries);
+ mView.setTopHeadsUpEntry(topEntry);
+ }
+ };
@Inject
public NotificationStackScrollLayoutController(
@@ -496,6 +531,8 @@ public class NotificationStackScrollLayoutController {
mSwipeHelper);
mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here?
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+ mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
@@ -910,7 +947,23 @@ public class NotificationStackScrollLayoutController {
}
public RemoteInputController.Delegate createDelegate() {
- return mView.createDelegate();
+ return new RemoteInputController.Delegate() {
+ public void setRemoteInputActive(NotificationEntry entry,
+ boolean remoteInputActive) {
+ mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
+ entry.notifyHeightChanged(true /* needsAnimation */);
+ updateFooter();
+ }
+
+ public void lockScrollTo(NotificationEntry entry) {
+ mView.lockScrollTo(entry.getRow());
+ }
+
+ public void requestDisallowLongPressAndDismiss() {
+ mView.requestDisallowLongPress();
+ mView.requestDisallowDismiss();
+ }
+ };
}
public void updateSectionBoundaries(String reason) {
@@ -966,10 +1019,6 @@ public class NotificationStackScrollLayoutController {
return mView.getFirstChildNotGone();
}
- public void setInHeadsUpPinnedMode(boolean inPinnedMode) {
- mView.setInHeadsUpPinnedMode(inPinnedMode);
- }
-
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
mView.generateHeadsUpAnimation(entry, isHeadsUp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 8750a02ed5b3..148029758a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3546,7 +3546,6 @@ public class NotificationPanelViewController extends PanelViewController {
private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
@Override
public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
- mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode);
if (inPinnedMode) {
mHeadsUpExistenceChangedRunnable.run();
updateNotificationTranslucency();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index c8e0f490a783..909acead9553 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -69,7 +69,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -125,7 +125,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock GlobalActionsPanelPlugin mWalletPlugin;
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
@Mock private Handler mHandler;
- @Mock private CurrentUserContextTracker mCurrentUserContextTracker;
+ @Mock private UserContextProvider mUserContextProvider;
private ControlsComponent mControlsComponent;
private TestableLooper mTestableLooper;
@@ -137,7 +137,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
- when(mCurrentUserContextTracker.getCurrentUserContext()).thenReturn(mContext);
+ when(mUserContextProvider.getUserContext()).thenReturn(mContext);
mControlsComponent = new ControlsComponent(
true,
() -> mControlsController,
@@ -176,7 +176,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mSysUiState,
mHandler,
mControlsComponent,
- mCurrentUserContextTracker
+ mUserContextProvider
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 4c9e141c45cc..3e37fde84544 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -33,7 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import org.junit.Before;
@@ -62,7 +62,7 @@ public class RecordingServiceTest extends SysuiTestCase {
@Mock
private Executor mExecutor;
@Mock
- private CurrentUserContextTracker mUserContextTracker;
+ private UserContextProvider mUserContextTracker;
private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
boolean requiresShadeOpen) {
@@ -92,7 +92,7 @@ public class RecordingServiceTest extends SysuiTestCase {
doNothing().when(mRecordingService).startForeground(anyInt(), any());
doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
- doReturn(mContext).when(mUserContextTracker).getCurrentUserContext();
+ doReturn(mContext).when(mUserContextTracker).getUserContext();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
deleted file mode 100644
index 628c06a56abd..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 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.systemui.settings
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class CurrentUserContextTrackerTest : SysuiTestCase() {
-
- private lateinit var tracker: CurrentUserContextTracker
- @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- allowTestableLooperAsMainThread()
-
- // wrap Context so that tests don't throw for missing package errors
- val wrapped = object : ContextWrapper(context) {
- override fun createContextAsUser(user: UserHandle, flags: Int): Context {
- val mockContext = mock(Context::class.java)
- `when`(mockContext.user).thenReturn(user)
- `when`(mockContext.userId).thenReturn(user.identifier)
- return mockContext
- }
- }
-
- tracker = CurrentUserContextTracker(wrapped, broadcastDispatcher)
- tracker.initialize()
- }
-
- @Test
- fun testContextExistsAfterInit_noCrash() {
- tracker.currentUserContext
- }
-
- @Test
- fun testUserContextIsCorrectAfterUserSwitch() {
- // We always start out with system ui test
- assertTrue("Starting userId should be 0", tracker.currentUserContext.userId == 0)
-
- // WHEN user changes
- tracker.handleUserSwitched(1)
-
- // THEN user context should have the correct userId
- assertTrue("User has changed to userId 1, the context should reflect that",
- tracker.currentUserContext.userId == 1)
- }
-
- @Suppress("UNUSED_PARAMETER")
- @Test(expected = IllegalStateException::class)
- fun testContextTrackerThrowsExceptionWhenNotInitialized() {
- // GIVEN an uninitialized CurrentUserContextTracker
- val userTracker = CurrentUserContextTracker(context, broadcastDispatcher)
-
- // WHEN client asks for a context
- val userContext = userTracker.currentUserContext
-
- // THEN an exception is thrown
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
new file mode 100644
index 000000000000..f76b50a173c3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 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.systemui.settings
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserTrackerImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var context: Context
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock(stubOnly = true)
+ private lateinit var dumpManager: DumpManager
+ @Mock(stubOnly = true)
+ private lateinit var handler: Handler
+
+ private val executor = Executor(Runnable::run)
+ private lateinit var tracker: UserTrackerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM)
+ `when`(context.user).thenReturn(UserHandle.SYSTEM)
+ `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
+ val user = invocation.getArgument<UserHandle>(0)
+ `when`(context.user).thenReturn(user)
+ `when`(context.userId).thenReturn(user.identifier)
+ context
+ }
+ `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL)
+ listOf(info)
+ }
+
+ tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+ }
+
+ @Test
+ fun testNotInitialized() {
+ assertThat(tracker.initialized).isFalse()
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserIdBeforeInitThrowsException() {
+ tracker.userId
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserHandleBeforeInitThrowsException() {
+ tracker.userHandle
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserContextBeforeInitThrowsException() {
+ tracker.userContext
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserContentResolverBeforeInitThrowsException() {
+ tracker.userContentResolver
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetUserProfilesBeforeInitThrowsException() {
+ tracker.userProfiles
+ }
+
+ @Test
+ fun testInitialize() {
+ tracker.initialize(0)
+
+ assertThat(tracker.initialized).isTrue()
+ }
+
+ @Test
+ fun testReceiverRegisteredOnInitialize() {
+ tracker.initialize(0)
+
+ val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
+
+ verify(context).registerReceiverForAllUsers(
+ eq(tracker), capture(captor), isNull(), eq(handler))
+ }
+
+ @Test
+ fun testInitialValuesSet() {
+ val testID = 4
+ tracker.initialize(testID)
+
+ verify(userManager).getProfiles(testID)
+
+ assertThat(tracker.userId).isEqualTo(testID)
+ assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
+ assertThat(tracker.userContext.userId).isEqualTo(testID)
+ assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
+ assertThat(tracker.userProfiles).hasSize(1)
+
+ val info = tracker.userProfiles[0]
+ assertThat(info.id).isEqualTo(testID)
+ }
+
+ @Test
+ fun testUserSwitch() {
+ tracker.initialize(0)
+ val newID = 5
+
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+ tracker.onReceive(context, intent)
+
+ verify(userManager).getProfiles(newID)
+
+ assertThat(tracker.userId).isEqualTo(newID)
+ assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
+ assertThat(tracker.userContext.userId).isEqualTo(newID)
+ assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
+ assertThat(tracker.userProfiles).hasSize(1)
+
+ val info = tracker.userProfiles[0]
+ assertThat(info.id).isEqualTo(newID)
+ }
+
+ @Test
+ fun testManagedProfileAvailable() {
+ tracker.initialize(0)
+ val profileID = tracker.userId + 10
+
+ `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile = UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
+
+ val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ tracker.onReceive(context, intent)
+
+ assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
+ }
+
+ @Test
+ fun testCallbackNotCalledOnAdd() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+
+ tracker.addCallback(callback, executor)
+
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ }
+
+ @Test
+ fun testCallbackCalledOnUserChanged() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+
+ val newID = 5
+
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+ tracker.onReceive(context, intent)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(1)
+ assertThat(callback.lastUser).isEqualTo(newID)
+ assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
+ }
+
+ @Test
+ fun testCallbackCalledOnProfileChanged() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ val profileID = tracker.userId + 10
+
+ `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile = UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
+
+ val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intent)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+ }
+
+ @Test
+ fun testCallbackRemoved() {
+ tracker.initialize(0)
+ val newID = 5
+ val profileID = newID + 10
+
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ tracker.removeCallback(callback)
+
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
+ tracker.onReceive(context, intent)
+
+ val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intentProfiles)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+ }
+
+ private class TestCallback : UserTracker.Callback {
+ var calledOnUserChanged = 0
+ var calledOnProfilesChanged = 0
+ var lastUser: Int? = null
+ var lastUserContext: Context? = null
+ var lastUserProfiles = emptyList<UserInfo>()
+
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ calledOnUserChanged++
+ lastUser = newUser
+ lastUserContext = userContext
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ calledOnProfilesChanged++
+ lastUserProfiles = profiles
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index c2c40cac3d0f..e1668cab3333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -69,7 +69,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -128,7 +128,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private ShortcutManager mShortcutManager;
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock private CurrentUserContextTracker mContextTracker;
+ @Mock private UserContextProvider mContextTracker;
@Mock private BubbleController mBubbleController;
@Mock(answer = Answers.RETURNS_SELF)
private PriorityOnboardingDialogController.Builder mBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index fa9ea6b1ea10..beb0cc2809ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -122,7 +122,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private EmptyShadeView mEmptyShadeView;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
- @Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private MetricsLogger mMetricsLogger;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@@ -203,8 +202,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mNotificationRoundnessManager,
mock(DynamicPrivacyController.class),
mStatusBarStateController,
- mHeadsUpManager,
- new FalsingManagerFake(),
mock(NotificationGutsManager.class),
mNotificationSectionsManager,
mock(ForegroundServiceSectionController.class),
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 71f1833854ce..534533f2fc97 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1133,9 +1133,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return;
}
- String dumpFilter = args.length == 0 ? null : args[0];
-
- ipw.println("Location Manager State:");
+ ipw.print("Location Manager State:");
ipw.increaseIndent();
ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
@@ -1166,34 +1164,28 @@ public class LocationManagerService extends ILocationManager.Stub {
ipw.println(
"Location Controller Extra Package: " + mExtraLocationControllerPackage
+ (mExtraLocationControllerPackageEnabled ? " [enabled]"
- : "[disabled]"));
+ : " [disabled]"));
}
}
ipw.println("Location Providers:");
ipw.increaseIndent();
for (LocationProviderManager manager : mProviderManagers) {
- if (dumpFilter == null || manager.getName().equals(dumpFilter)) {
- manager.dump(fd, ipw, args);
- }
+ manager.dump(fd, ipw, args);
}
ipw.decreaseIndent();
- if (dumpFilter == null || GPS_PROVIDER.equals(dumpFilter)) {
- if (mGnssManagerService != null) {
- ipw.println("GNSS Manager:");
- ipw.increaseIndent();
- mGnssManagerService.dump(fd, ipw, args);
- ipw.decreaseIndent();
- }
- }
-
- if (dumpFilter == null || "geofence".equals(dumpFilter)) {
- ipw.println("Geofence Manager:");
+ if (mGnssManagerService != null) {
+ ipw.println("GNSS Manager:");
ipw.increaseIndent();
- mGeofenceManager.dump(fd, ipw, args);
+ mGnssManagerService.dump(fd, ipw, args);
ipw.decreaseIndent();
}
+
+ ipw.println("Geofence Manager:");
+ ipw.increaseIndent();
+ mGeofenceManager.dump(fd, ipw, args);
+ ipw.decreaseIndent();
}
private class LocalService extends LocationManagerInternal {
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 06105bfcc0f3..66245a279666 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -84,7 +84,7 @@ import com.android.server.LocalServices;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions.PermissionLevel;
import com.android.server.location.listeners.ListenerMultiplexer;
-import com.android.server.location.listeners.RemovableListenerRegistration;
+import com.android.server.location.listeners.RemoteListenerRegistration;
import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppForegroundHelper.AppForegroundListener;
import com.android.server.location.util.AppOpsHelper;
@@ -154,15 +154,8 @@ class LocationProviderManager extends
@Override
public void deliverOnLocationChanged(Location location,
- @Nullable Runnable onCompleteCallback)
- throws RemoteException {
- mListener.onLocationChanged(location,
- onCompleteCallback == null ? null : new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) {
- onCompleteCallback.run();
- }
- });
+ @Nullable Runnable onCompleteCallback) throws RemoteException {
+ mListener.onLocationChanged(location, SingleUseCallback.wrap(onCompleteCallback));
}
@Override
@@ -221,7 +214,7 @@ class LocationProviderManager extends
}
protected abstract class Registration extends
- RemovableListenerRegistration<LocationRequest, LocationTransport> {
+ RemoteListenerRegistration<LocationRequest, LocationTransport> {
@PermissionLevel protected final int mPermissionLevel;
private final WorkSource mWorkSource;
@@ -306,11 +299,12 @@ class LocationProviderManager extends
}
@Override
- protected final void onInactive() {
+ protected final ListenerOperation<LocationTransport> onInactive() {
onHighPowerUsageChanged();
if (!getRequest().getHideFromAppOps()) {
mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
}
+ return null;
}
@Override
@@ -826,6 +820,12 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
protected void onProviderListenerRegister() {
+ try {
+ ((IBinder) getKey()).linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ remove();
+ }
+
mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
SystemClock.elapsedRealtime());
@@ -837,12 +837,6 @@ class LocationProviderManager extends
0, this, FgThread.getHandler(), getWorkSource());
}
- try {
- ((IBinder) getKey()).linkToDeath(this, 0);
- } catch (RemoteException e) {
- remove();
- }
-
// start listening for provider enabled/disabled events
addEnabledListener(this);
@@ -1066,8 +1060,13 @@ class LocationProviderManager extends
mUserInfoHelper.addListener(mUserChangedListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
- // initialize enabled state
- onUserStarted(UserHandle.USER_ALL);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // initialize enabled state
+ onUserStarted(UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -1077,10 +1076,15 @@ class LocationProviderManager extends
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
// notify and remove all listeners
- onUserStopped(UserHandle.USER_ALL);
- removeRegistrationIf(key -> true);
- mEnabledListeners.clear();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ onUserStopped(UserHandle.USER_ALL);
+ removeRegistrationIf(key -> true);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ mEnabledListeners.clear();
mStarted = false;
}
}
@@ -1141,14 +1145,26 @@ class LocationProviderManager extends
public void setRealProvider(AbstractLocationProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mStarted);
- mProvider.setRealProvider(provider);
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mProvider.setRealProvider(provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
public void setMockProvider(@Nullable MockProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mStarted);
- mProvider.setMockProvider(provider);
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mProvider.setMockProvider(provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
// when removing a mock provider, also clear any mock last locations and reset the
// location fudger. the mock provider could have been used to infer the current
@@ -1170,7 +1186,12 @@ class LocationProviderManager extends
throw new IllegalArgumentException(mName + " provider is not a test provider");
}
- mProvider.setMockProviderAllowed(enabled);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mProvider.setMockProviderAllowed(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -1180,15 +1201,20 @@ class LocationProviderManager extends
throw new IllegalArgumentException(mName + " provider is not a test provider");
}
- String locationProvider = location.getProvider();
- if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
- // The location has an explicit provider that is different from the mock
- // provider name. The caller may be trying to fool us via b/33091107.
- EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
- mName + "!=" + locationProvider);
- }
+ long identity = Binder.clearCallingIdentity();
+ try {
+ String locationProvider = location.getProvider();
+ if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
+ // The location has an explicit provider that is different from the mock
+ // provider name. The caller may be trying to fool us via b/33091107.
+ EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+ mName + "!=" + locationProvider);
+ }
- mProvider.setMockProviderLocation(location);
+ mProvider.setMockProviderLocation(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -1279,7 +1305,7 @@ class LocationProviderManager extends
}
}
- public void getCurrentLocation(LocationRequest request, CallerIdentity identity,
+ public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity,
int permissionLevel, ICancellationSignal cancellationTransport,
ILocationCallback callback) {
Preconditions.checkArgument(mName.equals(request.getProvider()));
@@ -1291,12 +1317,12 @@ class LocationProviderManager extends
GetCurrentLocationListenerRegistration registration =
new GetCurrentLocationListenerRegistration(
request,
- identity,
+ callerIdentity,
new GetCurrentLocationTransport(callback),
permissionLevel);
synchronized (mLock) {
- Location lastLocation = getLastLocation(request, identity, permissionLevel);
+ Location lastLocation = getLastLocation(request, callerIdentity, permissionLevel);
if (lastLocation != null) {
long locationAgeMs = NANOSECONDS.toMillis(
SystemClock.elapsedRealtimeNanos()
@@ -1314,7 +1340,13 @@ class LocationProviderManager extends
}
// if last location isn't good enough then we add a location request
- addRegistration(callback.asBinder(), registration);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ addRegistration(callback.asBinder(), registration);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
cancellationTransport);
if (cancellationSignal != null) {
@@ -1329,48 +1361,73 @@ class LocationProviderManager extends
}
public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
- mProvider.sendExtraCommand(uid, pid, command, extras);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mProvider.sendExtraCommand(uid, pid, command, extras);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+ public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
@PermissionLevel int permissionLevel, ILocationListener listener) {
Preconditions.checkArgument(mName.equals(request.getProvider()));
synchronized (mLock) {
- addRegistration(
- listener.asBinder(),
- new LocationListenerRegistration(
- request,
- identity,
- new LocationListenerTransport(listener),
- permissionLevel));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ addRegistration(
+ listener.asBinder(),
+ new LocationListenerRegistration(
+ request,
+ callerIdentity,
+ new LocationListenerTransport(listener),
+ permissionLevel));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
- public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+ public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
@PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
Preconditions.checkArgument(mName.equals(request.getProvider()));
synchronized (mLock) {
- addRegistration(
- pendingIntent,
- new LocationPendingIntentRegistration(
- request,
- identity,
- new LocationPendingIntentTransport(mContext, pendingIntent),
- permissionLevel));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ addRegistration(
+ pendingIntent,
+ new LocationPendingIntentRegistration(
+ request,
+ callerIdentity,
+ new LocationPendingIntentTransport(mContext, pendingIntent),
+ permissionLevel));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
public void unregisterLocationRequest(ILocationListener listener) {
synchronized (mLock) {
- removeRegistration(listener.asBinder());
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeRegistration(listener.asBinder());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
public void unregisterLocationRequest(PendingIntent pendingIntent) {
synchronized (mLock) {
- removeRegistration(pendingIntent);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeRegistration(pendingIntent);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -1958,4 +2015,50 @@ class LocationProviderManager extends
}
}
}
+
+ private static class SingleUseCallback extends IRemoteCallback.Stub {
+
+ @Nullable
+ public static IRemoteCallback wrap(@Nullable Runnable callback) {
+ return callback == null ? null : new SingleUseCallback(callback);
+ }
+
+ @GuardedBy("this")
+ @Nullable private Runnable mCallback;
+
+ private SingleUseCallback(Runnable callback) {
+ mCallback = Objects.requireNonNull(callback);
+ }
+
+ @Override
+ public void sendResult(Bundle data) {
+ Runnable callback;
+ synchronized (this) {
+ callback = mCallback;
+ mCallback = null;
+ }
+
+ // prevent this callback from being run more than once - otherwise this could provide an
+ // attack vector for a malicious app to break assumptions on how many times a callback
+ // may be invoked, and thus crash system server.
+ if (callback == null) {
+ return;
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ callback.run();
+ } catch (RuntimeException e) {
+ // since this is within a oneway binder transaction there is nowhere
+ // for exceptions to go - move onto another thread to crash system
+ // server so we find out about it
+ FgThread.getExecutor().execute(() -> {
+ throw new AssertionError(e);
+ });
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 2d9734ef0553..2d7f02873b8f 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -32,6 +32,7 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.WorkSource;
@@ -291,17 +292,28 @@ public class GeofenceManager extends
@Nullable String attributionTag) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
- AppOpsManager.toReceiverId(pendingIntent));
- addRegistration(new GeofenceKey(pendingIntent, geofence),
- new GeofenceRegistration(geofence, identity, pendingIntent));
+ CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
+ attributionTag, AppOpsManager.toReceiverId(pendingIntent));
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ addRegistration(new GeofenceKey(pendingIntent, geofence),
+ new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Removes the geofence associated with the PendingIntent.
*/
public void removeGeofence(PendingIntent pendingIntent) {
- removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 1b599b026c38..a9fdacca9a06 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
@@ -218,16 +219,27 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
/**
* Adds a listener with the given identity and request.
*/
- protected void addListener(TRequest request, CallerIdentity identity, TListener listener) {
- addRegistration(listener.asBinder(),
- new GnssListenerRegistration(request, identity, listener));
+ protected void addListener(TRequest request, CallerIdentity callerIdentity,
+ TListener listener) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ addRegistration(listener.asBinder(),
+ new GnssListenerRegistration(request, callerIdentity, listener));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Removes the given listener.
*/
public void removeListener(TListener listener) {
- removeRegistration(listener.asBinder());
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeRegistration(listener.asBinder());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 850cf7f4b7ce..5c30fe840073 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2022,6 +2022,21 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ boolean dumpAll = false;
+
+ int opti = 0;
+ while (opti < args.length) {
+ String opt = args[opti];
+ if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+ break;
+ }
+ opti++;
+ if ("-a".equals(opt)) {
+ dumpAll = true;
+ break;
+ }
+ }
+
StringBuilder s = new StringBuilder();
s.append("mStarted=").append(mStarted).append(" (changed ");
TimeUtils.formatDuration(SystemClock.elapsedRealtime()
@@ -2053,9 +2068,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
s.append("]\n");
}
s.append(mGnssMetrics.dumpGnssMetricsAsText());
- s.append("native internal state: \n");
- s.append(" ").append(native_get_internal_state());
- s.append("\n");
+ if (dumpAll) {
+ s.append("native internal state: \n");
+ s.append(" ").append(native_get_internal_state());
+ s.append("\n");
+ }
pw.append(s);
}
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index bd8bce8f6d52..58aabdad056f 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -32,10 +32,10 @@ import android.util.Log;
* @param <TListener> listener type
*/
public abstract class BinderListenerRegistration<TRequest, TListener> extends
- RemovableListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
+ RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
/**
- * Interface to allowed binder retrieval when keys are not themselves IBinder.
+ * Interface to allow binder retrieval when keys are not themselves IBinders.
*/
public interface BinderKey {
/**
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f94de9be0cfe..8a6b8aa1e463 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -18,12 +18,9 @@ package com.android.server.location.listeners;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Binder;
import android.os.Build;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
-import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
@@ -31,8 +28,10 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -42,7 +41,7 @@ import java.util.function.Predicate;
* divided into two categories, active registrations and inactive registrations, as defined by
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
* {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
- * whose active state may have changed.
+ * whose active state may have changed. Listeners will only be invoked for active registrations.
*
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
@@ -64,14 +63,6 @@ import java.util.function.Predicate;
* {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This
* ensures re-entrant removal does not accidentally remove the incorrect registration.
*
- * All callbacks will be invoked with a cleared binder identity.
- *
- * Listeners owned by other processes will be run on a direct executor (and thus while holding a
- * lock). Listeners owned by the same process this multiplexer is in will be run asynchronously (and
- * thus without holding a lock). The underlying assumption is that listeners owned by other
- * processes will simply be forwarding the call to those other processes and perhaps performing
- * simple bookkeeping, with no potential for deadlock.
- *
* @param <TKey> key type
* @param <TRequest> request type
* @param <TListener> listener type
@@ -149,46 +140,51 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
/**
- * Invoked before the first registration occurs. This is a convenient entry point for
- * registering listeners, etc, which only need to be present while there are any registrations.
+ * Invoked when the multiplexer goes from having no registrations to having some registrations.
+ * This is a convenient entry point for registering listeners, etc, which only need to be
+ * present while there are any registrations. Invoked while holding the multiplexer's internal
+ * lock.
*/
protected void onRegister() {}
/**
- * Invoked after the last unregistration occurs. This is a convenient entry point for
- * unregistering listeners, etc, which only need to be present while there are any
- * registrations.
+ * Invoked when the multiplexer goes from having some registrations to having no registrations.
+ * This is a convenient entry point for unregistering listeners, etc, which only need to be
+ * present while there are any registrations. Invoked while holding the multiplexer's internal
+ * lock.
*/
protected void onUnregister() {}
/**
- * Invoked when a registration is added.
+ * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock.
*/
protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {}
/**
- * Invoked when a registration is removed.
+ * Invoked when a registration is removed. Invoked while holding the multiplexer's internal
+ * lock.
*/
protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {}
/**
- * Invoked when the manager goes from having no active registrations to having some active
+ * Invoked when the multiplexer goes from having no active registrations to having some active
* registrations. This is a convenient entry point for registering listeners, etc, which only
- * need to be present while there are active registrations.
+ * need to be present while there are active registrations. Invoked while holding the
+ * multiplexer's internal lock.
*/
protected void onActive() {}
/**
- * Invoked when the manager goes from having some active registrations to having no active
+ * Invoked when the multiplexer goes from having some active registrations to having no active
* registrations. This is a convenient entry point for unregistering listeners, etc, which only
- * need to be present while there are active registrations.
+ * need to be present while there are active registrations. Invoked while holding the
+ * multiplexer's internal lock.
*/
protected void onInactive() {}
/**
- * Adds a new registration with the given key. Registration may fail if
- * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration
- * will not be added. This method cannot be called to add a registration re-entrantly.
+ * Adds a new registration with the given key. This method cannot be called to add a
+ * registration re-entrantly.
*/
protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
Objects.requireNonNull(key);
@@ -204,7 +200,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// involve removing a prior registration. note that try-with-resources ordering is
// meaningful here as well. we want to close the reentrancy guard first, as this may
// generate additional service updates, then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
@@ -224,16 +219,13 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
registration.onRegister(key);
onRegistrationAdded(key, registration);
onRegistrationActiveChanged(registration);
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
}
/**
- * Removes the registration with the given key. If unregistration occurs,
- * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
- * cannot be called to remove a registration re-entrantly.
+ * Removes the registration with the given key. This method cannot be called to remove a
+ * registration re-entrantly.
*/
protected final void removeRegistration(@NonNull Object key) {
synchronized (mRegistrations) {
@@ -250,9 +242,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
/**
- * Removes all registrations with keys that satisfy the given predicate. If unregistration
- * occurs, {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This
- * method cannot be called to remove a registration re-entrantly.
+ * Removes all registrations with keys that satisfy the given predicate. This method cannot be
+ * called to remove a registration re-entrantly.
*/
protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) {
synchronized (mRegistrations) {
@@ -281,11 +272,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
/**
* Removes the given registration with the given key. If the given key has a different
- * registration at the time this method is called, nothing happens. If unregistration occurs,
- * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
- * allows for re-entrancy, and may be called to remove a registration re-entrantly. In this case
- * the registration will immediately be marked inactive and unregistered, and will be removed
- * completely at some later time.
+ * registration at the time this method is called, nothing happens. This method allows for
+ * re-entrancy, and may be called to remove a registration re-entrantly.
*/
protected final void removeRegistration(@NonNull Object key,
@NonNull ListenerRegistration<?, ?> registration) {
@@ -324,7 +312,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// in multiple service updates. note that try-with-resources ordering is meaningful here as
// well. we want to close the reentrancy guard first, as this may generate additional
// service updates, then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
@@ -337,8 +324,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
onUnregister();
}
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
@@ -362,38 +347,46 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
}
- long identity = Binder.clearCallingIdentity();
- try {
- if (actives.isEmpty()) {
+ if (actives.isEmpty()) {
+ mCurrentRequest = null;
+ if (mServiceRegistered) {
+ mServiceRegistered = false;
mCurrentRequest = null;
- if (mServiceRegistered) {
- mServiceRegistered = false;
- mCurrentRequest = null;
- unregisterWithService();
- }
- return;
+ unregisterWithService();
}
+ return;
+ }
- TMergedRequest merged = mergeRequests(actives);
- if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
- if (mServiceRegistered) {
- mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
- } else {
- mServiceRegistered = registerWithService(merged);
- }
- if (mServiceRegistered) {
- mCurrentRequest = merged;
- } else {
- mCurrentRequest = null;
- }
+ TMergedRequest merged = mergeRequests(actives);
+ if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
+ if (mServiceRegistered) {
+ mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
+ } else {
+ mServiceRegistered = registerWithService(merged);
+ }
+ if (mServiceRegistered) {
+ mCurrentRequest = merged;
+ } else {
+ mCurrentRequest = null;
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
}
/**
+ * Clears currently stored service state, and invokes {@link #updateService()} to force a new
+ * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if
+ * the backing service has crashed or otherwise lost state, and needs to be re-initialized.
+ */
+ protected final void resetService() {
+ synchronized (mRegistrations) {
+ mServiceRegistered = false;
+ mCurrentRequest = null;
+ updateService();
+ }
+ }
+
+ /**
* Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()}
* is called. This is useful to prevent extra work when combining multiple calls (for example,
* buffering {@code updateService()} until after multiple adds/removes/updates occur.
@@ -404,9 +397,9 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
/**
* Evaluates the predicate on all registrations. The predicate should return true if the active
- * state of the registration may have changed as a result. Any {@link #updateService()}
- * invocations made while this method is executing will be deferred until after the method is
- * complete so as to avoid redundant work.
+ * state of the registration may have changed as a result. If the active state of any
+ * registration has changed, {@link #updateService()} will automatically be invoked to handle
+ * the resulting changes.
*/
protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) {
synchronized (mRegistrations) {
@@ -415,7 +408,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
// callbacks. note that try-with-resources ordering is meaningful here as well. we want
// to close the reentrancy guard first, as this may generate additional service updates,
// then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
@@ -426,8 +418,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
onRegistrationActiveChanged(registration);
}
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
}
@@ -450,7 +440,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
execute(registration, operation);
}
} else {
- registration.onInactive();
+ ListenerOperation<TListener> operation = registration.onInactive();
+ if (operation != null) {
+ execute(registration, operation);
+ }
if (--mActiveRegistrationsCount == 0) {
onInactive();
}
@@ -468,7 +461,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
protected final void deliverToListeners(
@NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
synchronized (mRegistrations) {
- long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
@@ -480,8 +472,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
}
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
}
@@ -495,7 +485,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
*/
protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
synchronized (mRegistrations) {
- long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
@@ -504,8 +493,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
execute(registration, operation);
}
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
}
@@ -522,27 +509,26 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
/**
* Dumps debug information.
*/
- public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mRegistrations) {
- ipw.print("service: ");
- dumpServiceState(ipw);
- ipw.println();
+ pw.print("service: ");
+ dumpServiceState(pw);
+ pw.println();
if (!mRegistrations.isEmpty()) {
- ipw.println("listeners:");
+ pw.println("listeners:");
- ipw.increaseIndent();
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
- ipw.print(registration);
+ pw.print(" ");
+ pw.print(registration);
if (!registration.isActive()) {
- ipw.println(" (inactive)");
+ pw.println(" (inactive)");
} else {
- ipw.println();
+ pw.println();
}
}
- ipw.decreaseIndent();
}
}
}
@@ -577,7 +563,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
@GuardedBy("mRegistrations")
private int mGuardCount;
@GuardedBy("mRegistrations")
- private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
+ private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
@@ -602,7 +588,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
if (mScheduledRemovals == null) {
mScheduledRemovals = new ArraySet<>(mRegistrations.size());
}
- mScheduledRemovals.add(new Pair<>(key, registration));
+ mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration));
}
ReentrancyGuard acquire() {
@@ -612,7 +598,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
@Override
public void close() {
- ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
+ ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -620,14 +606,15 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
mScheduledRemovals = null;
}
- if (scheduledRemovals != null) {
- try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
- final int size = scheduledRemovals.size();
- for (int i = 0; i < size; i++) {
- Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
- i);
- removeRegistration(pair.first, pair.second);
- }
+ if (scheduledRemovals == null) {
+ return;
+ }
+
+ try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
+ final int size = scheduledRemovals.size();
+ for (int i = 0; i < size; i++) {
+ Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i);
+ removeRegistration(entry.getKey(), entry.getValue());
}
}
}
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index ac56c51568be..deb9660a1c82 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -17,55 +17,34 @@
package com.android.server.location.listeners;
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.listeners.ListenerExecutor;
-import com.android.server.FgThread;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* A listener registration object which holds data associated with the listener, such as an optional
- * request, and the identity of the listener owner.
+ * request, and an executor responsible for listener invocations.
*
* @param <TRequest> request type
* @param <TListener> listener type
*/
public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
- @VisibleForTesting
- public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
-
private final Executor mExecutor;
private final @Nullable TRequest mRequest;
- private final CallerIdentity mIdentity;
private boolean mActive;
private volatile @Nullable TListener mListener;
- protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
+ protected ListenerRegistration(Executor executor, @Nullable TRequest request,
TListener listener) {
- // if a client is in the same process as us, binder calls will execute synchronously and
- // we shouldn't run callbacks directly since they might be run under lock and deadlock
- if (identity.getPid() == Process.myPid()) {
- // there's a slight loophole here for pending intents - pending intent callbacks can
- // always be run on the direct executor since they're always asynchronous, but honestly
- // you shouldn't be using pending intent callbacks within the same process anyways
- mExecutor = IN_PROCESS_EXECUTOR;
- } else {
- mExecutor = DIRECT_EXECUTOR;
- }
-
+ mExecutor = Objects.requireNonNull(executor);
mRequest = request;
- mIdentity = Objects.requireNonNull(identity);
mActive = false;
mListener = Objects.requireNonNull(listener);
}
@@ -82,34 +61,34 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
}
/**
- * Returns the listener identity.
- */
- public final CallerIdentity getIdentity() {
- return mIdentity;
- }
-
- /**
- * May be overridden by subclasses. Invoked when registration occurs.
+ * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the
+ * owning multiplexer's internal lock.
*/
protected void onRegister(Object key) {}
/**
- * May be overridden by subclasses. Invoked when unregistration occurs.
+ * May be overridden by subclasses. Invoked when unregistration occurs. Invoked while holding
+ * the owning multiplexer's internal lock.
*/
protected void onUnregister() {}
/**
* May be overridden by subclasses. Invoked when this registration becomes active. If this
- * returns a non-null operation, that operation will be invoked for the listener.
+ * returns a non-null operation, that operation will be invoked for the listener. Invoked
+ * while holding the owning multiplexer's internal lock.
*/
protected @Nullable ListenerOperation<TListener> onActive() {
return null;
}
/**
- * May be overridden by subclasses. Invoked when registration becomes inactive.
+ * May be overridden by subclasses. Invoked when registration becomes inactive. If this returns
+ * a non-null operation, that operation will be invoked for the listener. Invoked while holding
+ * the owning multiplexer's internal lock.
*/
- protected void onInactive() {}
+ protected @Nullable ListenerOperation<TListener> onInactive() {
+ return null;
+ }
public final boolean isActive() {
return mActive;
@@ -136,8 +115,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
/**
* May be overridden by subclasses, however should rarely be needed. Invoked when the listener
* associated with this registration is unregistered, which may occur before the registration
- * itself is unregistered. This immediately prevents the listener from being further invoked
- * even if the various bookkeeping associated with unregistration has not occurred yet.
+ * itself is unregistered. This immediately prevents the listener from being further invoked.
*/
protected void onListenerUnregister() {};
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index b5d2ef6a72ec..7b6154eb0d00 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -30,7 +30,7 @@ import android.util.Log;
* @param <TListener> listener type
*/
public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
- RemovableListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
+ RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
/**
* Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
new file mode 100644
index 000000000000..e4b0b190d34c
--- /dev/null
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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.server.location.listeners;
+
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.annotation.Nullable;
+import android.location.util.identity.CallerIdentity;
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener registration representing a remote (possibly from a different process) listener.
+ * Listeners from a different process will be run on a direct executor, since the x-process listener
+ * invocation should already be asynchronous. Listeners from the same process will be run on a
+ * normal executor, since in-process listener invocation may be synchronous.
+ *
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ */
+public abstract class RemoteListenerRegistration<TRequest, TListener> extends
+ RemovableListenerRegistration<TRequest, TListener> {
+
+ @VisibleForTesting
+ public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
+
+ private static Executor chooseExecutor(CallerIdentity identity) {
+ // if a client is in the same process as us, binder calls will execute synchronously and
+ // we shouldn't run callbacks directly since they might be run under lock and deadlock
+ if (identity.getPid() == Process.myPid()) {
+ // there's a slight loophole here for pending intents - pending intent callbacks can
+ // always be run on the direct executor since they're always asynchronous, but honestly
+ // you shouldn't be using pending intent callbacks within the same process anyways
+ return IN_PROCESS_EXECUTOR;
+ } else {
+ return DIRECT_EXECUTOR;
+ }
+ }
+
+ private final CallerIdentity mIdentity;
+
+ protected RemoteListenerRegistration(String tag, @Nullable TRequest request,
+ CallerIdentity identity, TListener listener) {
+ super(tag, chooseExecutor(identity), request, listener);
+ mIdentity = Objects.requireNonNull(identity);
+ }
+
+ /**
+ * Returns the listener identity.
+ */
+ public final CallerIdentity getIdentity() {
+ return mIdentity;
+ }
+}
+
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index 0698cca903f0..2383bece4e0a 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -17,10 +17,10 @@
package com.android.server.location.listeners;
import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
import android.util.Log;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* A listener registration that stores its own key, and thus can remove itself. By default it will
@@ -36,9 +36,9 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
private volatile @Nullable Object mKey;
- protected RemovableListenerRegistration(String tag, @Nullable TRequest request,
- CallerIdentity callerIdentity, TListener listener) {
- super(request, callerIdentity, listener);
+ protected RemovableListenerRegistration(String tag, Executor executor,
+ @Nullable TRequest request, TListener listener) {
+ super(executor, request, listener);
mTag = Objects.requireNonNull(tag);
}
@@ -70,7 +70,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
@Override
public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
- Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e);
+ Log.w(mTag, "registration " + this + " removed due to unexpected exception", e);
remove();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bb7b63bbc933..c488a1716219 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11671,7 +11671,7 @@ public class PackageManagerService extends IPackageManager.Stub
configurePackageComponents(parsedPackage);
}
- final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
+ final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);
final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -17704,7 +17704,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
&& pkgSetting.getPkgState().isUpdatedSystemApp();
- final String abiOverride = deriveAbiOverride(args.abiOverride, pkgSetting);
+ final String abiOverride = deriveAbiOverride(args.abiOverride);
AndroidPackage oldPackage = mPackages.get(pkgName);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 491b4fc515ce..5553cd0e2fb8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -421,18 +421,13 @@ public class PackageManagerServiceUtils {
/**
* Derive the value of the {@code cpuAbiOverride} based on the provided
- * value and an optional stored value from the package settings.
+ * value.
*/
- public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
- String cpuAbiOverride = null;
+ public static String deriveAbiOverride(String abiOverride) {
if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
- cpuAbiOverride = null;
- } else if (abiOverride != null) {
- cpuAbiOverride = abiOverride;
- } else if (settings != null) {
- cpuAbiOverride = settings.cpuAbiOverrideString;
+ return null;
}
- return cpuAbiOverride;
+ return abiOverride;
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 137c587b1d79..d01a30fbd818 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -148,6 +149,7 @@ import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
@@ -1378,12 +1380,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private long getScreenshotChordLongPressDelay() {
+ long delayMs = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
+ ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
if (mKeyguardDelegate.isShowing()) {
// Double the time it takes to take a screenshot from the keyguard
- return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
- ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
+ return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
}
- return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout();
+ return delayMs;
}
private long getRingerToggleChordDelay() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index fdcadf3e3088..d6894cf2a4e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -33,6 +33,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationUtils.createLocation;
+import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR;
import static com.google.common.truth.Truth.assertThat;
@@ -85,7 +86,6 @@ import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.location.listeners.ListenerRegistration;
import com.android.server.location.util.FakeUserInfoHelper;
import com.android.server.location.util.TestInjector;
@@ -484,7 +484,7 @@ public class LocationProviderManagerTest {
PERMISSION_FINE, listener);
CountDownLatch blocker = new CountDownLatch(1);
- ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+ IN_PROCESS_EXECUTOR.execute(() -> {
try {
blocker.await();
} catch (InterruptedException e) {
@@ -622,7 +622,7 @@ public class LocationProviderManagerTest {
PERMISSION_FINE, listener);
CountDownLatch blocker = new CountDownLatch(1);
- ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+ IN_PROCESS_EXECUTOR.execute(() -> {
try {
blocker.await();
} catch (InterruptedException e) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
index 1ef12555a83a..69a9f4415fe7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
@@ -16,6 +16,8 @@
package com.android.server.location.listeners;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,8 +29,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -324,10 +324,8 @@ public class ListenerMultiplexerTest {
boolean mActive = true;
protected TestListenerRegistration(Integer integer,
- Consumer<TestListenerRegistration> consumer,
- boolean outOfProcess) {
- super(integer, CallerIdentity.forTest(Process.myUid(),
- Process.myPid() + (outOfProcess ? 1 : 0), "test", "test"), consumer);
+ Consumer<TestListenerRegistration> consumer) {
+ super(DIRECT_EXECUTOR, integer, consumer);
}
}
@@ -345,7 +343,7 @@ public class ListenerMultiplexerTest {
}
public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
- addRegistration(consumer, new TestListenerRegistration(request, consumer, true));
+ addRegistration(consumer, new TestListenerRegistration(request, consumer));
}
public void removeListener(Consumer<TestListenerRegistration> consumer) {